From fac7ee62b4061585c7be80f96b7d051cfb4003fb Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 2 Aug 2024 17:52:18 -0700 Subject: [PATCH 001/139] wip: fix manganis import path --- Cargo.lock | 55 +++++++++++++++--------------- Cargo.toml | 6 ++-- packages/desktop/src/protocol.rs | 58 ++++++++++++++++---------------- 3 files changed, 61 insertions(+), 58 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7d2380e0f9..830419622f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -402,7 +402,7 @@ dependencies = [ "pin-project-lite", "tokio", "zstd 0.13.2", - "zstd-safe 7.2.0", + "zstd-safe 7.2.1", ] [[package]] @@ -1526,6 +1526,15 @@ dependencies = [ "target-lexicon", ] +[[package]] +name = "cfg-expr" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "345c78335be0624ed29012dc10c49102196c6882c12dde65d9f35b02da2aada8" +dependencies = [ + "smallvec", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -5674,12 +5683,12 @@ dependencies = [ [[package]] name = "krates" -version = "0.17.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d0956eb8f64d0353d56c69544657bbf3bc143c3c0f0883cc4fa9ec1b252a404" +checksum = "866bd5c1d0fc9c130a2469a1b3086803ea67b93b35683ae8ab2ec8a970120a65" dependencies = [ "camino", - "cfg-expr", + "cfg-expr 0.16.0", "petgraph", "semver 1.0.23", "serde", @@ -6075,8 +6084,6 @@ dependencies = [ [[package]] name = "manganis" version = "0.3.0-alpha.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578cb1a3a1a7dfad59feaa34eb928855e7f7d09a031db913b84ac2cc10fdfb84" dependencies = [ "manganis-macro", ] @@ -6084,8 +6091,6 @@ dependencies = [ [[package]] name = "manganis-cli-support" version = "0.3.0-alpha.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "378a47dc7523152750e923d3c0ea94a0d491ea2cd3616abce0d20ae0f1df36c0" dependencies = [ "anyhow", "image 0.25.2", @@ -6111,8 +6116,6 @@ dependencies = [ [[package]] name = "manganis-common" version = "0.3.0-alpha.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86a1bfc5ff78ac6ade38584b033952f7cef921b90db6d5ac2016bc30caf47f8b" dependencies = [ "anyhow", "base64 0.21.7", @@ -6129,8 +6132,6 @@ dependencies = [ [[package]] name = "manganis-macro" version = "0.3.0-alpha.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046c953865755a7b4e41f3680a2659833b41fdb171a0bffbaeba8b8237319924" dependencies = [ "manganis-common", "proc-macro2", @@ -6326,9 +6327,9 @@ dependencies = [ [[package]] name = "mozjpeg-sys" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf2a48f2f79f47fcce75d125d112e113a93f4a6f3cd8cc5b4cd6e16589d050f" +checksum = "27e31c0171e0b1158c0dfb7386dbdf999f4a9afaa83fd68de39c7929f4d5c16f" dependencies = [ "cc", "dunce", @@ -7952,9 +7953,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.5" +version = "1.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", @@ -10302,9 +10303,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_typescript" -version = "0.194.4" +version = "0.194.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99619353488c1dae960159faef86f0261e7fefe43b84ed1ba80c8cbc5a539434" +checksum = "57d16aa796183d9f2249243e7e8429ccf2f00d7b9134d807d5e68ec826c289af" dependencies = [ "ryu-js", "serde", @@ -10553,7 +10554,7 @@ version = "6.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" dependencies = [ - "cfg-expr", + "cfg-expr 0.15.8", "heck 0.5.0", "pkg-config", "toml 0.8.19", @@ -12116,11 +12117,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -12764,7 +12765,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" dependencies = [ - "zstd-safe 7.2.0", + "zstd-safe 7.2.1", ] [[package]] @@ -12779,18 +12780,18 @@ dependencies = [ [[package]] name = "zstd-safe" -version = "7.2.0" +version = "7.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa556e971e7b568dc775c136fc9de8c779b1c2fc3a63defaafadffdbd3181afa" +checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" dependencies = [ "zstd-sys", ] [[package]] name = "zstd-sys" -version = "2.0.12+zstd.1.5.6" +version = "2.0.13+zstd.1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a4e40c320c3cb459d9a9ff6de98cff88f4751ee9275d140e2be94a2b74e4c13" +checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" dependencies = [ "cc", "pkg-config", diff --git a/Cargo.toml b/Cargo.toml index 4b64bea7ee..f805eedf6a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -92,8 +92,10 @@ dioxus-static-site-generation = { path = "packages/static-generation", version = dioxus_server_macro = { path = "packages/server-macro", version = "0.6.0-alpha.0", default-features = false } lazy-js-bundle = { path = "packages/lazy-js-bundle", version = "0.6.0-alpha.0" } -manganis-cli-support = { version = "0.3.0-alpha.0", features = ["html"] } -manganis = { version = "0.3.0-alpha.0", default-features = false } +manganis-cli-support = { version = "0.3.0-alpha.0", features = ["html"], path = "../ecosystem-dioxus/manganis/cli-support" } +manganis = { version = "0.3.0-alpha.0", default-features = false, features = ["macro"], path = "../ecosystem-dioxus/manganis" } +# manganis-cli-support = { version = "0.3.0-alpha.0", features = ["html"] } +# manganis = { version = "0.3.0-alpha.0", default-features = false } warnings = { version = "0.2.0" } diff --git a/packages/desktop/src/protocol.rs b/packages/desktop/src/protocol.rs index e9e70ecaa4..f24a2bf757 100644 --- a/packages/desktop/src/protocol.rs +++ b/packages/desktop/src/protocol.rs @@ -114,30 +114,30 @@ fn assets_head() -> Option { fn resolve_resource(path: &Path) -> PathBuf { let mut base_path = get_asset_root_or_default(); - if running_in_dev_mode() { - base_path.push(path); - - // Special handler for Manganis filesystem fallback. - // We need this since Manganis provides assets from workspace root. - if !base_path.exists() { - let workspace_root = get_workspace_root_from_cargo(); - let asset_path = workspace_root.join(path); - return asset_path; - } - } else { - let mut resource_path = PathBuf::new(); - for component in path.components() { - // Tauri-bundle inserts special path segments for abnormal component paths - match component { - Component::Prefix(_) => {} - Component::RootDir => resource_path.push("_root_"), - Component::CurDir => {} - Component::ParentDir => resource_path.push("_up_"), - Component::Normal(p) => resource_path.push(p), - } - } - base_path.push(resource_path); - } + // if running_in_dev_mode() { + // base_path.push(path); + + // // Special handler for Manganis filesystem fallback. + // // We need this since Manganis provides assets from workspace root. + // if !base_path.exists() { + // let workspace_root = get_workspace_root_from_cargo(); + // let asset_path = workspace_root.join(path); + // return asset_path; + // } + // } else { + // let mut resource_path = PathBuf::new(); + // for component in path.components() { + // // Tauri-bundle inserts special path segments for abnormal component paths + // match component { + // Component::Prefix(_) => {} + // Component::RootDir => resource_path.push("_root_"), + // Component::CurDir => {} + // Component::ParentDir => resource_path.push("_up_"), + // Component::Normal(p) => resource_path.push(p), + // } + // } + // base_path.push(resource_path); + // } base_path } @@ -249,11 +249,11 @@ fn get_asset_root_or_default() -> PathBuf { get_asset_root().unwrap_or_else(|| std::env::current_dir().unwrap()) } -fn running_in_dev_mode() -> bool { - // If running under cargo, there's no bundle! - // There might be a smarter/more resilient way of doing this - std::env::var_os("CARGO").is_some() -} +// fn running_in_dev_mode() -> bool { +// // If running under cargo, there's no bundle! +// // There might be a smarter/more resilient way of doing this +// std::env::var_os("CARGO").is_some() +// } /// Get the asset directory, following tauri/cargo-bundles directory discovery approach /// From d263e402a1760fcc8836692bddb3f174b9952870 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 2 Aug 2024 18:55:32 -0700 Subject: [PATCH 002/139] upgrade tauri bundler, convert todo!() to unimplemented!() --- Cargo.lock | 917 ++++++++++++++++-- Cargo.toml | 58 +- packages/cli-config/src/bundle.rs | 21 +- packages/cli/src/cli/bundle.rs | 2 +- packages/core/docs/common_spawn_errors.md | 10 +- packages/core/docs/reactivity.md | 4 +- packages/core/src/error_boundary.rs | 6 +- packages/core/src/global_context.rs | 4 +- packages/core/src/scope_context.rs | 4 +- packages/desktop/src/protocol.rs | 12 +- packages/dioxus/README.md | 2 +- .../examples/axum-desktop/src/main.rs | 2 +- packages/fullstack/src/hooks/server_cached.rs | 2 +- packages/fullstack/src/hooks/server_future.rs | 2 +- packages/hooks/docs/derived_state.md | 2 +- packages/hooks/docs/rules_of_hooks.md | 10 +- packages/hooks/docs/side_effects.md | 2 +- packages/signals/docs/memo.md | 2 +- packages/signals/docs/signals.md | 2 +- packages/ssr/README.md | 4 +- 20 files changed, 916 insertions(+), 152 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 830419622f..47254fa1fa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -27,12 +27,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "adler32" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" - [[package]] name = "aead" version = "0.5.2" @@ -1040,6 +1034,12 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "base64" version = "0.21.7" @@ -1067,6 +1067,16 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "bcder" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c627747a6774aab38beb35990d88309481378558875a41da1a4b2e373c906ef0" +dependencies = [ + "bytes", + "smallvec", +] + [[package]] name = "better_scoped_tls" version = "0.1.1" @@ -1131,6 +1141,12 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" +[[package]] +name = "bitfield" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d7e60934ceec538daadb9d8432424ed043a904d8e0243f3c6446bce549a46ac" + [[package]] name = "bitflags" version = "1.3.2" @@ -1146,6 +1162,18 @@ dependencies = [ "serde", ] +[[package]] +name = "bitness" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57792b99d555ebf109c83169228076f7d997e2b37ba1a653850ccd703ac7bab0" +dependencies = [ + "sysctl", + "thiserror", + "uname", + "winapi", +] + [[package]] name = "bitstream-io" version = "2.5.0" @@ -1179,6 +1207,15 @@ dependencies = [ "generic-array 0.14.7", ] +[[package]] +name = "block-padding" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +dependencies = [ + "generic-array 0.14.7", +] + [[package]] name = "blocking" version = "1.6.1" @@ -1192,6 +1229,16 @@ dependencies = [ "piper", ] +[[package]] +name = "blowfish" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7" +dependencies = [ + "byteorder", + "cipher", +] + [[package]] name = "borsh" version = "1.5.1" @@ -1266,6 +1313,15 @@ dependencies = [ "serde", ] +[[package]] +name = "buffer-redux" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c9f8ddd22e0a12391d1e7ada69ec3b0da1914f1cec39c5cf977143c5b2854f5" +dependencies = [ + "memchr", +] + [[package]] name = "built" version = "0.7.3" @@ -1376,6 +1432,16 @@ dependencies = [ "system-deps", ] +[[package]] +name = "camellia" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3264e2574e9ef2b53ce6f536dea83a69ac0bc600b762d1523ff83fe07230ce30" +dependencies = [ + "byteorder", + "cipher", +] + [[package]] name = "camino" version = "1.1.7" @@ -1471,6 +1537,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" +[[package]] +name = "cast5" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b07d673db1ccf000e90f54b819db9e75a8348d6eb056e9b8ab53231b7a9911" +dependencies = [ + "cipher", +] + [[package]] name = "castaway" version = "0.2.3" @@ -1516,6 +1591,15 @@ dependencies = [ "uuid", ] +[[package]] +name = "cfb-mode" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "738b8d467867f80a71351933f70461f5b56f24d5c93e0cf216e59229c968d330" +dependencies = [ + "cipher", +] + [[package]] name = "cfg-expr" version = "0.15.8" @@ -1974,13 +2058,10 @@ dependencies = [ ] [[package]] -name = "core2" -version = "0.4.0" +name = "cpio" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" -dependencies = [ - "memchr", -] +checksum = "27e77cfc4543efb4837662cb7cd53464ae66f0fd5c708d71e0f338b1c11d62d3" [[package]] name = "cpufeatures" @@ -2006,6 +2087,12 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" +[[package]] +name = "crc24" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd121741cf3eb82c08dd3023eb55bf2665e5f60ec20f89760cf836ae4562e6a0" + [[package]] name = "crc32fast" version = "1.4.2" @@ -2128,6 +2215,18 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array 0.14.7", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -2217,6 +2316,33 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "rustc_version 0.4.0", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + [[package]] name = "cvt" version = "0.1.2" @@ -2270,14 +2396,38 @@ dependencies = [ "syn 2.0.72", ] +[[package]] +name = "darling" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +dependencies = [ + "darling_core 0.14.4", + "darling_macro 0.14.4", +] + [[package]] name = "darling" version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.20.10", + "darling_macro 0.20.10", +] + +[[package]] +name = "darling_core" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.10.0", + "syn 1.0.109", ] [[package]] @@ -2296,20 +2446,25 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.20.10" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" dependencies = [ - "darling_core", + "darling_core 0.14.4", "quote", - "syn 2.0.72", + "syn 1.0.109", ] [[package]] -name = "dary_heap" -version = "0.3.6" +name = "darling_macro" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7762d17f1241643615821a8455a0b2c3e803784b058693d990b11f2dce25a0ca" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core 0.20.10", + "quote", + "syn 2.0.72", +] [[package]] name = "dashmap" @@ -2370,6 +2525,37 @@ dependencies = [ "serde", ] +[[package]] +name = "derive_builder" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d67778784b508018359cbc8696edb3db78160bab2c2a28ba7f56ef6932997f8" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c11bdc11a0c47bc7d37d582b5285da6849c96681023680b906673c5707af7b0f" +dependencies = [ + "darling 0.14.4", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_builder_macro" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebcda35c7a396850a55ffeac740804b40ffec779b98fffbb1738f4033f0ee79e" +dependencies = [ + "derive_builder_core", + "syn 1.0.109", +] + [[package]] name = "derive_more" version = "0.99.18" @@ -2383,6 +2569,15 @@ dependencies = [ "syn 2.0.72", ] +[[package]] +name = "des" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdd80ce8ce993de27e9f063a444a4d53ce8e8db4c1f00cc03af5ad5a9867a1e" +dependencies = [ + "cipher", +] + [[package]] name = "dialoguer" version = "0.11.0" @@ -3230,6 +3425,22 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" +[[package]] +name = "dsa" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48bc224a9084ad760195584ce5abb3c2c34a225fa312a128ad245a6b412b7689" +dependencies = [ + "digest", + "num-bigint-dig", + "num-traits", + "pkcs8", + "rfc6979", + "sha2", + "signature", + "zeroize", +] + [[package]] name = "dtoa" version = "1.0.9" @@ -3251,6 +3462,44 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +dependencies = [ + "curve25519-dalek", + "ed25519", + "serde", + "sha2", + "subtle", + "zeroize", +] + [[package]] name = "either" version = "1.13.0" @@ -3260,6 +3509,27 @@ dependencies = [ "serde", ] +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array 0.14.7", + "group", + "hkdf", + "pem-rfc7468", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + [[package]] name = "encode_unicode" version = "0.3.6" @@ -3281,6 +3551,28 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" +[[package]] +name = "enum-display-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f16ef37b2a9b242295d61a154ee91ae884afff6b8b933b486b12481cc58310ca" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "enum-primitive-derive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba7795da175654fe16979af73f81f26a8ea27638d8d9823d317016888a63dc4c" +dependencies = [ + "num-traits", + "quote", + "syn 2.0.72", +] + [[package]] name = "enumflags2" version = "0.7.10" @@ -3317,7 +3609,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59c3b24c345d8c314966bdc1832f6c2635bfcce8e7cf363bd115987bba2ee242" dependencies = [ - "darling", + "darling 0.20.10", "proc-macro2", "quote", "syn 2.0.72", @@ -3365,6 +3657,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "erased-serde" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24e2389d65ab4fab27dc2a5de7b191e1f6617d1f1c8855c0dc569c94a4cbb18d" +dependencies = [ + "serde", + "typeid", +] + [[package]] name = "errno" version = "0.3.9" @@ -3528,6 +3830,22 @@ dependencies = [ "log", ] +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + [[package]] name = "field-offset" version = "0.3.6" @@ -3962,6 +4280,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -4609,6 +4928,17 @@ dependencies = [ "system-deps", ] +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "gtk" version = "0.18.1" @@ -4711,9 +5041,9 @@ dependencies = [ [[package]] name = "handlebars" -version = "4.5.0" +version = "5.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faa67bab9ff362228eb3d00bd024a4965d8231bbb7921167f0cfa66c6626b225" +checksum = "d08485b96a0e6393e9e4d1b8d48cf74ad6c063cd905eb33f42c1ce3f0377539b" dependencies = [ "log", "pest", @@ -5197,6 +5527,15 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" +[[package]] +name = "idea" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "075557004419d7f2031b8bb7f44bb43e55a83ca7b63076a8fb8fe75753836477" +dependencies = [ + "cipher", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -5357,9 +5696,9 @@ dependencies = [ [[package]] name = "infer" -version = "0.13.0" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f551f8c3a39f68f986517db0d1759de85881894fdc7db798bd2a9df9cb04b7fc" +checksum = "cb33622da908807a06f9513c19b3c1ad50fab3e4137d82a78107d502075aa199" dependencies = [ "cfb", ] @@ -5519,6 +5858,12 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[package]] +name = "iter-read" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a598c1abae8e3456ebda517868b254b6bc2a9bb6501ffd5b9d0875bf332e048b" + [[package]] name = "itertools" version = "0.10.5" @@ -5650,6 +5995,29 @@ dependencies = [ "serde_json", ] +[[package]] +name = "k256" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2", + "signature", +] + +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + [[package]] name = "keyboard-types" version = "0.7.0" @@ -5764,30 +6132,6 @@ version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" -[[package]] -name = "libflate" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45d9dfdc14ea4ef0900c1cddbc8dcd553fbaacd8a4a282cf4018ae9dd04fb21e" -dependencies = [ - "adler32", - "core2", - "crc32fast", - "dary_heap", - "libflate_lz77", -] - -[[package]] -name = "libflate_lz77" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e0d73b369f386f1c44abd9c570d5318f55ccde816ff4b562fa452e5182863d" -dependencies = [ - "core2", - "hashbrown 0.14.5", - "rle-decode-fast", -] - [[package]] name = "libfuzzer-sys" version = "0.4.7" @@ -6056,6 +6400,17 @@ dependencies = [ "hashbrown 0.14.5", ] +[[package]] +name = "lzma-sys" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + [[package]] name = "mac" version = "0.1.1" @@ -6416,7 +6771,7 @@ dependencies = [ "bitflags 1.3.2", "jni-sys", "ndk-sys", - "num_enum", + "num_enum 0.5.11", "raw-window-handle 0.5.2", "thiserror", ] @@ -6553,6 +6908,20 @@ dependencies = [ "winapi", ] +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + [[package]] name = "num-bigint" version = "0.4.6" @@ -6577,10 +6946,20 @@ dependencies = [ "num-iter", "num-traits", "rand 0.8.5", + "serde", "smallvec", "zeroize", ] +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -6655,7 +7034,16 @@ version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" dependencies = [ - "num_enum_derive", + "num_enum_derive 0.5.11", +] + +[[package]] +name = "num_enum" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +dependencies = [ + "num_enum_derive 0.7.3", ] [[package]] @@ -6670,6 +7058,18 @@ dependencies = [ "syn 1.0.109", ] +[[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 3.1.0", + "proc-macro2", + "quote", + "syn 2.0.72", +] + [[package]] name = "num_threads" version = "0.1.7" @@ -6736,6 +7136,16 @@ dependencies = [ "wasmparser 0.214.0", ] +[[package]] +name = "once-cell-regex" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3de7e389a5043420c8f2b95ed03f3f104ad6f4c41f7d7e27298f033abc253e8" +dependencies = [ + "once_cell", + "regex", +] + [[package]] name = "once_cell" version = "1.19.0" @@ -6856,6 +7266,30 @@ dependencies = [ "supports-color", ] +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "p384" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + [[package]] name = "pango" version = "0.18.3" @@ -7005,6 +7439,16 @@ dependencies = [ "sha2", ] +[[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 = "pem-rfc7468" version = "0.7.0" @@ -7075,6 +7519,62 @@ dependencies = [ "indexmap 2.3.0", ] +[[package]] +name = "pgp" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "031fa1e28c4cb54c90502ef0642a44ef10ec8349349ebe6372089f1b1ef4f297" +dependencies = [ + "aes", + "base64 0.21.7", + "bitfield", + "block-padding", + "blowfish", + "bstr", + "buffer-redux", + "byteorder", + "camellia", + "cast5", + "cfb-mode", + "chrono", + "cipher", + "const-oid", + "crc24", + "curve25519-dalek", + "derive_builder", + "des", + "digest", + "dsa", + "ed25519-dalek", + "elliptic-curve", + "flate2", + "generic-array 0.14.7", + "hex", + "idea", + "iter-read", + "k256", + "log", + "md-5", + "nom", + "num-bigint-dig", + "num-traits", + "num_enum 0.7.3", + "p256", + "p384", + "rand 0.8.5", + "ripemd", + "rsa", + "sha1", + "sha2", + "sha3", + "signature", + "smallvec", + "thiserror", + "twofish", + "x25519-dalek", + "zeroize", +] + [[package]] name = "phf" version = "0.8.0" @@ -7467,6 +7967,15 @@ dependencies = [ "syn 2.0.72", ] +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + [[package]] name = "proc-macro-crate" version = "1.3.1" @@ -8116,6 +8625,16 @@ dependencies = [ "winreg 0.52.0", ] +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + [[package]] name = "rfd" version = "0.14.1" @@ -8191,6 +8710,15 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "ripemd" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" +dependencies = [ + "digest", +] + [[package]] name = "rkyv" version = "0.7.44" @@ -8220,12 +8748,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "rle-decode-fast" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3582f63211428f83597b51b2ddb88e2a91a9d52d12831f9d08f5e624e8977422" - [[package]] name = "ron" version = "0.8.1" @@ -8247,6 +8769,36 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "rpm" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e68a0d60350e5f4229cd69f08ec8f373e34424701702d1ee51a89aee1e9adcd1" +dependencies = [ + "bitflags 2.6.0", + "bzip2", + "chrono", + "cpio", + "digest", + "enum-display-derive", + "enum-primitive-derive", + "flate2", + "hex", + "itertools 0.12.1", + "log", + "md-5", + "nom", + "num", + "num-derive", + "num-traits", + "pgp", + "sha1", + "sha2", + "thiserror", + "xz2", + "zstd 0.13.2", +] + [[package]] name = "rsa" version = "0.9.6" @@ -8533,6 +9085,20 @@ version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array 0.14.7", + "pkcs8", + "subtle", + "zeroize", +] + [[package]] name = "security-framework" version = "2.11.1" @@ -8624,6 +9190,17 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-untagged" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2676ba99bd82f75cae5cbd2c8eda6fa0b8760f18978ea840e980dd5567b5c5b6" +dependencies = [ + "erased-serde", + "serde", + "typeid", +] + [[package]] name = "serde-wasm-bindgen" version = "0.5.0" @@ -8745,7 +9322,7 @@ version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8fee4991ef4f274617a51ad4af30519438dacb2f56ac773b08a1922ff743350" dependencies = [ - "darling", + "darling 0.20.10", "proc-macro2", "quote", "syn 2.0.72", @@ -8848,6 +9425,16 @@ dependencies = [ "digest", ] +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -9058,6 +9645,17 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "socks" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c3dbbd9ae980613c6dd8e28a9407b50509d3803b57624d5dfe8315218cd58b" +dependencies = [ + "byteorder", + "libc", + "winapi", +] + [[package]] name = "soup3" version = "0.5.0" @@ -10527,6 +11125,19 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +[[package]] +name = "sysctl" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225e483f02d0ad107168dc57381a8a40c3aeea6abe47f37506931f861643cfa8" +dependencies = [ + "bitflags 1.3.2", + "byteorder", + "libc", + "thiserror", + "walkdir", +] + [[package]] name = "system-configuration" version = "0.5.1" @@ -10635,33 +11246,36 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tauri-bundler" -version = "1.4.8" +version = "2.0.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4693930d30c4836ef41f51097502fd77a0ae9f54c3af6aafb8997b2a81f40b4" +checksum = "afefaa18bdaa49afff89a299d6afb05de7ffc2017d84d717239455e6645d21ea" dependencies = [ "anyhow", "ar", - "dirs-next", + "bitness", + "dirs", "dunce", + "flate2", "glob", "handlebars", - "heck 0.4.1", + "heck 0.5.0", "hex", "image 0.24.9", - "libflate", "log", "md5", "os_pipe", "plist", "regex", + "rpm", "semver 1.0.23", "serde", "serde_json", "sha1", "sha2", - "strsim 0.10.0", + "strsim 0.11.1", "tar", "tauri-icns", + "tauri-macos-sign", "tauri-utils", "tempfile", "thiserror", @@ -10669,8 +11283,8 @@ dependencies = [ "ureq", "uuid", "walkdir", - "windows-sys 0.48.0", - "winreg 0.51.0", + "windows-registry", + "windows-sys 0.52.0", "zip", ] @@ -10684,31 +11298,51 @@ dependencies = [ "png", ] +[[package]] +name = "tauri-macos-sign" +version = "0.1.0-beta.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a689cc26acd1e619b317f7cec95db2eb9fa1c69888030162a546a7fafa7eb3d" +dependencies = [ + "anyhow", + "dirs-next", + "once-cell-regex", + "os_pipe", + "plist", + "rand 0.8.5", + "serde", + "serde_json", + "tempfile", + "x509-certificate", +] + [[package]] name = "tauri-utils" -version = "1.5.4" +version = "2.0.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "450b17a7102e5d46d4bdabae0d1590fd27953e704e691fc081f06c06d2253b35" +checksum = "6f435eeaae1e69cf93cf19da0f727989eed2e5eb6fc63a8d21432f59dd3ac4ac" dependencies = [ "ctor", "dunce", "glob", - "heck 0.5.0", "html5ever", - "infer 0.13.0", + "infer 0.15.0", "json-patch", "kuchikiki", "log", "memchr", "phf 0.11.2", + "regex", "semver 1.0.23", "serde", + "serde-untagged", "serde_json", "serde_with", "thiserror", + "toml 0.8.19", "url", + "urlpattern", "walkdir", - "windows-version", ] [[package]] @@ -11344,6 +11978,15 @@ dependencies = [ "utf-8", ] +[[package]] +name = "twofish" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a78e83a30223c757c3947cd144a31014ff04298d8719ae10d03c31c0448c8013" +dependencies = [ + "cipher", +] + [[package]] name = "twox-hash" version = "1.6.3" @@ -11360,6 +12003,12 @@ version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" +[[package]] +name = "typeid" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "059d83cc991e7a42fc37bd50941885db0888e34209f8cfd9aab07ddec03bc9cf" + [[package]] name = "typenum" version = "1.17.0" @@ -11383,6 +12032,56 @@ dependencies = [ "winapi", ] +[[package]] +name = "uname" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b72f89f0ca32e4db1c04e2a72f5345d59796d4866a1ee0609084569f73683dc8" +dependencies = [ + "libc", +] + +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-ucd-ident" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + [[package]] name = "unicase" version = "2.7.0" @@ -11511,6 +12210,7 @@ dependencies = [ "once_cell", "rustls 0.23.12", "rustls-pki-types", + "socks", "url", "webpki-roots 0.26.3", ] @@ -11533,6 +12233,19 @@ version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" +[[package]] +name = "urlpattern" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9bd5ff03aea02fa45b13a7980151fe45009af1980ba69f651ec367121a31609" +dependencies = [ + "derive_more", + "regex", + "serde", + "unic-ucd-ident", + "url", +] + [[package]] name = "utf-8" version = "0.7.6" @@ -12227,6 +12940,16 @@ dependencies = [ "syn 2.0.72", ] +[[package]] +name = "windows-registry" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acc134c90a0318d873ec962b13149e9c862ff0d2669082a709a4810167a3c6ee" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", +] + [[package]] name = "windows-result" version = "0.1.2" @@ -12487,16 +13210,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "winreg" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "937f3df7948156640f46aacef17a70db0de5917bda9c92b0f751f3a955b588fc" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - [[package]] name = "winreg" version = "0.52.0" @@ -12579,6 +13292,37 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "x25519-dalek" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" +dependencies = [ + "curve25519-dalek", + "rand_core 0.6.4", + "serde", + "zeroize", +] + +[[package]] +name = "x509-certificate" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66534846dec7a11d7c50a74b7cdb208b9a581cad890b7866430d438455847c85" +dependencies = [ + "bcder", + "bytes", + "chrono", + "der", + "hex", + "pem", + "ring", + "signature", + "spki", + "thiserror", + "zeroize", +] + [[package]] name = "xattr" version = "1.3.1" @@ -12606,6 +13350,15 @@ version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a5cbf750400958819fb6178eaa83bee5cd9c29a26a40cc241df8c70fdd46984" +[[package]] +name = "xz2" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2" +dependencies = [ + "lzma-sys", +] + [[package]] name = "yansi" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index f805eedf6a..1bceccc8d4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,34 +63,34 @@ version = "0.6.0-alpha.1" # dependencies that are shared across packages [workspace.dependencies] -dioxus = { path = "packages/dioxus", version = "0.6.0-alpha.0" } -dioxus-lib = { path = "packages/dioxus-lib", version = "0.6.0-alpha.0" } -dioxus-core = { path = "packages/core", version = "0.6.0-alpha.0" } -dioxus-core-macro = { path = "packages/core-macro", version = "0.6.0-alpha.0" } -dioxus-config-macro = { path = "packages/config-macro", version = "0.6.0-alpha.0" } -dioxus-router = { path = "packages/router", version = "0.6.0-alpha.0" } -dioxus-router-macro = { path = "packages/router-macro", version = "0.6.0-alpha.0" } -dioxus-html = { path = "packages/html", version = "0.6.0-alpha.0", default-features = false } -dioxus-html-internal-macro = { path = "packages/html-internal-macro", version = "0.6.0-alpha.0" } -dioxus-hooks = { path = "packages/hooks", version = "0.6.0-alpha.0" } -dioxus-web = { path = "packages/web", version = "0.6.0-alpha.0", default-features = false } -dioxus-ssr = { path = "packages/ssr", version = "0.6.0-alpha.0", default-features = false } -dioxus-desktop = { path = "packages/desktop", version = "0.6.0-alpha.0", default-features = false } -dioxus-mobile = { path = "packages/mobile", version = "0.6.0-alpha.0" } -dioxus-interpreter-js = { path = "packages/interpreter", version = "0.6.0-alpha.0" } -dioxus-liveview = { path = "packages/liveview", version = "0.6.0-alpha.0" } -dioxus-autofmt = { path = "packages/autofmt", version = "0.6.0-alpha.0" } -dioxus-check = { path = "packages/check", version = "0.6.0-alpha.0" } -dioxus-rsx = { path = "packages/rsx", version = "0.6.0-alpha.0" } -rsx-rosetta = { path = "packages/rsx-rosetta", version = "0.6.0-alpha.0" } -dioxus-signals = { path = "packages/signals", version = "0.6.0-alpha.0" } -dioxus-cli-config = { path = "packages/cli-config", version = "0.6.0-alpha.0", default-features = false} -generational-box = { path = "packages/generational-box", version = "0.6.0-alpha.0" } -dioxus-hot-reload = { path = "packages/hot-reload", version = "0.6.0-alpha.0" } +dioxus = { path = "packages/dioxus", version = "0.6.0-alpha.1" } +dioxus-lib = { path = "packages/dioxus-lib", version = "0.6.0-alpha.1" } +dioxus-core = { path = "packages/core", version = "0.6.0-alpha.1" } +dioxus-core-macro = { path = "packages/core-macro", version = "0.6.0-alpha.1" } +dioxus-config-macro = { path = "packages/config-macro", version = "0.6.0-alpha.1" } +dioxus-router = { path = "packages/router", version = "0.6.0-alpha.1" } +dioxus-router-macro = { path = "packages/router-macro", version = "0.6.0-alpha.1" } +dioxus-html = { path = "packages/html", version = "0.6.0-alpha.1", default-features = false } +dioxus-html-internal-macro = { path = "packages/html-internal-macro", version = "0.6.0-alpha.1" } +dioxus-hooks = { path = "packages/hooks", version = "0.6.0-alpha.1" } +dioxus-web = { path = "packages/web", version = "0.6.0-alpha.1", default-features = false } +dioxus-ssr = { path = "packages/ssr", version = "0.6.0-alpha.1", default-features = false } +dioxus-desktop = { path = "packages/desktop", version = "0.6.0-alpha.1", default-features = false } +dioxus-mobile = { path = "packages/mobile", version = "0.6.0-alpha.1" } +dioxus-interpreter-js = { path = "packages/interpreter", version = "0.6.0-alpha.1" } +dioxus-liveview = { path = "packages/liveview", version = "0.6.0-alpha.1" } +dioxus-autofmt = { path = "packages/autofmt", version = "0.6.0-alpha.1" } +dioxus-check = { path = "packages/check", version = "0.6.0-alpha.1" } +dioxus-rsx = { path = "packages/rsx", version = "0.6.0-alpha.1" } +rsx-rosetta = { path = "packages/rsx-rosetta", version = "0.6.0-alpha.1" } +dioxus-signals = { path = "packages/signals", version = "0.6.0-alpha.1" } +dioxus-cli-config = { path = "packages/cli-config", version = "0.6.0-alpha.1", default-features = false} +generational-box = { path = "packages/generational-box", version = "0.6.0-alpha.1" } +dioxus-hot-reload = { path = "packages/hot-reload", version = "0.6.0-alpha.1" } dioxus-fullstack = { path = "packages/fullstack", version = "0.6.0-alpha.1" } -dioxus-static-site-generation = { path = "packages/static-generation", version = "0.6.0-alpha.0" } -dioxus_server_macro = { path = "packages/server-macro", version = "0.6.0-alpha.0", default-features = false } -lazy-js-bundle = { path = "packages/lazy-js-bundle", version = "0.6.0-alpha.0" } +dioxus-static-site-generation = { path = "packages/static-generation", version = "0.6.0-alpha.1" } +dioxus_server_macro = { path = "packages/server-macro", version = "0.6.0-alpha.1", default-features = false } +lazy-js-bundle = { path = "packages/lazy-js-bundle", version = "0.6.0-alpha.1" } manganis-cli-support = { version = "0.3.0-alpha.0", features = ["html"], path = "../ecosystem-dioxus/manganis/cli-support" } manganis = { version = "0.3.0-alpha.0", default-features = false, features = ["macro"], path = "../ecosystem-dioxus/manganis" } @@ -118,8 +118,8 @@ thiserror = "1.0.40" prettyplease = { version = "0.2.20", features = ["verbatim"] } const_format = "0.2.32" cargo_toml = { version = "0.20.3" } -tauri-utils = { version = "=1.5.*" } -tauri-bundler = { version = "=1.4.*" } +tauri-utils = { version = "=2.0.0-rc.0" } +tauri-bundler = { version = "=2.0.0-rc.0" } lru = "0.12.2" async-trait = "0.1.77" axum = "0.7.0" diff --git a/packages/cli-config/src/bundle.rs b/packages/cli-config/src/bundle.rs index 07c4fec213..1bef710dbe 100644 --- a/packages/cli-config/src/bundle.rs +++ b/packages/cli-config/src/bundle.rs @@ -53,6 +53,16 @@ impl From for tauri_bundler::DebianSettings { depends: val.depends, files: val.files, desktop_template: None, + provides: todo!(), + conflicts: todo!(), + replaces: todo!(), + section: todo!(), + priority: todo!(), + changelog: todo!(), + pre_install_script: todo!(), + post_install_script: todo!(), + pre_remove_script: todo!(), + post_remove_script: todo!(), } } } @@ -104,8 +114,6 @@ impl From for tauri_bundler::WixSettings { feature_group_refs: val.feature_group_refs, feature_refs: val.feature_refs, merge_refs: val.merge_refs, - skip_webview_install: val.skip_webview_install, - license: val.license, enable_elevated_update_task: val.enable_elevated_update_task, banner_path: val.banner_path, dialog_image_path: val.dialog_image_path, @@ -132,12 +140,13 @@ impl From for tauri_bundler::MacOsSettings { tauri_bundler::MacOsSettings { frameworks: val.frameworks, minimum_system_version: val.minimum_system_version, - license: val.license, exception_domain: val.exception_domain, signing_identity: val.signing_identity, provider_short_name: val.provider_short_name, entitlements: val.entitlements, info_plist_path: val.info_plist_path, + files: todo!(), + hardened_runtime: todo!(), } } } @@ -170,6 +179,7 @@ impl From for tauri_bundler::WindowsSettings { webview_fixed_runtime_path: val.webview_fixed_runtime_path, allow_downgrades: val.allow_downgrades, nsis: val.nsis.map(Into::into), + sign_command: todo!(), } } } @@ -191,7 +201,6 @@ pub struct NsisSettings { impl From for tauri_bundler::NsisSettings { fn from(val: NsisSettings) -> Self { tauri_bundler::NsisSettings { - license: val.license, header_image: val.header_image, sidebar_image: val.sidebar_image, installer_icon: val.installer_icon, @@ -200,7 +209,9 @@ impl From for tauri_bundler::NsisSettings { display_language_selector: val.display_language_selector, custom_language_files: None, template: None, - compression: None, + compression: tauri_utils::config::NsisCompression::None, + start_menu_folder: todo!(), + installer_hooks: todo!(), } } } diff --git a/packages/cli/src/cli/bundle.rs b/packages/cli/src/cli/bundle.rs index f82843edc0..9b4597a8b9 100644 --- a/packages/cli/src/cli/bundle.rs +++ b/packages/cli/src/cli/bundle.rs @@ -193,7 +193,7 @@ impl Bundle { #[cfg(target_os = "macos")] std::env::set_var("CI", "true"); - tauri_bundler::bundle::bundle_project(settings.unwrap()).unwrap_or_else(|err|{ + tauri_bundler::bundle::bundle_project(&settings.unwrap()).unwrap_or_else(|err|{ #[cfg(target_os = "macos")] panic!("Failed to bundle project: {:#?}\nMake sure you have automation enabled in your terminal (https://github.com/tauri-apps/tauri/issues/3055#issuecomment-1624389208) and full disk access enabled for your terminal (https://github.com/tauri-apps/tauri/issues/3055#issuecomment-1624389208)", err); #[cfg(not(target_os = "macos"))] diff --git a/packages/core/docs/common_spawn_errors.md b/packages/core/docs/common_spawn_errors.md index 257d7c54d9..287dfa2493 100644 --- a/packages/core/docs/common_spawn_errors.md +++ b/packages/core/docs/common_spawn_errors.md @@ -20,7 +20,7 @@ fn App() -> Element { }) }); - todo!() + unimplemented!() } ``` @@ -39,7 +39,7 @@ fn App() -> Element { }) }); - todo!() + unimplemented!() } ``` @@ -68,7 +68,7 @@ fn MyComponent(string: String) -> Element { }) }); - todo!() + unimplemented!() } ``` @@ -92,7 +92,7 @@ fn MyComponent(string: ReadOnlySignal) -> Element { }) }); - todo!() + unimplemented!() } ``` @@ -119,7 +119,7 @@ fn MyComponent(string: String) -> Element { }) }); - todo!() + unimplemented!() } ``` diff --git a/packages/core/docs/reactivity.md b/packages/core/docs/reactivity.md index fdb0fe8c8b..03048183c3 100644 --- a/packages/core/docs/reactivity.md +++ b/packages/core/docs/reactivity.md @@ -89,14 +89,14 @@ let doubled = use_memo(move || state() * 2); #[component] fn MyComponent(state: i32) -> Element { let doubled = use_memo(move || state * 2); - todo!() + unimplemented!() } // ✅ Wrap your props in ReadOnlySignal to make them reactive #[component] fn MyReactiveComponent(state: ReadOnlySignal) -> Element { let doubled = use_memo(move || state() * 2); - todo!() + unimplemented!() } ``` diff --git a/packages/core/src/error_boundary.rs b/packages/core/src/error_boundary.rs index 8d86c03d70..bb6d102c32 100644 --- a/packages/core/src/error_boundary.rs +++ b/packages/core/src/error_boundary.rs @@ -108,7 +108,7 @@ pub trait Context: private::Sealed { /// "Error parsing number: {error}" /// } /// })?; - /// todo!() + /// unimplemented!() /// } /// ``` fn show(self, display_error: impl FnOnce(&E) -> Element) -> Result; @@ -122,7 +122,7 @@ pub trait Context: private::Sealed { /// // You can bubble up errors with `?` inside components, and event handlers /// // Along with the error itself, you can provide a way to display the error by calling `context` /// let number = "-1234".parse::().context("Parsing number inside of the NumberParser")?; - /// todo!() + /// unimplemented!() /// } /// ``` fn context(self, context: C) -> Result; @@ -136,7 +136,7 @@ pub trait Context: private::Sealed { /// // You can bubble up errors with `?` inside components, and event handlers /// // Along with the error itself, you can provide a way to display the error by calling `context` /// let number = "-1234".parse::().with_context(|| format!("Timestamp: {:?}", std::time::Instant::now()))?; - /// todo!() + /// unimplemented!() /// } /// ``` fn with_context(self, context: impl FnOnce() -> C) -> Result; diff --git a/packages/core/src/global_context.rs b/packages/core/src/global_context.rs index 92a4dfd13b..3318fb458b 100644 --- a/packages/core/src/global_context.rs +++ b/packages/core/src/global_context.rs @@ -25,13 +25,13 @@ pub fn vdom_is_rendering() -> bool { /// fn Component() -> Element { /// let request = spawn(async move { /// match reqwest::get("https://api.example.com").await { -/// Ok(_) => todo!(), +/// Ok(_) => unimplemented!(), /// // You can explicitly throw an error into a scope with throw_error /// Err(err) => ScopeId::APP.throw_error(err) /// } /// }); /// -/// todo!() +/// unimplemented!() /// } /// ``` pub fn throw_error(error: impl Into + 'static) { diff --git a/packages/core/src/scope_context.rs b/packages/core/src/scope_context.rs index 9c7abfb941..c37b562a98 100644 --- a/packages/core/src/scope_context.rs +++ b/packages/core/src/scope_context.rs @@ -556,13 +556,13 @@ impl ScopeId { /// fn Component() -> Element { /// let request = spawn(async move { /// match reqwest::get("https://api.example.com").await { - /// Ok(_) => todo!(), + /// Ok(_) => unimplemented!(), /// // You can explicitly throw an error into a scope with throw_error /// Err(err) => ScopeId::APP.throw_error(err) /// } /// }); /// - /// todo!() + /// unimplemented!() /// } /// ``` pub fn throw_error(self, error: impl Into + 'static) { diff --git a/packages/desktop/src/protocol.rs b/packages/desktop/src/protocol.rs index f24a2bf757..4f93de711d 100644 --- a/packages/desktop/src/protocol.rs +++ b/packages/desktop/src/protocol.rs @@ -266,12 +266,12 @@ fn get_asset_root_or_default() -> PathBuf { /// - [ ] Android #[allow(unreachable_code)] fn get_asset_root() -> Option { - if running_in_dev_mode() { - return dioxus_cli_config::CURRENT_CONFIG - .as_ref() - .map(|c| c.application.out_dir.clone()) - .ok(); - } + // if running_in_dev_mode() { + // return dioxus_cli_config::CURRENT_CONFIG + // .as_ref() + // .map(|c| c.application.out_dir.clone()) + // .ok(); + // } #[cfg(target_os = "macos")] { diff --git a/packages/dioxus/README.md b/packages/dioxus/README.md index a841353c09..1df30e3eda 100644 --- a/packages/dioxus/README.md +++ b/packages/dioxus/README.md @@ -114,7 +114,7 @@ In Dioxus, all properties are memorized by default with Clone and PartialEq. For ```rust, no_run # use dioxus::prelude::*; -# #[component] fn Header(title: String, color: String) -> Element { todo!() } +# #[component] fn Header(title: String, color: String) -> Element { unimplemented!() } #[component] fn App() -> Element { rsx! { diff --git a/packages/fullstack/examples/axum-desktop/src/main.rs b/packages/fullstack/examples/axum-desktop/src/main.rs index ecefd2636b..160c43f779 100644 --- a/packages/fullstack/examples/axum-desktop/src/main.rs +++ b/packages/fullstack/examples/axum-desktop/src/main.rs @@ -12,7 +12,7 @@ fn main() { } /// Run with `cargo run --features server` -#[cfg(all(feature = "server", not(feature = "desktop")))] +#[cfg(not(feature = "desktop"))] #[tokio::main] async fn main() { use server_fn::axum::register_explicit; diff --git a/packages/fullstack/src/hooks/server_cached.rs b/packages/fullstack/src/hooks/server_cached.rs index 499db03623..7e3b55a99e 100644 --- a/packages/fullstack/src/hooks/server_cached.rs +++ b/packages/fullstack/src/hooks/server_cached.rs @@ -17,7 +17,7 @@ use serde::{de::DeserializeOwned, Serialize}; /// 1234 /// }); /// -/// todo!() +/// unimplemented!() /// } /// ``` pub fn use_server_cached( diff --git a/packages/fullstack/src/hooks/server_future.rs b/packages/fullstack/src/hooks/server_future.rs index 2f910c5ec7..bf6893cc99 100644 --- a/packages/fullstack/src/hooks/server_future.rs +++ b/packages/fullstack/src/hooks/server_future.rs @@ -40,7 +40,7 @@ use std::future::Future; /// /// ```rust, no_run /// # use dioxus::prelude::*; -/// # async fn fetch_article(id: u32) -> String { todo!() } +/// # async fn fetch_article(id: u32) -> String { unimplemented!() } /// use dioxus::prelude::*; /// /// fn App() -> Element { diff --git a/packages/hooks/docs/derived_state.md b/packages/hooks/docs/derived_state.md index 7f0db4f6dd..8db95724ac 100644 --- a/packages/hooks/docs/derived_state.md +++ b/packages/hooks/docs/derived_state.md @@ -72,6 +72,6 @@ Signals will automatically be added as dependencies, so you don't need to call t fn Comp(count: u32) -> Element { // Because the memo subscribes to `count` by adding it as a dependency, the memo will rerun every time `count` changes. let new_count = use_memo(use_reactive((&count,), |(count,)| count + 1)); - todo!() + unimplemented!() } ``` diff --git a/packages/hooks/docs/rules_of_hooks.md b/packages/hooks/docs/rules_of_hooks.md index 9ae67840dd..7d87c2676f 100644 --- a/packages/hooks/docs/rules_of_hooks.md +++ b/packages/hooks/docs/rules_of_hooks.md @@ -18,7 +18,7 @@ fn App() -> Element { let string = use_signal(|| "hello world".to_string()); } - todo!() + unimplemented!() } fn use_my_hook() -> Signal { @@ -56,7 +56,7 @@ fn App() -> Element { _ => (), } - todo!() + unimplemented!() } ``` @@ -71,7 +71,7 @@ fn App() -> Element { let string = use_signal(|| "hello world".to_string()); } - todo!() + unimplemented!() } ``` @@ -103,7 +103,7 @@ fn App() -> Element { string() }); - todo!() + unimplemented!() } ``` @@ -121,7 +121,7 @@ fn App() -> Element { let string = use_signal(|| "hello world".to_string()); // Hook 2 let doubled = use_memo(move || number() * 2); // Hook 3 - todo!() + unimplemented!() } ``` diff --git a/packages/hooks/docs/side_effects.md b/packages/hooks/docs/side_effects.md index 1f63877b8d..5765d0ebf7 100644 --- a/packages/hooks/docs/side_effects.md +++ b/packages/hooks/docs/side_effects.md @@ -52,7 +52,7 @@ fn Comp(count: u32) -> Element { // Because the memo subscribes to `count` by adding it as a dependency, the memo will rerun every time `count` changes. use_effect(use_reactive((&count,), |(count,)| println!("Manually manipulate the dom") )); - todo!() + unimplemented!() } ``` diff --git a/packages/signals/docs/memo.md b/packages/signals/docs/memo.md index e812fdf522..25ef4a185b 100644 --- a/packages/signals/docs/memo.md +++ b/packages/signals/docs/memo.md @@ -142,6 +142,6 @@ fn IncrementButton(mut child_signal: Signal>>) -> Element { // Don't do this: it may cause issues if you drop the child component child_signal.set(Some(memo_owned_by_child)); - todo!() + unimplemented!() } ``` diff --git a/packages/signals/docs/signals.md b/packages/signals/docs/signals.md index bf5f025eac..4ca21bb01f 100644 --- a/packages/signals/docs/signals.md +++ b/packages/signals/docs/signals.md @@ -160,6 +160,6 @@ fn IncrementButton(mut child_signal: Signal>>) -> Element { // Don't do this: it may cause issues if you drop the child component child_signal.set(Some(signal_owned_by_child)); - todo!() + unimplemented!() } ``` diff --git a/packages/ssr/README.md b/packages/ssr/README.md index aeab037858..765726d879 100644 --- a/packages/ssr/README.md +++ b/packages/ssr/README.md @@ -51,7 +51,7 @@ let content = dioxus_ssr::render_element(rsx!{ ```rust, no_run # use dioxus::prelude::*; -# fn app() -> Element { todo!() } +# fn app() -> Element { unimplemented!() } let mut vdom = VirtualDom::new(app); vdom.rebuild_in_place(); @@ -70,7 +70,7 @@ To enable pre-rendering, simply set the pre-rendering flag to true. ```rust, no_run # use dioxus::prelude::*; -# fn App() -> Element { todo!() } +# fn App() -> Element { unimplemented!() } let mut vdom = VirtualDom::new(App); vdom.rebuild_in_place(); From 9d0ae132247453fa6f21462df59312b813368225 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 2 Aug 2024 20:23:41 -0700 Subject: [PATCH 003/139] NO MORE CACHE THRASH PLEASE --- .cargo/config.toml | 9 ++- Cargo.lock | 58 ++++++++------ Cargo.toml | 2 +- packages/cli-config/Cargo.toml | 3 +- packages/cli-config/build.rs | 12 +-- packages/cli-config/src/lib.rs | 117 +++++++++++++++++------------ packages/cli/Cargo.toml | 1 + packages/cli/src/builder/cargo.rs | 11 ++- packages/cli/src/cli/bundle.rs | 1 + packages/cli/src/serve/server.rs | 4 + packages/router/src/history/web.rs | 4 +- 11 files changed, 134 insertions(+), 88 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 464189e013..fb8c098da1 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,5 +1,12 @@ -# All of these variables are used in the `openid_connect_demo` example, they are set here for the CI to work, they are set here because as stated here for now: `https://doc.rust-lang.org/cargo/reference/config.html` the .cargo/config.toml of the inner workspaces are not read when being invoked from the root workspace. +# All of these variables are used in the `openid_connect_demo` example, they are set here for the +# CI to work, they are set here because as stated here for now: +# `https://doc.rust-lang.org/cargo/reference/config.html` +# the .cargo/config.toml of the inner workspaces are not read when being invoked from the root workspace. + [env] DIOXUS_FRONT_ISSUER_URL = "" DIOXUS_FRONT_CLIENT_ID = "" DIOXUS_FRONT_URL = "" + +[profile.dev.package."*"] +opt-level = 3 diff --git a/Cargo.lock b/Cargo.lock index 47254fa1fa..ab9ac0ea6d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1451,6 +1451,18 @@ dependencies = [ "serde", ] +[[package]] +name = "cargo-config2" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d83ce0be8bd1479e5de6202def660e6c7e27e4e0599bffa4fed05bd380ec2ede" +dependencies = [ + "home", + "serde", + "serde_derive", + "toml_edit 0.22.20", +] + [[package]] name = "cargo-generate" version = "0.21.1" @@ -1488,7 +1500,7 @@ dependencies = [ "tempfile", "thiserror", "time", - "toml 0.8.19", + "toml", "walkdir", ] @@ -1522,7 +1534,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad639525b1c67b6a298f378417b060fbc04618bea559482a8484381cce27d965" dependencies = [ "serde", - "toml 0.8.19", + "toml", ] [[package]] @@ -2679,6 +2691,7 @@ dependencies = [ "axum-server", "brotli", "built", + "cargo-config2", "cargo-generate", "cargo_metadata", "cargo_toml", @@ -2732,7 +2745,7 @@ dependencies = [ "thiserror", "tokio", "tokio-stream", - "toml 0.8.19", + "toml", "toml_edit 0.22.20", "tower", "tower-http", @@ -2750,6 +2763,7 @@ name = "dioxus-cli-config" version = "0.6.0-alpha.1" dependencies = [ "built", + "cargo-config2", "cargo_toml", "clap", "dirs", @@ -2758,7 +2772,7 @@ dependencies = [ "serde_json", "tauri-bundler", "tauri-utils", - "toml 0.8.19", + "toml", "tracing", ] @@ -5703,6 +5717,15 @@ dependencies = [ "cfb", ] +[[package]] +name = "infer" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc150e5ce2330295b8616ce0e3f53250e53af31759a9dbedad1621ba29151847" +dependencies = [ + "cfb", +] + [[package]] name = "inotify" version = "0.9.6" @@ -6473,13 +6496,14 @@ name = "manganis-common" version = "0.3.0-alpha.0" dependencies = [ "anyhow", - "base64 0.21.7", + "base64 0.22.1", "built", + "cargo-config2", "home", - "infer 0.11.0", + "infer 0.16.0", "reqwest 0.12.5", "serde", - "toml 0.7.8", + "toml", "tracing", "url", ] @@ -11168,7 +11192,7 @@ dependencies = [ "cfg-expr 0.15.8", "heck 0.5.0", "pkg-config", - "toml 0.8.19", + "toml", "version-compare", ] @@ -11339,7 +11363,7 @@ dependencies = [ "serde_json", "serde_with", "thiserror", - "toml 0.8.19", + "toml", "url", "urlpattern", "walkdir", @@ -11643,18 +11667,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "toml" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit 0.19.15", -] - [[package]] name = "toml" version = "0.8.19" @@ -11684,8 +11696,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ "indexmap 2.3.0", - "serde", - "serde_spanned", "toml_datetime", "winnow 0.5.40", ] @@ -11937,7 +11947,7 @@ dependencies = [ "serde_derive", "serde_json", "termcolor", - "toml 0.8.19", + "toml", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 1bceccc8d4..a2e8ba8373 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -150,10 +150,10 @@ tokio-tungstenite = { version = "0.23.1" } # cli, cli-config dirs = "5.0.1" +cargo-config2 = "0.1.26" criterion = { version = "0.5" } - [profile.dev.package.dioxus-core-macro] opt-level = 3 diff --git a/packages/cli-config/Cargo.toml b/packages/cli-config/Cargo.toml index a2624470ad..ccc0afcbdf 100644 --- a/packages/cli-config/Cargo.toml +++ b/packages/cli-config/Cargo.toml @@ -14,6 +14,7 @@ serde = { version = "1.0.136", features = ["derive"] } serde_json = "1.0.79" toml = { workspace = true, optional = true } cargo_toml = { workspace = true, optional = true } +cargo-config2 = { workspace = true, optional = true } once_cell = "1.18.0" tracing = { workspace = true } @@ -28,7 +29,7 @@ built = { version = "=0.7.3", features = ["git2"] } [features] default = ["read-config"] -cli = ["dep:tauri-bundler", "dep:tauri-utils", "read-from-args", "dep:toml", "dep:cargo_toml", "dep:dirs"] +cli = ["dep:tauri-bundler", "dep:tauri-utils", "read-from-args", "dep:toml", "dep:cargo_toml", "dep:dirs", "dep:cargo-config2"] read-config = [] read-from-args = ["dep:clap"] diff --git a/packages/cli-config/build.rs b/packages/cli-config/build.rs index 4892c32bb1..7186269b08 100644 --- a/packages/cli-config/build.rs +++ b/packages/cli-config/build.rs @@ -4,10 +4,10 @@ fn main() { built::write_built_file().expect("Failed to acquire build-time information"); - println!("cargo:rerun-if-env-changed=DIOXUS_CONFIG"); - let dioxus_config = std::env::var("DIOXUS_CONFIG"); - let built_with_dioxus = dioxus_config.is_ok(); - if cfg!(feature = "read-config") && !built_with_dioxus { - println!("cargo:warning=A library is trying to access the crate's configuration, but the dioxus CLI was not used to build the application. Information about the Dioxus CLI is available at https://dioxuslabs.com/learn/0.5/CLI/installation"); - } + // println!("cargo:rerun-if-env-changed=DIOXUS_CONFIG"); + // let dioxus_config = std::env::var("DIOXUS_CONFIG"); + // let built_with_dioxus = dioxus_config.is_ok(); + // if cfg!(feature = "read-config") && !built_with_dioxus { + // println!("cargo:warning=A library is trying to access the crate's configuration, but the dioxus CLI was not used to build the application. Information about the Dioxus CLI is available at https://dioxuslabs.com/learn/0.5/CLI/installation"); + // } } diff --git a/packages/cli-config/src/lib.rs b/packages/cli-config/src/lib.rs index 91c3f646ad..36b588d76a 100644 --- a/packages/cli-config/src/lib.rs +++ b/packages/cli-config/src/lib.rs @@ -21,26 +21,34 @@ pub mod __private { pub(crate) const CONFIG_ENV: &str = "DIOXUS_CONFIG"; pub(crate) const CONFIG_BASE_PATH_ENV: &str = "DIOXUS_CONFIG_BASE_PATH"; - pub fn save_config(config: &DioxusConfig, cli_version: &str) -> CrateConfigDropGuard { - std::env::set_var(CONFIG_ENV, serde_json::to_string(config).unwrap()); - std::env::set_var( - CONFIG_BASE_PATH_ENV, - config.web.app.base_path.clone().unwrap_or_default(), - ); - std::env::set_var(DIOXUS_CLI_VERSION, cli_version); - CrateConfigDropGuard + pub fn env_args() -> Vec<(&'static str, &'static str)> { + vec![ + (CONFIG_ENV, "DIOXUS_CONFIG"), + (CONFIG_BASE_PATH_ENV, "DIOXUS_CONFIG_BASE_PATH"), + (DIOXUS_CLI_VERSION, "DIOXUS_CLI_VERSION"), + ] } - /// A guard that removes the config from the environment when dropped. - pub struct CrateConfigDropGuard; - - impl Drop for CrateConfigDropGuard { - fn drop(&mut self) { - std::env::remove_var(CONFIG_ENV); - std::env::remove_var(CONFIG_BASE_PATH_ENV); - std::env::remove_var(DIOXUS_CLI_VERSION); - } - } + // pub fn save_config(config: &DioxusConfig, cli_version: &str) -> CrateConfigDropGuard { + // std::env::set_var(CONFIG_ENV, serde_json::to_string(config).unwrap()); + // std::env::set_var( + // CONFIG_BASE_PATH_ENV, + // config.web.app.base_path.clone().unwrap_or_default(), + // ); + // std::env::set_var(DIOXUS_CLI_VERSION, cli_version); + // CrateConfigDropGuard + // } + + // /// A guard that removes the config from the environment when dropped. + // pub struct CrateConfigDropGuard; + + // impl Drop for CrateConfigDropGuard { + // fn drop(&mut self) { + // std::env::remove_var(CONFIG_ENV); + // std::env::remove_var(CONFIG_BASE_PATH_ENV); + // std::env::remove_var(DIOXUS_CLI_VERSION); + // } + // } #[cfg(feature = "read-config")] /// The environment variable that stores the CLIs serve configuration. @@ -65,35 +73,50 @@ impl std::error::Error for DioxusCLINotUsed {} pub static CURRENT_CONFIG: once_cell::sync::Lazy< Result, > = once_cell::sync::Lazy::new(|| { - CURRENT_CONFIG_JSON - .ok_or_else(|| { - tracing::warn!("A library is trying to access the crate's configuration, but the dioxus CLI was not used to build the application."); - DioxusCLINotUsed -}).and_then( - |config| - match serde_json::from_str(config) { - Ok(config) => Ok(config), - Err(err) => { - let mut cli_version = crate::build_info::PKG_VERSION.to_string(); - - if let Some(hash) = crate::build_info::GIT_COMMIT_HASH_SHORT { - let hash = &hash.trim_start_matches('g')[..4]; - cli_version.push_str(&format!("-{hash}")); - } - - let dioxus_version = std::option_env!("DIOXUS_CLI_VERSION").unwrap_or("unknown"); - - tracing::warn!("Failed to parse the CLI config file. This is likely caused by a mismatch between the version of the CLI and the dioxus version.\nCLI version: {cli_version}\nDioxus version: {dioxus_version}\nSerialization error: {err}"); - Err(DioxusCLINotUsed) - } - } -) + std::env::var("DIOXUS_CONFIG") + .ok() + .and_then(|config| serde_json::from_str(&config).ok()) + .ok_or_else(|| { + tracing::warn!("A library is trying to access the crate's configuration, but the dioxus CLI was not used to build the application."); + DioxusCLINotUsed + }) + + // CURRENT_CONFIG_JSON + // .ok_or_else(|| { + // tracing::warn!("A library is trying to access the crate's configuration, but the dioxus CLI was not used to build the application."); + // DioxusCLINotUsed + // }).and_then( + // |config| + // match serde_json::from_str(config) { + // Ok(config) => Ok(config), + // Err(err) => { + // let mut cli_version = crate::build_info::PKG_VERSION.to_string(); + + // if let Some(hash) = crate::build_info::GIT_COMMIT_HASH_SHORT { + // let hash = &hash.trim_start_matches('g')[..4]; + // cli_version.push_str(&format!("-{hash}")); + // } + + // let dioxus_version = std::option_env!("DIOXUS_CLI_VERSION").unwrap_or("unknown"); + + // tracing::warn!("Failed to parse the CLI config file. This is likely caused by a mismatch between the version of the CLI and the dioxus version.\nCLI version: {cli_version}\nDioxus version: {dioxus_version}\nSerialization error: {err}"); + // Err(DioxusCLINotUsed) + // } + // } + // ) }); -#[cfg(feature = "read-config")] -/// The current crate's configuration. -pub const CURRENT_CONFIG_JSON: Option<&str> = std::option_env!("DIOXUS_CONFIG"); +/// Get the "base path" from the Dioxus.toml file +/// +/// This is typically the offset of the asset from its domain +pub fn base_path() -> Option<&'static str> { + todo!() +} -#[cfg(feature = "read-config")] -/// The current crate's configuration. -pub const BASE_PATH: Option<&str> = std::option_env!("DIOXUS_CONFIG_BASE_PATH"); +// #[cfg(feature = "read-config")] +// /// The current crate's configuration. +// pub const CURRENT_CONFIG_JSON: Option<&str> = std::option_env!("DIOXUS_CONFIG"); + +// #[cfg(feature = "read-config")] +// /// The current crate's configuration. +// pub const BASE_PATH: Option<&str> = std::option_env!("DIOXUS_CONFIG_BASE_PATH"); diff --git a/packages/cli/Cargo.toml b/packages/cli/Cargo.toml index 8e55a08742..11df6d7823 100644 --- a/packages/cli/Cargo.toml +++ b/packages/cli/Cargo.toml @@ -45,6 +45,7 @@ console = "0.15.8" ctrlc = "3.2.3" futures-channel = { workspace = true } krates = { version = "0.17.0" } +cargo-config2 = { workspace = true, optional = true } axum = { workspace = true, features = ["ws"] } axum-server = { workspace = true, features = ["tls-rustls"] } diff --git a/packages/cli/src/builder/cargo.rs b/packages/cli/src/builder/cargo.rs index 9bafa456f2..d000242f83 100644 --- a/packages/cli/src/builder/cargo.rs +++ b/packages/cli/src/builder/cargo.rs @@ -16,7 +16,6 @@ use anyhow::Context; use dioxus_cli_config::Platform; use futures_channel::mpsc::UnboundedSender; use manganis_cli_support::AssetManifest; -use manganis_cli_support::ManganisSupportGuard; use std::fs::create_dir_all; use std::path::PathBuf; @@ -109,11 +108,11 @@ impl BuildRequest { let hash = &hash.trim_start_matches('g')[..4]; dioxus_version.push_str(&format!("-{hash}")); } - let _guard = dioxus_cli_config::__private::save_config( - &self.dioxus_crate.dioxus_config, - &dioxus_version, - ); - let _manganis_support = ManganisSupportGuard::default(); + // let _guard = dioxus_cli_config::__private::save_config( + // &self.dioxus_crate.dioxus_config, + // &dioxus_version, + // ); + // let _manganis_support = ManganisSupportGuard::default(); let _asset_guard = AssetConfigDropGuard::new(self.dioxus_crate.dioxus_config.web.app.base_path.as_deref()); diff --git a/packages/cli/src/cli/bundle.rs b/packages/cli/src/cli/bundle.rs index 9b4597a8b9..5e61294ff5 100644 --- a/packages/cli/src/cli/bundle.rs +++ b/packages/cli/src/cli/bundle.rs @@ -15,6 +15,7 @@ use super::*; pub struct Bundle { #[clap(long)] pub package: Option>, + /// The arguments for the dioxus build #[clap(flatten)] pub build_arguments: Build, diff --git a/packages/cli/src/serve/server.rs b/packages/cli/src/serve/server.rs index ab125c7799..7342c25a81 100644 --- a/packages/cli/src/serve/server.rs +++ b/packages/cli/src/serve/server.rs @@ -215,6 +215,10 @@ impl Server { /// Sends hot reloadable changes to all clients. pub async fn send_hotreload(&mut self, reload: HotReloadMsg) { + if !reload.assets.is_empty() { + tracing::debug!("Hot reloading assets {:?}", reload.assets); + } + let msg = DevserverMsg::HotReload(reload); let msg = serde_json::to_string(&msg).unwrap(); diff --git a/packages/router/src/history/web.rs b/packages/router/src/history/web.rs index 0f6811da69..a60dfe7e64 100644 --- a/packages/router/src/history/web.rs +++ b/packages/router/src/history/web.rs @@ -17,9 +17,9 @@ use super::{ fn base_path() -> Option<&'static str> { tracing::trace!( "Using base_path from Dioxus.toml: {:?}", - dioxus_cli_config::BASE_PATH + dioxus_cli_config::base_path() ); - dioxus_cli_config::BASE_PATH + dioxus_cli_config::base_path() } #[allow(clippy::extra_unused_type_parameters)] From 249c51b30a40b9e48de7dfc95a9ceabc82d0ac95 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Tue, 6 Aug 2024 13:00:11 -0700 Subject: [PATCH 004/139] wip: rip out old asset protocol api --- Cargo.lock | 4 +-- packages/desktop/src/protocol.rs | 50 +++++++++++++++--------------- packages/html/src/document/head.rs | 23 ++++++++++++++ packages/html/src/document/mod.rs | 4 ++- packages/html/src/lib.rs | 2 +- 5 files changed, 53 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ab9ac0ea6d..741c53d409 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6498,12 +6498,10 @@ dependencies = [ "anyhow", "base64 0.22.1", "built", - "cargo-config2", - "home", "infer 0.16.0", "reqwest 0.12.5", + "scratch", "serde", - "toml", "tracing", "url", ] diff --git a/packages/desktop/src/protocol.rs b/packages/desktop/src/protocol.rs index 4f93de711d..08deed89e3 100644 --- a/packages/desktop/src/protocol.rs +++ b/packages/desktop/src/protocol.rs @@ -320,30 +320,30 @@ fn get_mime_by_ext(trimmed: &Path) -> &'static str { } } -/// A global that stores the workspace root. Used in [`get_workspace_root_from_cargo`]. -static WORKSPACE_ROOT: OnceLock = OnceLock::new(); +// /// A global that stores the workspace root. Used in [`get_workspace_root_from_cargo`]. +// static WORKSPACE_ROOT: OnceLock = OnceLock::new(); -/// Describes the metadata we need from `cargo metadata`. -#[derive(Deserialize)] -struct CargoMetadata { - workspace_root: PathBuf, -} +// /// Describes the metadata we need from `cargo metadata`. +// #[derive(Deserialize)] +// struct CargoMetadata { +// workspace_root: PathBuf, +// } -/// Get the workspace root using `cargo metadata`. Should not be used in release mode. -pub(crate) fn get_workspace_root_from_cargo() -> PathBuf { - WORKSPACE_ROOT - .get_or_init(|| { - let out = Command::new("cargo") - .args(["metadata", "--format-version", "1", "--no-deps"]) - .output() - .expect("`cargo metadata` failed to run"); - - let out = - String::from_utf8(out.stdout).expect("failed to parse output of `cargo metadata`"); - let metadata = serde_json::from_str::(&out) - .expect("failed to deserialize data from `cargo metadata`"); - - metadata.workspace_root - }) - .to_owned() -} +// /// Get the workspace root using `cargo metadata`. Should not be used in release mode. +// pub(crate) fn get_workspace_root_from_cargo() -> PathBuf { +// WORKSPACE_ROOT +// .get_or_init(|| { +// let out = Command::new("cargo") +// .args(["metadata", "--format-version", "1", "--no-deps"]) +// .output() +// .expect("`cargo metadata` failed to run"); + +// let out = +// String::from_utf8(out.stdout).expect("failed to parse output of `cargo metadata`"); +// let metadata = serde_json::from_str::(&out) +// .expect("failed to deserialize data from `cargo metadata`"); + +// metadata.workspace_root +// }) +// .to_owned() +// } diff --git a/packages/html/src/document/head.rs b/packages/html/src/document/head.rs index a5a6662f96..2787820157 100644 --- a/packages/html/src/document/head.rs +++ b/packages/html/src/document/head.rs @@ -465,6 +465,29 @@ pub fn Link(props: LinkProps) -> Element { VNode::empty() } +/// Render a `` element with a `rel="stylesheet"` attribute by default. +/// +/// # Example +/// +/// ```rust, no_run +/// # use dioxus::prelude::*; +/// fn App() -> Element { +/// rsx! { +/// Stylesheet { +/// href: "https://example.com/styles.css", +/// } +/// } +/// } +/// ``` +#[doc(alias = "")] +#[component] +pub fn Stylesheet(props: LinkProps) -> Element { + Link(LinkProps { + rel: Some("stylesheet".to_string()), + ..props + }) +} + fn get_or_insert_root_context() -> T { match ScopeId::ROOT.has_context::() { Some(context) => context, diff --git a/packages/html/src/document/mod.rs b/packages/html/src/document/mod.rs index 27c05c5509..5cb13fad4e 100644 --- a/packages/html/src/document/mod.rs +++ b/packages/html/src/document/mod.rs @@ -14,7 +14,9 @@ mod eval; pub use eval::*; pub mod head; -pub use head::{Meta, MetaProps, Script, ScriptProps, Style, StyleProps, Title, TitleProps}; +pub use head::{ + Meta, MetaProps, Script, ScriptProps, Style, StyleProps, Stylesheet, Title, TitleProps, +}; fn format_attributes(attributes: &[(&str, String)]) -> String { let mut formatted = String::from("["); diff --git a/packages/html/src/lib.rs b/packages/html/src/lib.rs index 3f1646912f..962a30c4a4 100644 --- a/packages/html/src/lib.rs +++ b/packages/html/src/lib.rs @@ -60,7 +60,7 @@ pub mod prelude { #[cfg(feature = "document")] pub use crate::document::{ self, document, eval, head, Document, Meta, MetaProps, Script, ScriptProps, Style, - StyleProps, Title, TitleProps, UseEval, + StyleProps, Stylesheet, Title, TitleProps, UseEval, }; pub use crate::elements::extensions::*; pub use crate::events::*; From 25faa0724f19ab29e598a19347791088b3c5fc05 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 7 Aug 2024 14:30:03 -0700 Subject: [PATCH 005/139] use dif path --- Cargo.lock | 47 ++++++++++++++++++++++++++++++++++++++ Cargo.toml | 4 ++-- packages/dioxus/src/lib.rs | 10 +++++++- 3 files changed, 58 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 741c53d409..83add80ab1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1239,6 +1239,12 @@ dependencies = [ "cipher", ] +[[package]] +name = "borrow-or-share" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eeab4423108c5d7c744f4d234de88d18d636100093ae04caf4825134b9c3a32" + [[package]] name = "borsh" version = "1.5.1" @@ -3898,6 +3904,17 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "fluent-uri" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d77395429e0ce700a8378be6625660a4aa00ca5dc5cd1527193ebd0946cc9b3" +dependencies = [ + "borrow-or-share", + "ref-cast", + "serde", +] + [[package]] name = "flume" version = "0.11.0" @@ -6463,7 +6480,13 @@ dependencies = [ name = "manganis" version = "0.3.0-alpha.0" dependencies = [ + "core-foundation", + "dirs", + "dunce", + "infer 0.16.0", + "manganis-common", "manganis-macro", + "once_cell", ] [[package]] @@ -6498,6 +6521,7 @@ dependencies = [ "anyhow", "base64 0.22.1", "built", + "fluent-uri", "infer 0.16.0", "reqwest 0.12.5", "scratch", @@ -6510,9 +6534,12 @@ dependencies = [ name = "manganis-macro" version = "0.3.0-alpha.0" dependencies = [ + "base64 0.22.1", + "manganis-cli-support", "manganis-common", "proc-macro2", "quote", + "serde", "serde_json", "syn 2.0.72", "tracing-subscriber", @@ -8482,6 +8509,26 @@ dependencies = [ "thiserror", ] +[[package]] +name = "ref-cast" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf0a6f84d5f1d581da8b41b47ec8600871962f2a528115b542b362d4b744931" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + [[package]] name = "regex" version = "1.10.6" diff --git a/Cargo.toml b/Cargo.toml index a2e8ba8373..1d17950542 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -92,8 +92,8 @@ dioxus-static-site-generation = { path = "packages/static-generation", version = dioxus_server_macro = { path = "packages/server-macro", version = "0.6.0-alpha.1", default-features = false } lazy-js-bundle = { path = "packages/lazy-js-bundle", version = "0.6.0-alpha.1" } -manganis-cli-support = { version = "0.3.0-alpha.0", features = ["html"], path = "../ecosystem-dioxus/manganis/cli-support" } -manganis = { version = "0.3.0-alpha.0", default-features = false, features = ["macro"], path = "../ecosystem-dioxus/manganis" } +manganis-cli-support = { version = "0.3.0-alpha.0", features = ["html"], path = "../ecosystem-dioxus/manganis/packages/cli-support" } +manganis = { version = "0.3.0-alpha.0", default-features = false, features = ["macro"], path = "../ecosystem-dioxus/manganis/packages/manganis" } # manganis-cli-support = { version = "0.3.0-alpha.0", features = ["html"] } # manganis = { version = "0.3.0-alpha.0", default-features = false } warnings = { version = "0.2.0" } diff --git a/packages/dioxus/src/lib.rs b/packages/dioxus/src/lib.rs index f1231745d5..45c0a0dcb8 100644 --- a/packages/dioxus/src/lib.rs +++ b/packages/dioxus/src/lib.rs @@ -125,7 +125,7 @@ pub mod prelude { #[cfg(feature = "asset")] #[cfg_attr(docsrs, doc(cfg(feature = "asset")))] - pub use manganis::{self, classes, mg as asset, ImageAsset, ImageType}; + pub use manganis::{self, self as assets, asset, classes, ImageAsset, ImageType}; } #[cfg(feature = "web")] @@ -159,3 +159,11 @@ pub use dioxus_liveview as liveview; #[cfg(feature = "ssr")] #[cfg_attr(docsrs, doc(cfg(feature = "ssr")))] pub use dioxus_ssr as ssr; + +#[cfg(feature = "asset")] +#[cfg_attr(docsrs, doc(cfg(feature = "asset")))] +pub use manganis as assets; + +#[cfg(feature = "asset")] +#[cfg_attr(docsrs, doc(cfg(feature = "asset")))] +pub use manganis; From 23495f3c1747b2af006befab4bf29d07e910c0b7 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 8 Aug 2024 17:07:31 -0700 Subject: [PATCH 006/139] change lock --- Cargo.lock | 224 +++++++++++++++++++++++++++++------------------------ 1 file changed, 122 insertions(+), 102 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 83add80ab1..9a37ad29c8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -85,7 +85,7 @@ dependencies = [ "once_cell", "serde", "version_check", - "zerocopy 0.7.35", + "zerocopy", ] [[package]] @@ -939,7 +939,7 @@ dependencies = [ "hyper-util", "pin-project-lite", "rustls 0.23.12", - "rustls-pemfile 2.1.2", + "rustls-pemfile 2.1.3", "rustls-pki-types", "tokio", "tokio-rustls 0.26.0", @@ -1123,7 +1123,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "rustc-hash", + "rustc-hash 1.1.0", "shlex", "syn 2.0.72", "which", @@ -1321,9 +1321,9 @@ dependencies = [ [[package]] name = "buffer-redux" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c9f8ddd22e0a12391d1e7ada69ec3b0da1914f1cec39c5cf977143c5b2854f5" +checksum = "4e8acf87c5b9f5897cd3ebb9a327f420e0cae9dd4e5c1d2e36f2c84c571a58f1" dependencies = [ "memchr", ] @@ -1575,9 +1575,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc" +checksum = "504bdec147f2cc13c8b57ed9401fd8a147cc66b67ad5cb241394244f2c947549" dependencies = [ "jobserver", "libc", @@ -2806,7 +2806,7 @@ dependencies = [ "pretty_assertions", "rand 0.8.5", "reqwest 0.12.5", - "rustc-hash", + "rustc-hash 1.1.0", "rustversion", "serde", "slab", @@ -2866,7 +2866,7 @@ dependencies = [ "rand 0.8.5", "reqwest 0.12.5", "rfd", - "rustc-hash", + "rustc-hash 1.1.0", "separator", "serde", "serde_json", @@ -3102,7 +3102,7 @@ dependencies = [ "futures-util", "generational-box", "pretty_env_logger", - "rustc-hash", + "rustc-hash 1.1.0", "serde", "serde_json", "slab", @@ -3241,7 +3241,7 @@ dependencies = [ "parking_lot", "rand 0.8.5", "reqwest 0.12.5", - "rustc-hash", + "rustc-hash 1.1.0", "serde", "simple_logger", "tokio", @@ -3270,7 +3270,7 @@ dependencies = [ "generational-box", "http 1.1.0", "lru 0.12.4", - "rustc-hash", + "rustc-hash 1.1.0", "serde", "serde_json", "thiserror", @@ -3327,7 +3327,7 @@ dependencies = [ "gloo-dialogs", "gloo-timers", "js-sys", - "rustc-hash", + "rustc-hash 1.1.0", "serde", "serde-wasm-bindgen", "serde_json", @@ -3478,9 +3478,9 @@ dependencies = [ [[package]] name = "dunce" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" [[package]] name = "ecdsa" @@ -3896,9 +3896,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.30" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" +checksum = "7f211bbe8e69bbd0cfdea405084f128ae8b4aaa6b0b522fc8f2b009084797920" dependencies = [ "crc32fast", "miniz_oxide", @@ -5270,7 +5270,7 @@ dependencies = [ "new_debug_unreachable", "once_cell", "phf 0.11.2", - "rustc-hash", + "rustc-hash 1.1.0", "triomphe", ] @@ -5511,9 +5511,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab92f4f49ee4fb4f997c784b7a2e0fa70050211e0b6a287f898c3c9785ca956" +checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9" dependencies = [ "bytes", "futures-channel", @@ -5658,9 +5658,9 @@ dependencies = [ [[package]] name = "imagequant" -version = "4.3.1" +version = "4.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09db32417831053bf246bc74fc7c139a05458552d2d98a9f58ff5744d8dea8d3" +checksum = "5fecc99538c9061ee4d88476f6cd704c9f06575a34f0083affdaa1337a331aa7" dependencies = [ "arrayvec", "once_cell", @@ -6281,9 +6281,9 @@ dependencies = [ [[package]] name = "lightningcss" -version = "1.0.0-alpha.57" +version = "1.0.0-alpha.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10bc10261f46b8df263b80e7779d1748b1880488cd951fbb9e096430cead10e6" +checksum = "ec380ca49dc7f6a1cafbdd2de5e587958eac0f67ab26b1e56727fcc60a0c3d4d" dependencies = [ "ahash 0.8.11", "bitflags 2.6.0", @@ -6295,6 +6295,7 @@ dependencies = [ "getrandom 0.2.15", "itertools 0.10.5", "lazy_static", + "lightningcss-derive", "parcel_selectors", "parcel_sourcemap", "paste", @@ -6304,6 +6305,18 @@ dependencies = [ "smallvec", ] +[[package]] +name = "lightningcss-derive" +version = "1.0.0-alpha.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c12744d1279367caed41739ef094c325d53fb0ffcd4f9b84a368796f870252" +dependencies = [ + "convert_case 0.6.0", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "line-col" version = "0.2.1" @@ -6505,7 +6518,7 @@ dependencies = [ "ravif", "rayon", "reqwest 0.12.5", - "rustc-hash", + "rustc-hash 1.1.0", "serde", "serde_json", "swc", @@ -6920,11 +6933,11 @@ dependencies = [ [[package]] name = "normpath" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5831952a9476f2fed74b77d74182fa5ddc4d21c72ec45a333b250e3ed0272804" +checksum = "c8911957c4b1549ac0dc74e30db9c8b0e66ddcd6d7acc33098f4c63a64a6d7ed" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -7175,14 +7188,14 @@ dependencies = [ [[package]] name = "object" -version = "0.36.2" +version = "0.36.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e" +checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9" dependencies = [ "flate2", "memchr", "ruzstd", - "wasmparser 0.214.0", + "wasmparser 0.215.0", ] [[package]] @@ -7366,9 +7379,9 @@ dependencies = [ [[package]] name = "parcel_selectors" -version = "0.26.5" +version = "0.26.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce9c47a67c66fee4a5a42756f9784d92941bd0ab2b653539a9e90521a44b66f0" +checksum = "512215cb1d3814e276ace4ec2dbc2cac16726ea3fcac20c22ae1197e16fdd72d" dependencies = [ "bitflags 2.6.0", "cssparser 0.33.0", @@ -7945,11 +7958,11 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.18" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee4364d9f3b902ef14fab8a1ddffb783a1cb6b4bba3bfc1fa3922732c7de97f" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" dependencies = [ - "zerocopy 0.6.6", + "zerocopy", ] [[package]] @@ -8216,16 +8229,17 @@ dependencies = [ [[package]] name = "quinn" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4ceeeeabace7857413798eb1ffa1e9c905a9946a57d81fb69b4b71c4d8eb3ad" +checksum = "b22d8e7369034b9a7132bc2008cac12f2013c8132b45e0554e6e20e2617f2156" dependencies = [ "bytes", "pin-project-lite", "quinn-proto", "quinn-udp", - "rustc-hash", + "rustc-hash 2.0.0", "rustls 0.23.12", + "socket2 0.5.7", "thiserror", "tokio", "tracing", @@ -8233,14 +8247,14 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.3" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddf517c03a109db8100448a4be38d498df8a210a99fe0e1b9eaf39e78c640efe" +checksum = "ba92fb39ec7ad06ca2582c0ca834dfeadcaf06ddfc8e635c80aa7e1c05315fdd" dependencies = [ "bytes", "rand 0.8.5", "ring", - "rustc-hash", + "rustc-hash 2.0.0", "rustls 0.23.12", "slab", "thiserror", @@ -8257,6 +8271,7 @@ dependencies = [ "libc", "once_cell", "socket2 0.5.7", + "tracing", "windows-sys 0.52.0", ] @@ -8583,7 +8598,7 @@ dependencies = [ "cvt", "fs_at", "libc", - "normpath 1.2.0", + "normpath 1.3.0", "windows-sys 0.52.0", ] @@ -8673,7 +8688,7 @@ dependencies = [ "pin-project-lite", "quinn", "rustls 0.23.12", - "rustls-pemfile 2.1.2", + "rustls-pemfile 2.1.3", "rustls-pki-types", "serde", "serde_json", @@ -8729,9 +8744,9 @@ dependencies = [ [[package]] name = "rgb" -version = "0.8.45" +version = "0.8.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade4539f42266ded9e755c605bdddf546242b2c961b03b06a7375260788a0523" +checksum = "0f86ae463694029097b846d8f99fd5536740602ae00022c0c50c5600720b2f71" dependencies = [ "bytemuck", ] @@ -8931,6 +8946,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc-hash" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" + [[package]] name = "rustc_version" version = "0.2.3" @@ -9011,7 +9032,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a88d6d420651b496bdd98684116959239430022a115c1240e6c3993be0b15fba" dependencies = [ "openssl-probe", - "rustls-pemfile 2.1.2", + "rustls-pemfile 2.1.3", "rustls-pki-types", "schannel", "security-framework", @@ -9028,9 +9049,9 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "2.1.2" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" +checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" dependencies = [ "base64 0.22.1", "rustls-pki-types", @@ -9038,9 +9059,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" +checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" [[package]] name = "rustls-webpki" @@ -9658,7 +9679,7 @@ checksum = "f20798defa0e9d4eff9ca451c7f84774c7378a9c3b5a40112cfa2b3eadb97ae2" dependencies = [ "lru 0.12.4", "once_cell", - "rustc-hash", + "rustc-hash 1.1.0", ] [[package]] @@ -9762,7 +9783,26 @@ dependencies = [ "data-encoding", "debugid", "if_chain", - "rustc-hash", + "rustc-hash 1.1.0", + "rustc_version 0.2.3", + "serde", + "serde_json", + "unicode-id-start", + "url", +] + +[[package]] +name = "sourcemap" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dab08a862c70980b8e23698b507e272317ae52a608a164a844111f5372374f1f" +dependencies = [ + "base64-simd", + "bitvec", + "data-encoding", + "debugid", + "if_chain", + "rustc-hash 1.1.0", "rustc_version 0.2.3", "serde", "serde_json", @@ -10232,10 +10272,10 @@ dependencies = [ "parking_lot", "pathdiff", "regex", - "rustc-hash", + "rustc-hash 1.1.0", "serde", "serde_json", - "sourcemap", + "sourcemap 8.0.1", "swc_atoms", "swc_cached", "swc_common", @@ -10274,7 +10314,7 @@ dependencies = [ "bumpalo", "hashbrown 0.14.5", "ptr_meta", - "rustc-hash", + "rustc-hash 1.1.0", "triomphe", ] @@ -10286,7 +10326,7 @@ checksum = "bb6567e4e67485b3e7662b486f1565bdae54bd5b9d6b16b2ba1a9babb1e42125" dependencies = [ "hstr", "once_cell", - "rustc-hash", + "rustc-hash 1.1.0", "serde", ] @@ -10320,10 +10360,10 @@ dependencies = [ "num-bigint", "once_cell", "parking_lot", - "rustc-hash", + "rustc-hash 1.1.0", "serde", "siphasher", - "sourcemap", + "sourcemap 8.0.1", "swc_allocator", "swc_atoms", "swc_eq_ignore_macros", @@ -10343,10 +10383,10 @@ dependencies = [ "base64 0.21.7", "once_cell", "pathdiff", - "rustc-hash", + "rustc-hash 1.1.0", "serde", "serde_json", - "sourcemap", + "sourcemap 8.0.1", "swc_allocator", "swc_atoms", "swc_common", @@ -10361,15 +10401,15 @@ dependencies = [ [[package]] name = "swc_config" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84b67e115ab136fe0eb03558bb0508ca7782eeb446a96d165508c48617e3fd94" +checksum = "4740e53eaf68b101203c1df0937d5161a29f3c13bceed0836ddfe245b72dd000" dependencies = [ "anyhow", "indexmap 2.3.0", "serde", "serde_json", - "sourcemap", + "sourcemap 9.0.0", "swc_cached", "swc_config_macro", ] @@ -10406,15 +10446,15 @@ dependencies = [ [[package]] name = "swc_ecma_codegen" -version = "0.154.4" +version = "0.154.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7badcda2c45056495ed94b957884099cb000470ae7901ba68db2e7fd48414a4b" +checksum = "530226ed8f294908954687c7bf623a020ed1bc080a54a33f66979c5f4b5798a1" dependencies = [ "memchr", "num-bigint", "once_cell", "serde", - "sourcemap", + "sourcemap 8.0.1", "swc_allocator", "swc_atoms", "swc_common", @@ -10700,7 +10740,7 @@ dependencies = [ "phf 0.11.2", "radix_fmt", "regex", - "rustc-hash", + "rustc-hash 1.1.0", "ryu-js", "serde", "serde_json", @@ -10753,7 +10793,7 @@ dependencies = [ "indexmap 2.3.0", "once_cell", "preset_env_base", - "rustc-hash", + "rustc-hash 1.1.0", "semver 1.0.23", "serde", "serde_json", @@ -10798,7 +10838,7 @@ dependencies = [ "indexmap 2.3.0", "once_cell", "phf 0.11.2", - "rustc-hash", + "rustc-hash 1.1.0", "serde", "smallvec", "swc_atoms", @@ -10909,7 +10949,7 @@ dependencies = [ "indexmap 2.3.0", "once_cell", "petgraph", - "rustc-hash", + "rustc-hash 1.1.0", "serde_json", "swc_atoms", "swc_common", @@ -10925,12 +10965,12 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_proposal" -version = "0.177.1" +version = "0.177.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2d84d062b05ae89982a76ff47881a5e15bbd02e9b3c68dc14a3f5eacf48abca" +checksum = "91dc2013d4cb5e25bf050fea84ea09aca12ada788f8debb47f2fdbff07fae651" dependencies = [ "either", - "rustc-hash", + "rustc-hash 1.1.0", "serde", "smallvec", "swc_atoms", @@ -10992,7 +11032,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38f583b8db3cb9848537bd6f91c75398407e64332aee982337fe62ea4dec7e7b" dependencies = [ "indexmap 2.3.0", - "rustc-hash", + "rustc-hash 1.1.0", "swc_atoms", "swc_common", "swc_ecma_ast", @@ -11011,7 +11051,7 @@ dependencies = [ "indexmap 2.3.0", "num_cpus", "once_cell", - "rustc-hash", + "rustc-hash 1.1.0", "ryu-js", "swc_atoms", "swc_common", @@ -11068,7 +11108,7 @@ checksum = "f3f854cf8efc290aa927d31dab98b42011ff2341fecb2b27fdc817ef7b30ef3b" dependencies = [ "indexmap 2.3.0", "petgraph", - "rustc-hash", + "rustc-hash 1.1.0", "swc_common", ] @@ -11122,7 +11162,7 @@ checksum = "eda3e80e1ad638d3575bc07745a914af13dcb02215098659f864731078271f2c" dependencies = [ "better_scoped_tls", "once_cell", - "rustc-hash", + "rustc-hash 1.1.0", "serde", "serde_json", ] @@ -12710,9 +12750,9 @@ checksum = "449167e2832691a1bff24cde28d2804e90e09586a448c8e76984792c44334a6b" [[package]] name = "wasmparser" -version = "0.214.0" +version = "0.215.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5309c1090e3e84dad0d382f42064e9933fdaedb87e468cc239f0eabea73ddcb6" +checksum = "53fbde0881f24199b81cf49b6ff8f9c145ac8eb1b7fc439adb5c099734f7d90e" dependencies = [ "bitflags 2.6.0", ] @@ -13477,34 +13517,14 @@ dependencies = [ "zvariant", ] -[[package]] -name = "zerocopy" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854e949ac82d619ee9a14c66a1b674ac730422372ccb759ce0c39cabcf2bf8e6" -dependencies = [ - "byteorder", - "zerocopy-derive 0.6.6", -] - [[package]] name = "zerocopy" version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ - "zerocopy-derive 0.7.35", -] - -[[package]] -name = "zerocopy-derive" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "125139de3f6b9d625c39e2efdd73d41bdac468ccd556556440e322be0e1bbd91" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.72", + "byteorder", + "zerocopy-derive", ] [[package]] From 26046d5bd69c2597f5ce147f1cfbb35f3c7e677e Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 8 Aug 2024 17:16:34 -0700 Subject: [PATCH 007/139] feat: mobile hotreloading + ios asset configuration --- Cargo.lock | 68 +++----------- Cargo.toml | 2 +- examples/custom_assets.rs | 22 +++-- packages/core/src/diff/node.rs | 2 +- packages/desktop/Cargo.toml | 8 +- packages/desktop/src/app.rs | 18 +--- packages/desktop/src/ipc.rs | 7 +- packages/desktop/src/launch.rs | 7 +- packages/desktop/src/protocol.rs | 117 +++++++++---------------- packages/hot-reload/src/ws_receiver.rs | 9 +- 10 files changed, 87 insertions(+), 173 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e51350699f..3a959d04bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11284,17 +11284,18 @@ dependencies = [ [[package]] name = "tao" -version = "0.26.2" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69ebbccb78deb5a36744c079eea2981b4a48ecbbe6b1b2ffbaa528bea3f5e5db" +checksum = "ea538df05fbc2dcbbd740ba0cfe8607688535f4798d213cbbfa13ce494f3451f" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", "cocoa", "core-foundation", "core-graphics", "crossbeam-channel", "dispatch", "dlopen2", + "dpi", "gdkwayland-sys", "gdkx11-sys", "gtk", @@ -11315,7 +11316,8 @@ dependencies = [ "tao-macros", "unicode-segmentation", "url", - "windows 0.54.0", + "windows", + "windows-core 0.57.0", "windows-version", "x11-dl", ] @@ -12852,10 +12854,10 @@ checksum = "6516cfa64c6b3212686080eeec378e662c2af54bb2a5b2a22749673f5cb2226f" dependencies = [ "webview2-com-macros", "webview2-com-sys", - "windows 0.57.0", + "windows", "windows-core 0.57.0", - "windows-implement 0.57.0", - "windows-interface 0.57.0", + "windows-implement", + "windows-interface", ] [[package]] @@ -12876,7 +12878,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c76d5b77320ff155660be1df3e6588bc85c75f1a9feef938cc4dc4dd60d1d7cf" dependencies = [ "thiserror", - "windows 0.57.0", + "windows", "windows-core 0.57.0", ] @@ -12939,18 +12941,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows" -version = "0.54.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49" -dependencies = [ - "windows-core 0.54.0", - "windows-implement 0.53.0", - "windows-interface 0.53.0", - "windows-targets 0.52.6", -] - [[package]] name = "windows" version = "0.57.0" @@ -12970,39 +12960,18 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-core" -version = "0.54.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65" -dependencies = [ - "windows-result", - "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-implement", + "windows-interface", "windows-result", "windows-targets 0.52.6", ] -[[package]] -name = "windows-implement" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942ac266be9249c84ca862f0a164a39533dc2f6f33dc98ec89c8da99b82ea0bd" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.72", -] - [[package]] name = "windows-implement" version = "0.57.0" @@ -13014,17 +12983,6 @@ dependencies = [ "syn 2.0.72", ] -[[package]] -name = "windows-interface" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da33557140a288fae4e1d5f8873aaf9eb6613a9cf82c3e070223ff177f598b60" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.72", -] - [[package]] name = "windows-interface" version = "0.57.0" @@ -13352,7 +13310,7 @@ dependencies = [ "webkit2gtk", "webkit2gtk-sys", "webview2-com", - "windows 0.57.0", + "windows", "windows-core 0.57.0", "windows-version", "x11-dl", diff --git a/Cargo.toml b/Cargo.toml index 340fd5efb5..a6af8d4e4f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -229,7 +229,7 @@ fullstack = ["dioxus/fullstack"] axum = ["dioxus/axum"] server = ["dioxus/axum"] web = ["dioxus/web"] -collect-assets = ["dep:manganis"] +# collect-assets = ["dep:manganis"] http = ["dep:reqwest", "dep:http-range"] [[example]] diff --git a/examples/custom_assets.rs b/examples/custom_assets.rs index 6c927d21a4..271a52e4bb 100644 --- a/examples/custom_assets.rs +++ b/examples/custom_assets.rs @@ -1,16 +1,22 @@ //! A simple example on how to use assets loading from the filesystem. //! -//! If the feature "collect-assets" is enabled, the assets will be collected via the dioxus CLI and embedded into the -//! final bundle. This lets you do various useful things like minify, compress, and optimize your assets. +//! Dioxus provides an asset!() macro which properly handles asset loading and bundling for you. +//! For bundling, asset!() must be paired with a tool that handles mangansis-link sections. The dioxus-cli +//! handles this for you, but this means you can't just simply `cargo build --release` to build and +//! distribute your app. //! -//! We can still use assets without the CLI middleware, but generally larger apps will benefit from it. +//! You can run this example with `cargo run --example assets` or `dx serve --example assets`. +//! When manganis is not active, the asset!() macro will fallback to the path of the asset on +//! your filesystem. use dioxus::prelude::*; -#[cfg(not(feature = "collect-assets"))] -static ASSET_PATH: &str = "examples/assets/logo.png"; - -#[cfg(feature = "collect-assets")] +/// asset!() will mark this asset as a dependency of the app without actually including it in the +/// generated code. This is better than include_str!() or include_bytes!() since it works +/// for web apps as well as native and mobile apps. +/// +/// When used with web apps, manganis will detect the import of the image, optimize it, and put it +/// in the output dist folder in the right location, ensuring no two images have the same name. static ASSET_PATH: &str = asset!("examples/assets/logo.png".format(ImageType::Avif)); fn main() { @@ -21,7 +27,7 @@ fn app() -> Element { rsx! { div { h1 { "This should show an image:" } - img { src: ASSET_PATH.to_string() } + img { src: ASSET_PATH } } } } diff --git a/packages/core/src/diff/node.rs b/packages/core/src/diff/node.rs index 06925f5a83..03d34e17d7 100644 --- a/packages/core/src/diff/node.rs +++ b/packages/core/src/diff/node.rs @@ -79,7 +79,7 @@ impl VNode { match (old_node, new_node) { (Text(old), Text(new)) => { // Diffing text is just a side effect, if we are diffing suspended nodes and are not outputting mutations, we can skip it - if let Some(to) = to{ + if let Some(to) = to { let mount = &dom.mounts[mount.0]; self.diff_vtext(to, mount, idx, old, new) } diff --git a/packages/desktop/Cargo.toml b/packages/desktop/Cargo.toml index 5153729f14..fcbdab5341 100644 --- a/packages/desktop/Cargo.toml +++ b/packages/desktop/Cargo.toml @@ -51,7 +51,7 @@ dioxus-hooks = { workspace = true } futures-util = { workspace = true } urlencoding = "2.1.2" async-trait = "0.1.68" -tao = { version = "0.26.1", features = ["rwh_05"] } +tao = { version = "0.28.1", features = ["rwh_05"] } [target.'cfg(unix)'.dependencies] @@ -63,9 +63,6 @@ rfd = { version = "0.14", default-features = false, features = ["xdg-portal", "t muda = "0.11.3" -[target.'cfg(target_os = "ios")'.dependencies] -objc = "0.2.7" -objc_id = "0.1.1" # use rustls on android [target.'cfg(target_os = "android")'.dependencies] @@ -75,10 +72,11 @@ tokio-tungstenite = { workspace = true, optional = true, features = ["rustls"]} [target.'cfg(not(target_os = "android"))'.dependencies] tokio-tungstenite = { workspace = true, optional = true, features = ["native-tls"]} -[target.'cfg(target_os = "macos")'.dependencies] +[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies] cocoa = "0.25" core-foundation = "0.9.3" objc = "0.2.7" +objc_id = "0.1.1" [features] default = ["tokio_runtime", "wry/objc-exception", "hot-reload", "devtools"] diff --git a/packages/desktop/src/app.rs b/packages/desktop/src/app.rs index 1c3da6c024..5247aa520e 100644 --- a/packages/desktop/src/app.rs +++ b/packages/desktop/src/app.rs @@ -87,12 +87,7 @@ impl App { app.set_menubar_receiver(); // Allow hotreloading to work - but only in debug mode - #[cfg(all( - feature = "hot-reload", - debug_assertions, - not(target_os = "android"), - not(target_os = "ios") - ))] + #[cfg(all(feature = "hot-reload", debug_assertions))] app.connect_hotreload(); #[cfg(debug_assertions)] @@ -141,12 +136,7 @@ impl App { } } - #[cfg(all( - feature = "hot-reload", - debug_assertions, - not(target_os = "android"), - not(target_os = "ios") - ))] + #[cfg(all(feature = "hot-reload", debug_assertions,))] pub fn connect_hotreload(&self) { let proxy = self.shared.proxy.clone(); @@ -329,8 +319,8 @@ impl App { #[cfg(all( feature = "hot-reload", debug_assertions, - not(target_os = "android"), - not(target_os = "ios") + // not(target_os = "android"), + // not(target_os = "ios") ))] pub fn handle_hot_reload_msg(&mut self, msg: dioxus_hot_reload::DevserverMsg) { use dioxus_hot_reload::DevserverMsg; diff --git a/packages/desktop/src/ipc.rs b/packages/desktop/src/ipc.rs index dcf7792a11..9fba684e11 100644 --- a/packages/desktop/src/ipc.rs +++ b/packages/desktop/src/ipc.rs @@ -18,12 +18,7 @@ pub enum UserWindowEvent { Ipc { id: WindowId, msg: IpcMessage }, /// Handle a hotreload event, basically telling us to update our templates - #[cfg(all( - feature = "hot-reload", - debug_assertions, - not(target_os = "android"), - not(target_os = "ios") - ))] + #[cfg(all(feature = "hot-reload", debug_assertions))] HotReloadEvent(dioxus_hot_reload::DevserverMsg), /// Create a new window diff --git a/packages/desktop/src/launch.rs b/packages/desktop/src/launch.rs index 79c39399f2..a93aa356d7 100644 --- a/packages/desktop/src/launch.rs +++ b/packages/desktop/src/launch.rs @@ -41,12 +41,7 @@ pub fn launch_virtual_dom_blocking(virtual_dom: VirtualDom, desktop_config: Conf #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] UserWindowEvent::MudaMenuEvent(evnt) => app.handle_menu_event(evnt), - #[cfg(all( - feature = "hot-reload", - debug_assertions, - not(target_os = "android"), - not(target_os = "ios") - ))] + #[cfg(all(feature = "hot-reload", debug_assertions))] UserWindowEvent::HotReloadEvent(msg) => app.handle_hot_reload_msg(msg), UserWindowEvent::Ipc { id, msg } => match msg.method() { diff --git a/packages/desktop/src/protocol.rs b/packages/desktop/src/protocol.rs index 08deed89e3..85b8e23607 100644 --- a/packages/desktop/src/protocol.rs +++ b/packages/desktop/src/protocol.rs @@ -111,33 +111,40 @@ fn assets_head() -> Option { } } +fn running_in_dev_mode() -> bool { + // If running under cargo, there's no bundle! + // There might be a smarter/more resilient way of doing this + std::env::var_os("CARGO").is_some() +} + fn resolve_resource(path: &Path) -> PathBuf { let mut base_path = get_asset_root_or_default(); - // if running_in_dev_mode() { - // base_path.push(path); - - // // Special handler for Manganis filesystem fallback. - // // We need this since Manganis provides assets from workspace root. - // if !base_path.exists() { - // let workspace_root = get_workspace_root_from_cargo(); - // let asset_path = workspace_root.join(path); - // return asset_path; - // } - // } else { - // let mut resource_path = PathBuf::new(); - // for component in path.components() { - // // Tauri-bundle inserts special path segments for abnormal component paths - // match component { - // Component::Prefix(_) => {} - // Component::RootDir => resource_path.push("_root_"), - // Component::CurDir => {} - // Component::ParentDir => resource_path.push("_up_"), - // Component::Normal(p) => resource_path.push(p), - // } - // } - // base_path.push(resource_path); - // } + // Even in dev mode, mobile needs to resolve the asset path from the bundle + if running_in_dev_mode() || cfg!(any(target_os = "ios", target_os = "android")) { + base_path.push(path); + // Special handler for Manganis filesystem fallback. + // We need this since Manganis provides assets from workspace root. + if !base_path.exists() { + let workspace_root = get_workspace_root_from_cargo(); + let asset_path = workspace_root.join(path); + return asset_path; + } + } else { + let mut resource_path = PathBuf::new(); + for component in path.components() { + // Tauri-bundle inserts special path segments for abnormal component paths + match component { + Component::Prefix(_) => {} + Component::RootDir => resource_path.push("_root_"), + Component::CurDir => {} + Component::ParentDir => resource_path.push("_up_"), + Component::Normal(p) => resource_path.push(p), + } + } + base_path.push(resource_path); + } + base_path } @@ -249,40 +256,30 @@ fn get_asset_root_or_default() -> PathBuf { get_asset_root().unwrap_or_else(|| std::env::current_dir().unwrap()) } -// fn running_in_dev_mode() -> bool { -// // If running under cargo, there's no bundle! -// // There might be a smarter/more resilient way of doing this -// std::env::var_os("CARGO").is_some() -// } - /// Get the asset directory, following tauri/cargo-bundles directory discovery approach /// /// Currently supports: /// - [x] macOS -/// - [ ] Windows +/// - [x] iOS /// - [ ] Linux (rpm) /// - [ ] Linux (deb) -/// - [ ] iOS +/// - [ ] Windows /// - [ ] Android #[allow(unreachable_code)] fn get_asset_root() -> Option { - // if running_in_dev_mode() { - // return dioxus_cli_config::CURRENT_CONFIG - // .as_ref() - // .map(|c| c.application.out_dir.clone()) - // .ok(); - // } - - #[cfg(target_os = "macos")] + // Use the prescence of the bundle to determine if we're in dev mode + // todo: for other platforms, we should check their bundles too. This currently only works for macOS and iOS + #[cfg(any(target_os = "macos", target_os = "ios"))] { - let bundle = core_foundation::bundle::CFBundle::main_bundle(); - let bundle_path = bundle.path()?; - let resources_path = bundle.resources_path()?; - let absolute_resources_root = bundle_path.join(resources_path); - return dunce::canonicalize(absolute_resources_root).ok(); + if let Some(resources) = core_foundation::bundle::CFBundle::main_bundle().resources_path() { + return dunce::canonicalize(resources).ok(); + } } - None + dioxus_cli_config::CURRENT_CONFIG + .as_ref() + .map(|c| c.application.out_dir.clone()) + .ok() } /// Get the mime type from a path-like string @@ -319,31 +316,3 @@ fn get_mime_by_ext(trimmed: &Path) -> &'static str { None => "application/octet-stream", } } - -// /// A global that stores the workspace root. Used in [`get_workspace_root_from_cargo`]. -// static WORKSPACE_ROOT: OnceLock = OnceLock::new(); - -// /// Describes the metadata we need from `cargo metadata`. -// #[derive(Deserialize)] -// struct CargoMetadata { -// workspace_root: PathBuf, -// } - -// /// Get the workspace root using `cargo metadata`. Should not be used in release mode. -// pub(crate) fn get_workspace_root_from_cargo() -> PathBuf { -// WORKSPACE_ROOT -// .get_or_init(|| { -// let out = Command::new("cargo") -// .args(["metadata", "--format-version", "1", "--no-deps"]) -// .output() -// .expect("`cargo metadata` failed to run"); - -// let out = -// String::from_utf8(out.stdout).expect("failed to parse output of `cargo metadata`"); -// let metadata = serde_json::from_str::(&out) -// .expect("failed to deserialize data from `cargo metadata`"); - -// metadata.workspace_root -// }) -// .to_owned() -// } diff --git a/packages/hot-reload/src/ws_receiver.rs b/packages/hot-reload/src/ws_receiver.rs index 2e64da5c4d..a13b674fa5 100644 --- a/packages/hot-reload/src/ws_receiver.rs +++ b/packages/hot-reload/src/ws_receiver.rs @@ -30,14 +30,17 @@ pub struct NativeReceiver { impl NativeReceiver { /// Connect to the devserver async fn create(url: String) -> TtResult { - let (socket, _ws) = tokio_tungstenite::connect_async(&url).await?; + let (socket, _ws) = tokio_tungstenite::connect_async(&url).await.unwrap(); Ok(Self { socket }) } /// Connect to the devserver with an address from the CLI. Returns None if the current application was not run with the CLI pub async fn create_from_cli() -> Option> { - let cli_args = dioxus_cli_config::RuntimeCLIArguments::from_cli()?; - let addr = cli_args.cli_address(); + // todo: allow external configuration of this address for use by mobile when launching + // from the ios-deploy tooling. This could be stored in a config file= that gets + // uploaded to the device. + let addr = + dioxus_cli_config::RuntimeCLIArguments::from_cli().map(|args| args.cli_address())?; Some(Self::create(format!("ws://{addr}/_dioxus")).await) } From 340102c3f20f9f5e76c7f436b748e22efb08cb7a Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 16 Aug 2024 22:11:16 -0700 Subject: [PATCH 008/139] lockfile... --- Cargo.lock | 186 ++++++++++++++++++++++++----------------------------- 1 file changed, 83 insertions(+), 103 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3a959d04bc..90f615db22 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6537,7 +6537,6 @@ dependencies = [ "built", "fluent-uri", "infer 0.16.0", - "reqwest 0.12.5", "scratch", "serde", "tracing", @@ -9773,25 +9772,6 @@ dependencies = [ "system-deps", ] -[[package]] -name = "sourcemap" -version = "8.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "208d40b9e8cad9f93613778ea295ed8f3c2b1824217c6cfc7219d3f6f45b96d4" -dependencies = [ - "base64-simd", - "bitvec", - "data-encoding", - "debugid", - "if_chain", - "rustc-hash 1.1.0", - "rustc_version 0.2.3", - "serde", - "serde_json", - "unicode-id-start", - "url", -] - [[package]] name = "sourcemap" version = "9.0.0" @@ -10258,9 +10238,9 @@ dependencies = [ [[package]] name = "swc" -version = "0.282.3" +version = "0.283.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f76ba75c4eaace11f3b1f22f31fbba74bd5983bf7581caef939c2c7aa1af7f4f" +checksum = "9cb7fe4bd6a604528819c84fc9acc5d5ec1b3bd0d11d13ee5d8a77d49ff678fa" dependencies = [ "anyhow", "base64 0.21.7", @@ -10276,7 +10256,7 @@ dependencies = [ "rustc-hash 1.1.0", "serde", "serde_json", - "sourcemap 8.0.1", + "sourcemap", "swc_atoms", "swc_cached", "swc_common", @@ -10347,9 +10327,9 @@ dependencies = [ [[package]] name = "swc_common" -version = "0.36.3" +version = "0.37.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "457fb92efa9f0c849d6bc4e86561982d464176bc3df96bb22baed5e98309e090" +checksum = "c4d6c716bb706926e22edc992565c98a8f00c0cfa983e97f525f473f9ce2f93f" dependencies = [ "ahash 0.8.11", "ast_node", @@ -10364,7 +10344,7 @@ dependencies = [ "rustc-hash 1.1.0", "serde", "siphasher", - "sourcemap 8.0.1", + "sourcemap", "swc_allocator", "swc_atoms", "swc_eq_ignore_macros", @@ -10376,9 +10356,9 @@ dependencies = [ [[package]] name = "swc_compiler_base" -version = "0.15.2" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd217a48b2061a6fbb602450f347abed706246464f5a5ac24054502f2ab8923" +checksum = "c9b47fac27c2e84e033bdea294ac12f575322c81c9d5d44f423e3f11ff239a86" dependencies = [ "anyhow", "base64 0.21.7", @@ -10387,7 +10367,7 @@ dependencies = [ "rustc-hash 1.1.0", "serde", "serde_json", - "sourcemap 8.0.1", + "sourcemap", "swc_allocator", "swc_atoms", "swc_common", @@ -10410,7 +10390,7 @@ dependencies = [ "indexmap 2.3.0", "serde", "serde_json", - "sourcemap 9.0.0", + "sourcemap", "swc_cached", "swc_config_macro", ] @@ -10429,9 +10409,9 @@ dependencies = [ [[package]] name = "swc_ecma_ast" -version = "0.117.4" +version = "0.118.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5da2f0310e8cd84b8c803095e75b2cbca872c71fc7f7404d4c9c8117d894960" +checksum = "ed6c1b94abbaf080a4e4ae47101a83d4eedef90d733dd98e32b361356d3f5e4b" dependencies = [ "bitflags 2.6.0", "is-macro", @@ -10447,15 +10427,15 @@ dependencies = [ [[package]] name = "swc_ecma_codegen" -version = "0.154.5" +version = "0.155.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "530226ed8f294908954687c7bf623a020ed1bc080a54a33f66979c5f4b5798a1" +checksum = "644514f303dcad13f7d1c244b50af798e0549f6eb8c53d64dd2ba9824266c868" dependencies = [ "memchr", "num-bigint", "once_cell", "serde", - "sourcemap 8.0.1", + "sourcemap", "swc_allocator", "swc_atoms", "swc_common", @@ -10478,9 +10458,9 @@ dependencies = [ [[package]] name = "swc_ecma_compat_bugfixes" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0729253b3f14e53fe300a7bb701144e319f539d1bcb1839eabab17a93d9fd6b2" +checksum = "b9f9cac39f19d6509db921f4b75934aaa64fc84b599416e5c1fcaed1c313132f" dependencies = [ "swc_atoms", "swc_common", @@ -10495,9 +10475,9 @@ dependencies = [ [[package]] name = "swc_ecma_compat_common" -version = "0.10.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4cc5d93c0517aba5a5cefed6770970a270960f9f3881c3234257244f29f9d81" +checksum = "f9acdf402b36f8e83084b10e119d7ba9d07e5229ef39e1343f147db816c7b73e" dependencies = [ "swc_common", "swc_ecma_ast", @@ -10508,9 +10488,9 @@ dependencies = [ [[package]] name = "swc_ecma_compat_es2015" -version = "0.10.2" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d705c2e9fa6a7024d1771fa6f8203f28762fbe0995db919a2f5a6cabd9c6b544" +checksum = "bd07cbb52c1ac41115c9ddd5a4d046a7388008bd950b61a48df7f7f490f19827" dependencies = [ "arrayvec", "indexmap 2.3.0", @@ -10534,9 +10514,9 @@ dependencies = [ [[package]] name = "swc_ecma_compat_es2016" -version = "0.10.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00a32d58418a36a2b41462197c5c4334ed24b21c73be31f2bd09203c0bb962db" +checksum = "209e347cdc3fb56632a1d882f981f3448f5f529c16d8da9d770207fffda4a8f6" dependencies = [ "swc_atoms", "swc_common", @@ -10551,9 +10531,9 @@ dependencies = [ [[package]] name = "swc_ecma_compat_es2017" -version = "0.10.2" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f7e60ec1d6a742de6ca35ddff7bc8a01e99ed53d1f68c352d4ea9a494d9b4d" +checksum = "8a564f1b5e852a0ac656626ba689d49dd2751ba5b980903154aebc971729959d" dependencies = [ "serde", "swc_atoms", @@ -10569,9 +10549,9 @@ dependencies = [ [[package]] name = "swc_ecma_compat_es2018" -version = "0.10.2" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9623c0aa17f9ec0bded894089b73aa3056f5205a0bf715955fbf36e6f63ae0f9" +checksum = "f577f098e7c3738ade709caadb17c9f3bd911ea2ee6cfacca561d12addcc5761" dependencies = [ "serde", "swc_atoms", @@ -10588,9 +10568,9 @@ dependencies = [ [[package]] name = "swc_ecma_compat_es2019" -version = "0.10.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8be9ff518dbca4cb96d3eebcc7c2fb23b73c5d0b5cfb81237ebab92f423a359" +checksum = "f9d52253dc2f83a3fca526c387c33e4ff9a8423b68c271414c9f870e1ced3231" dependencies = [ "swc_atoms", "swc_common", @@ -10604,9 +10584,9 @@ dependencies = [ [[package]] name = "swc_ecma_compat_es2020" -version = "0.10.2" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc6d451268aa73f105b2f663d20e7876ea4949c796bc25d62d58f598a776ea6" +checksum = "ed343932876fad34b1d4a13e30c55b94531e89916f45e7c04203bc49a29565b9" dependencies = [ "serde", "swc_atoms", @@ -10622,9 +10602,9 @@ dependencies = [ [[package]] name = "swc_ecma_compat_es2021" -version = "0.10.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c14bde6cfd6612cab24560f1db1aa26b0e09325d85a0c9cac3c84c196ea80397" +checksum = "0b6b28a2c109466eaa809d9b9a5b81dcbb4e269ba293a9c5c34aabc67b6427bc" dependencies = [ "swc_atoms", "swc_common", @@ -10638,9 +10618,9 @@ dependencies = [ [[package]] name = "swc_ecma_compat_es2022" -version = "0.10.2" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6454665fdfd1f74f1de8fd58dd2a4736903987b7ef840e1ad6f5e695555920c" +checksum = "ab3a644e271ea2a9df88e3e456c5c204c4916ef5136b7d946f9cd25607f47ec6" dependencies = [ "swc_atoms", "swc_common", @@ -10657,9 +10637,9 @@ dependencies = [ [[package]] name = "swc_ecma_compat_es3" -version = "0.10.2" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5092a2e55629997e8dd5191d1183b33b64707576fe719c17302fbd5cd2f8ac7a" +checksum = "e55ffadc12067b21524bf7b5d6938021ee918f65f18937ed27245c23544bc910" dependencies = [ "swc_common", "swc_ecma_ast", @@ -10672,9 +10652,9 @@ dependencies = [ [[package]] name = "swc_ecma_ext_transforms" -version = "0.119.0" +version = "0.120.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45036094d488b0f01706b9a385116326dd7912d8001e9451c506be171154a680" +checksum = "ad03ee53c734eb74757d03c07ec71b1a982261830c9253ef3e2e4a089f9af25d" dependencies = [ "phf 0.11.2", "swc_atoms", @@ -10686,9 +10666,9 @@ dependencies = [ [[package]] name = "swc_ecma_lints" -version = "0.98.2" +version = "0.99.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e008805fad22a9ca42a24395e9e46bacbc4b464048dba259fa5cf69e95cadd2" +checksum = "20c11bcc9e3dc49929500c07c8b0c84a88064847d31e9ee16204b257e6bd315c" dependencies = [ "auto_impl", "dashmap", @@ -10706,9 +10686,9 @@ dependencies = [ [[package]] name = "swc_ecma_loader" -version = "0.48.1" +version = "0.49.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a201c65ccbaa0c80fbcfd5c90dcc0bfc7ae62ac596f2233651ac715caf5d2c12" +checksum = "55fa3d55045b97894bfb04d38aff6d6302ac8a6a38e3bb3dfb0d20475c4974a9" dependencies = [ "anyhow", "dashmap", @@ -10728,9 +10708,9 @@ dependencies = [ [[package]] name = "swc_ecma_minifier" -version = "0.200.5" +version = "0.201.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624e23b532c9a2f74ea850120b19079ab67f6e85af53c83d1984adbecc820b03" +checksum = "5d75a42254926bad8b7fa9390767a18ac608d99cfd5a3be675e4739c2cf7db1b" dependencies = [ "arrayvec", "indexmap 2.3.0", @@ -10763,9 +10743,9 @@ dependencies = [ [[package]] name = "swc_ecma_parser" -version = "0.148.3" +version = "0.149.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a05ef8f80461e19374d9e8197f459a399b2802da19f72ea951ef343752ff3c04" +checksum = "7c7a81df222f44212c72fec4879c0d182c6eac66fb0e180afd05e8be6d920663" dependencies = [ "either", "new_debug_unreachable", @@ -10785,9 +10765,9 @@ dependencies = [ [[package]] name = "swc_ecma_preset_env" -version = "0.213.2" +version = "0.214.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae5f069e49b3ed41fa8136913497fa05357c24be2a29d205094d716d2d541a0a" +checksum = "e85162c77f8c80b55e5aed3a5e5477b74a53ce9cca05dec6e3dabec1de0f49af" dependencies = [ "anyhow", "dashmap", @@ -10810,9 +10790,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms" -version = "0.235.0" +version = "0.236.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bae99435503b117b96ffe1030976270c6ec6bdb6f5c62fcb2ce63674b48b1465" +checksum = "d289a83ab829d076d6c2bf2cc0beea7fbf0480ac47a415401f683181a65f4856" dependencies = [ "swc_atoms", "swc_common", @@ -10830,9 +10810,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_base" -version = "0.143.3" +version = "0.144.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6df81c1cbb920d9c47abe6fb105363b0f78df2c8f6b0910c4fdd2ad7cbdfb23d" +checksum = "7c0a71579d030e12fd3cfbfc8712c4ce21afc526f2a759903c77d8df61950f5e" dependencies = [ "better_scoped_tls", "bitflags 2.6.0", @@ -10853,9 +10833,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_classes" -version = "0.132.1" +version = "0.133.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53291bcdfca4bd4c2546c3170d7f0ea1d4f22f6fce2a531265ead010a9a2ebdf" +checksum = "0f37ec04525798a09ce02e52dc15433acee2d86664da0b8ede55bb5cefd95384" dependencies = [ "swc_atoms", "swc_common", @@ -10867,9 +10847,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_compat" -version = "0.169.2" +version = "0.170.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f689e03f059ebbff4455331d7e419933574fe409977f8c1d0005c480a89949e" +checksum = "4bb500b65423646da940e289ad37e7c88332d7194248c33fc63a9e768e104fe5" dependencies = [ "arrayvec", "indexmap 2.3.0", @@ -10915,9 +10895,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_module" -version = "0.186.3" +version = "0.187.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3d55d9d19cb00dfb661d571fd5d8b18e75c6b3bb080968449e71f785ecced00" +checksum = "cc022be297cdc70d5e71720587c7e810401a958577d77830f3108411e73c466d" dependencies = [ "Inflector", "anyhow", @@ -10942,9 +10922,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_optimization" -version = "0.204.2" +version = "0.205.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d7f9a903d6774d3f9005775badc25817296791ffed560f1b7e38aab62ca37ff" +checksum = "17446e46b75654901d962251ec4d0063423ee81759a325ed82fcf073308d97ca" dependencies = [ "dashmap", "indexmap 2.3.0", @@ -10966,9 +10946,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_proposal" -version = "0.177.2" +version = "0.178.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91dc2013d4cb5e25bf050fea84ea09aca12ada788f8debb47f2fdbff07fae651" +checksum = "f9c7ddb3aae86f19eb9e41b0c62509d8e400c1dc79c0889df98f6df1ab893f3f" dependencies = [ "either", "rustc-hash 1.1.0", @@ -10986,9 +10966,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_react" -version = "0.189.2" +version = "0.190.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d411add563dd86d50b3db6e74e38def06587fa2fd370b430f71226688bfa6ded" +checksum = "c3e54a8c87d90812bf69b0f07931bb629111a3f24efe83b9190c3a40a5ebc25e" dependencies = [ "base64 0.21.7", "dashmap", @@ -11011,9 +10991,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_typescript" -version = "0.194.5" +version = "0.195.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d16aa796183d9f2249243e7e8429ccf2f00d7b9134d807d5e68ec826c289af" +checksum = "f814b32ec83fde097df19e7346c429825390d156d0015f321f1f6434b6a06c0c" dependencies = [ "ryu-js", "serde", @@ -11028,9 +11008,9 @@ dependencies = [ [[package]] name = "swc_ecma_usage_analyzer" -version = "0.29.3" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f583b8db3cb9848537bd6f91c75398407e64332aee982337fe62ea4dec7e7b" +checksum = "6b04b74fc4525b03d1402018ca00855c10421631e4bbda4e202fc877d801acfa" dependencies = [ "indexmap 2.3.0", "rustc-hash 1.1.0", @@ -11045,9 +11025,9 @@ dependencies = [ [[package]] name = "swc_ecma_utils" -version = "0.133.4" +version = "0.134.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6148af60d25da893aef037621e4869e9b580eb280e12f5a8d4f87fa5e4cd5da" +checksum = "cde8f1ef3f7bd53340c7bd679f1ec563a45225ac8fb63f22d6de1ff4b345475d" dependencies = [ "indexmap 2.3.0", "num_cpus", @@ -11064,9 +11044,9 @@ dependencies = [ [[package]] name = "swc_ecma_visit" -version = "0.103.3" +version = "0.104.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed8026e4d9abcb75d511bf7623d49e8e135f02f4f9a6bb7c115df8239cfe3d4f" +checksum = "c71f5f97db49b96208805104b381c5e117f55fad5f3d178e626c92934a4d0e36" dependencies = [ "new_debug_unreachable", "num-bigint", @@ -11090,9 +11070,9 @@ dependencies = [ [[package]] name = "swc_error_reporters" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e02c81943772dc4fb0a6228360552d353fedc1a368ee6d80a5172ecb376b1796" +checksum = "0d049e9256abf29d9fc66d3db3ea44b6815a64ad565ce31e117a74ee96478bb3" dependencies = [ "anyhow", "miette", @@ -11103,9 +11083,9 @@ dependencies = [ [[package]] name = "swc_fast_graph" -version = "0.24.1" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3f854cf8efc290aa927d31dab98b42011ff2341fecb2b27fdc817ef7b30ef3b" +checksum = "357e2c97bb51431d65080f25b436bc4e2fc1a7f64a643bc21a8353e478dc799f" dependencies = [ "indexmap 2.3.0", "petgraph", @@ -11126,9 +11106,9 @@ dependencies = [ [[package]] name = "swc_node_comments" -version = "0.23.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6321ba0ee01751e16cf37fed1424c631faa3e362f95b2057f80b5c26adc1e4f" +checksum = "d016ab18b432523b2a3c104ce3aaf7d869db46c0a41477dbfb6201ddc86c1eb0" dependencies = [ "dashmap", "swc_atoms", @@ -11137,9 +11117,9 @@ dependencies = [ [[package]] name = "swc_timer" -version = "0.24.0" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78cf01b1f8a318614f566145b0016b2a0e84ac66d78c1374cdc3438e06c27740" +checksum = "6b5fb6f8b8b85512aacbb3d7140a828666e0e0b1bcc69bf84000a0cd36306bab" dependencies = [ "tracing", ] @@ -11170,9 +11150,9 @@ dependencies = [ [[package]] name = "swc_typescript" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03d9845b6dfbfb23aee9a990644a3c9560ef49243098149b5c8ef7ef4171f316" +checksum = "d5d043347b109a8aebfe01aaeada4af322304ea0f54ae8e5721df9afcb9305ca" dependencies = [ "swc_atoms", "swc_common", From e90fe837a02f48869159b2be32ca0877ecf491e1 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 16 Aug 2024 22:23:44 -0700 Subject: [PATCH 009/139] remove oid demo and its associated env vars --- .cargo/config.toml | 5 - .../openid_connect_demo/.cargo/config.toml | 5 - examples/openid_connect_demo/.gitignore | 3 - examples/openid_connect_demo/Cargo.lock | 6372 ----------------- examples/openid_connect_demo/Cargo.toml | 36 - examples/openid_connect_demo/Dioxus.toml | 35 - examples/openid_connect_demo/README.md | 12 - examples/openid_connect_demo/src/constants.rs | 2 - examples/openid_connect_demo/src/main.rs | 36 - examples/openid_connect_demo/src/model/mod.rs | 1 - .../openid_connect_demo/src/model/user.rs | 7 - examples/openid_connect_demo/src/oidc.rs | 127 - .../openid_connect_demo/src/props/client.rs | 20 - examples/openid_connect_demo/src/props/mod.rs | 1 - examples/openid_connect_demo/src/router.rs | 16 - examples/openid_connect_demo/src/storage.rs | 35 - .../openid_connect_demo/src/views/header.rs | 221 - .../openid_connect_demo/src/views/home.rs | 5 - .../openid_connect_demo/src/views/login.rs | 86 - examples/openid_connect_demo/src/views/mod.rs | 4 - .../src/views/not_found.rs | 10 - packages/cli-config/build.rs | 9 - packages/cli-config/src/lib.rs | 107 +- packages/desktop/build.rs | 12 +- 24 files changed, 41 insertions(+), 7126 deletions(-) delete mode 100644 examples/openid_connect_demo/.cargo/config.toml delete mode 100644 examples/openid_connect_demo/.gitignore delete mode 100644 examples/openid_connect_demo/Cargo.lock delete mode 100644 examples/openid_connect_demo/Cargo.toml delete mode 100644 examples/openid_connect_demo/Dioxus.toml delete mode 100644 examples/openid_connect_demo/README.md delete mode 100644 examples/openid_connect_demo/src/constants.rs delete mode 100644 examples/openid_connect_demo/src/main.rs delete mode 100644 examples/openid_connect_demo/src/model/mod.rs delete mode 100644 examples/openid_connect_demo/src/model/user.rs delete mode 100644 examples/openid_connect_demo/src/oidc.rs delete mode 100644 examples/openid_connect_demo/src/props/client.rs delete mode 100644 examples/openid_connect_demo/src/props/mod.rs delete mode 100644 examples/openid_connect_demo/src/router.rs delete mode 100644 examples/openid_connect_demo/src/storage.rs delete mode 100644 examples/openid_connect_demo/src/views/header.rs delete mode 100644 examples/openid_connect_demo/src/views/home.rs delete mode 100644 examples/openid_connect_demo/src/views/login.rs delete mode 100644 examples/openid_connect_demo/src/views/mod.rs delete mode 100644 examples/openid_connect_demo/src/views/not_found.rs diff --git a/.cargo/config.toml b/.cargo/config.toml index fb8c098da1..b9d1ed3471 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -3,10 +3,5 @@ # `https://doc.rust-lang.org/cargo/reference/config.html` # the .cargo/config.toml of the inner workspaces are not read when being invoked from the root workspace. -[env] -DIOXUS_FRONT_ISSUER_URL = "" -DIOXUS_FRONT_CLIENT_ID = "" -DIOXUS_FRONT_URL = "" - [profile.dev.package."*"] opt-level = 3 diff --git a/examples/openid_connect_demo/.cargo/config.toml b/examples/openid_connect_demo/.cargo/config.toml deleted file mode 100644 index fcb494816a..0000000000 --- a/examples/openid_connect_demo/.cargo/config.toml +++ /dev/null @@ -1,5 +0,0 @@ -[env] -DIOXUS_FRONT_ISSUER_URL = "TODO" -DIOXUS_FRONT_CLIENT_ID = "TODO" -DIOXUS_FRONT_CLIENT_SECRET = "TODO" -DIOXUS_FRONT_URL = "http://localhost:8080" diff --git a/examples/openid_connect_demo/.gitignore b/examples/openid_connect_demo/.gitignore deleted file mode 100644 index 919c8ee550..0000000000 --- a/examples/openid_connect_demo/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/target -/dist -.env diff --git a/examples/openid_connect_demo/Cargo.lock b/examples/openid_connect_demo/Cargo.lock deleted file mode 100644 index b7d513c11e..0000000000 --- a/examples/openid_connect_demo/Cargo.lock +++ /dev/null @@ -1,6372 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "addr2line" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "ahash" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy", -] - -[[package]] -name = "aho-corasick" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] - -[[package]] -name = "allocator-api2" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" - -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "anyhow" -version = "1.0.86" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" - -[[package]] -name = "anymap" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33954243bd79057c2de7338850b85983a44588021f8a5fee574a8888c6de4344" - -[[package]] -name = "anymap2" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c" - -[[package]] -name = "ashpd" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd884d7c72877a94102c3715f3b1cd09ff4fac28221add3e57cfbe25c236d093" -dependencies = [ - "async-fs", - "async-net", - "enumflags2", - "futures-channel", - "futures-util", - "rand 0.8.5", - "serde", - "serde_repr", - "url", - "zbus", -] - -[[package]] -name = "askama_escape" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341" - -[[package]] -name = "async-broadcast" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20cd0e2e25ea8e5f7e9df04578dc6cf5c83577fd09b1a46aaf5c85e1c33f2a7e" -dependencies = [ - "event-listener", - "event-listener-strategy", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-channel" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" -dependencies = [ - "concurrent-queue", - "event-listener-strategy", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-executor" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8828ec6e544c02b0d6691d21ed9f9218d0384a82542855073c2a3f58304aaf0" -dependencies = [ - "async-task", - "concurrent-queue", - "fastrand", - "futures-lite", - "slab", -] - -[[package]] -name = "async-fs" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" -dependencies = [ - "async-lock", - "blocking", - "futures-lite", -] - -[[package]] -name = "async-io" -version = "2.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d6baa8f0178795da0e71bc42c9e5d13261aac7ee549853162e66a241ba17964" -dependencies = [ - "async-lock", - "cfg-if", - "concurrent-queue", - "futures-io", - "futures-lite", - "parking", - "polling", - "rustix", - "slab", - "tracing", - "windows-sys 0.52.0", -] - -[[package]] -name = "async-lock" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" -dependencies = [ - "event-listener", - "event-listener-strategy", - "pin-project-lite", -] - -[[package]] -name = "async-net" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" -dependencies = [ - "async-io", - "blocking", - "futures-lite", -] - -[[package]] -name = "async-process" -version = "2.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7eda79bbd84e29c2b308d1dc099d7de8dcc7035e48f4bf5dc4a531a44ff5e2a" -dependencies = [ - "async-channel", - "async-io", - "async-lock", - "async-signal", - "async-task", - "blocking", - "cfg-if", - "event-listener", - "futures-lite", - "rustix", - "tracing", - "windows-sys 0.52.0", -] - -[[package]] -name = "async-recursion" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "async-signal" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "329972aa325176e89114919f2a80fdae4f4c040f66a370b1a1159c6c0f94e7aa" -dependencies = [ - "async-io", - "async-lock", - "atomic-waker", - "cfg-if", - "futures-core", - "futures-io", - "rustix", - "signal-hook-registry", - "slab", - "windows-sys 0.52.0", -] - -[[package]] -name = "async-task" -version = "4.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" - -[[package]] -name = "async-trait" -version = "0.1.80" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "atk" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4af014b17dd80e8af9fa689b2d4a211ddba6eb583c1622f35d0cb543f6b17e4" -dependencies = [ - "atk-sys", - "glib", - "libc", -] - -[[package]] -name = "atk-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "251e0b7d90e33e0ba930891a505a9a35ece37b2dd37a14f3ffc306c13b980009" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "atomic-polyfill" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" -dependencies = [ - "critical-section", -] - -[[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - -[[package]] -name = "autocfg" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" - -[[package]] -name = "axum" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" -dependencies = [ - "async-trait", - "axum-core", - "axum-macros", - "base64 0.21.7", - "bytes", - "futures-util", - "http 1.1.0", - "http-body 1.0.0", - "http-body-util", - "hyper 1.3.1", - "hyper-util", - "itoa 1.0.11", - "matchit", - "memchr", - "mime", - "multer", - "percent-encoding", - "pin-project-lite", - "rustversion", - "serde", - "serde_json", - "serde_path_to_error", - "serde_urlencoded", - "sha1", - "sync_wrapper 1.0.1", - "tokio", - "tokio-tungstenite", - "tower", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "axum-core" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" -dependencies = [ - "async-trait", - "bytes", - "futures-util", - "http 1.1.0", - "http-body 1.0.0", - "http-body-util", - "mime", - "pin-project-lite", - "rustversion", - "sync_wrapper 0.1.2", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "axum-macros" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00c055ee2d014ae5981ce1016374e8213682aa14d9bf40e48ab48b5f3ef20eaa" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "backtrace" -version = "0.3.72" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17c6a35df3749d2e8bb1b7b21a976d82b15548788d2735b9d82f329268f71a11" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - -[[package]] -name = "base16ct" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "base64" -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "base64ct" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" - -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" -dependencies = [ - "serde", -] - -[[package]] -name = "block" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array 0.14.7", -] - -[[package]] -name = "blocking" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" -dependencies = [ - "async-channel", - "async-task", - "futures-io", - "futures-lite", - "piper", -] - -[[package]] -name = "bstr" -version = "1.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" -dependencies = [ - "memchr", - "serde", -] - -[[package]] -name = "bumpalo" -version = "3.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "bytes" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" - -[[package]] -name = "cairo-rs" -version = "0.18.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" -dependencies = [ - "bitflags 2.5.0", - "cairo-sys-rs", - "glib", - "libc", - "once_cell", - "thiserror", -] - -[[package]] -name = "cairo-sys-rs" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" -dependencies = [ - "glib-sys", - "libc", - "system-deps", -] - -[[package]] -name = "camino" -version = "1.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo-platform" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo_metadata" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" -dependencies = [ - "camino", - "cargo-platform", - "semver", - "serde", - "serde_json", - "thiserror", -] - -[[package]] -name = "cc" -version = "1.0.98" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" - -[[package]] -name = "cesu8" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" - -[[package]] -name = "cfb" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" -dependencies = [ - "byteorder", - "fnv", - "uuid", -] - -[[package]] -name = "cfg-expr" -version = "0.15.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" -dependencies = [ - "smallvec", - "target-lexicon", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "cfg_aliases" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" - -[[package]] -name = "chrono" -version = "0.4.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "js-sys", - "num-traits", - "serde", - "wasm-bindgen", - "windows-targets 0.52.5", -] - -[[package]] -name = "ciborium" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" -dependencies = [ - "ciborium-io", - "ciborium-ll", - "serde", -] - -[[package]] -name = "ciborium-io" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" - -[[package]] -name = "ciborium-ll" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" -dependencies = [ - "ciborium-io", - "half", -] - -[[package]] -name = "cobs" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" - -[[package]] -name = "cocoa" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" -dependencies = [ - "bitflags 1.3.2", - "block", - "cocoa-foundation", - "core-foundation", - "core-graphics", - "foreign-types", - "libc", - "objc", -] - -[[package]] -name = "cocoa-foundation" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" -dependencies = [ - "bitflags 1.3.2", - "block", - "core-foundation", - "core-graphics-types", - "libc", - "objc", -] - -[[package]] -name = "combine" -version = "4.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" -dependencies = [ - "bytes", - "memchr", -] - -[[package]] -name = "concurrent-queue" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "console_error_panic_hook" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" -dependencies = [ - "cfg-if", - "wasm-bindgen", -] - -[[package]] -name = "const-oid" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" - -[[package]] -name = "const_format" -version = "0.2.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3a214c7af3d04997541b18d432afaff4c455e79e2029079647e72fc2bd27673" -dependencies = [ - "const_format_proc_macros", -] - -[[package]] -name = "const_format_proc_macros" -version = "0.2.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - -[[package]] -name = "convert_case" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" -dependencies = [ - "unicode-segmentation", -] - -[[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-sys" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" - -[[package]] -name = "core-graphics" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "core-graphics-types", - "foreign-types", - "libc", -] - -[[package]] -name = "core-graphics-types" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "libc", -] - -[[package]] -name = "cpufeatures" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" -dependencies = [ - "libc", -] - -[[package]] -name = "crc32fast" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "critical-section" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" - -[[package]] -name = "crossbeam-channel" -version = "0.5.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" -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.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[package]] -name = "crypto-bigint" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" -dependencies = [ - "generic-array 0.14.7", - "rand_core 0.6.4", - "subtle", - "zeroize", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array 0.14.7", - "typenum", -] - -[[package]] -name = "cssparser" -version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a" -dependencies = [ - "cssparser-macros", - "dtoa-short", - "itoa 0.4.8", - "matches", - "phf 0.8.0", - "proc-macro2", - "quote", - "smallvec", - "syn 1.0.109", -] - -[[package]] -name = "cssparser-macros" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" -dependencies = [ - "quote", - "syn 2.0.66", -] - -[[package]] -name = "curve25519-dalek" -version = "4.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348" -dependencies = [ - "cfg-if", - "cpufeatures", - "curve25519-dalek-derive", - "digest", - "fiat-crypto", - "platforms", - "rustc_version", - "subtle", - "zeroize", -] - -[[package]] -name = "curve25519-dalek-derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "darling" -version = "0.20.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.20.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 2.0.66", -] - -[[package]] -name = "darling_macro" -version = "0.20.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" -dependencies = [ - "darling_core", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "dashmap" -version = "5.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" -dependencies = [ - "cfg-if", - "hashbrown 0.14.5", - "lock_api", - "once_cell", - "parking_lot_core", -] - -[[package]] -name = "data-encoding" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" - -[[package]] -name = "der" -version = "0.7.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" -dependencies = [ - "const-oid", - "pem-rfc7468", - "zeroize", -] - -[[package]] -name = "deranged" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" -dependencies = [ - "powerfmt", - "serde", -] - -[[package]] -name = "derivative" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "derive_more" -version = "0.99.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" -dependencies = [ - "convert_case 0.4.0", - "proc-macro2", - "quote", - "rustc_version", - "syn 1.0.109", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "const-oid", - "crypto-common", - "subtle", -] - -[[package]] -name = "dioxus" -version = "0.5.2" -dependencies = [ - "dioxus-config-macro", - "dioxus-core", - "dioxus-core-macro", - "dioxus-desktop", - "dioxus-fullstack", - "dioxus-hooks", - "dioxus-hot-reload", - "dioxus-html", - "dioxus-liveview", - "dioxus-router", - "dioxus-signals", - "dioxus-ssr", - "dioxus-static-site-generation", - "dioxus-web", - "serde", -] - -[[package]] -name = "dioxus-cli-config" -version = "0.5.2" -dependencies = [ - "once_cell", - "serde", - "serde_json", - "tracing", -] - -[[package]] -name = "dioxus-config-macro" -version = "0.5.2" -dependencies = [ - "proc-macro2", - "quote", -] - -[[package]] -name = "dioxus-core" -version = "0.5.2" -dependencies = [ - "futures-channel", - "futures-util", - "generational-box", - "longest-increasing-subsequence", - "rustc-hash", - "serde", - "slab", - "tracing", - "tracing-subscriber", -] - -[[package]] -name = "dioxus-core-macro" -version = "0.5.2" -dependencies = [ - "convert_case 0.6.0", - "dioxus-rsx", - "prettyplease", - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "dioxus-debug-cell" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ea539174bb236e0e7dc9c12b19b88eae3cb574dedbd0252a2d43ea7e6de13e2" - -[[package]] -name = "dioxus-desktop" -version = "0.5.2" -dependencies = [ - "async-trait", - "cocoa", - "core-foundation", - "dioxus-cli-config", - "dioxus-core", - "dioxus-hooks", - "dioxus-hot-reload", - "dioxus-html", - "dioxus-interpreter-js", - "dunce", - "futures-channel", - "futures-util", - "generational-box", - "global-hotkey", - "infer", - "muda", - "objc", - "objc_id", - "rfd", - "rustc-hash", - "serde", - "serde_json", - "signal-hook", - "slab", - "tao", - "thiserror", - "tokio", - "tracing", - "urlencoding", - "webbrowser", - "wry", -] - -[[package]] -name = "dioxus-fullstack" -version = "0.5.2" -dependencies = [ - "anymap", - "async-trait", - "axum", - "base64 0.21.7", - "bytes", - "ciborium", - "dioxus-cli-config", - "dioxus-desktop", - "dioxus-hot-reload", - "dioxus-lib", - "dioxus-ssr", - "dioxus-web", - "dioxus_server_macro", - "futures-util", - "http 1.1.0", - "hyper 1.3.1", - "once_cell", - "pin-project", - "serde", - "serde_json", - "server_fn", - "thiserror", - "tokio", - "tokio-stream", - "tokio-util", - "tower", - "tower-http", - "tower-layer", - "tracing", - "tracing-futures", - "web-sys", -] - -[[package]] -name = "dioxus-hooks" -version = "0.5.2" -dependencies = [ - "dioxus-core", - "dioxus-debug-cell", - "dioxus-signals", - "futures-channel", - "futures-util", - "generational-box", - "slab", - "thiserror", - "tracing", -] - -[[package]] -name = "dioxus-hot-reload" -version = "0.5.2" -dependencies = [ - "axum", - "chrono", - "dioxus-core", - "dioxus-html", - "dioxus-rsx", - "execute", - "futures-util", - "ignore", - "interprocess-docfix", - "notify", - "once_cell", - "serde", - "serde_json", - "tokio", - "tokio-stream", - "tracing", -] - -[[package]] -name = "dioxus-html" -version = "0.5.2" -dependencies = [ - "async-trait", - "dioxus-core", - "dioxus-html-internal-macro", - "dioxus-rsx", - "enumset", - "euclid", - "futures-channel", - "generational-box", - "keyboard-types", - "serde", - "serde-value", - "serde_json", - "serde_repr", - "tokio", - "tracing", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "dioxus-html-internal-macro" -version = "0.5.2" -dependencies = [ - "convert_case 0.6.0", - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "dioxus-interpreter-js" -version = "0.5.2" -dependencies = [ - "dioxus-core", - "dioxus-html", - "js-sys", - "md5", - "sledgehammer_bindgen", - "sledgehammer_utils", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "dioxus-lib" -version = "0.5.2" -dependencies = [ - "dioxus-config-macro", - "dioxus-core", - "dioxus-core-macro", - "dioxus-hooks", - "dioxus-html", - "dioxus-rsx", - "dioxus-signals", -] - -[[package]] -name = "dioxus-liveview" -version = "0.5.2" -dependencies = [ - "axum", - "dioxus-cli-config", - "dioxus-core", - "dioxus-hot-reload", - "dioxus-html", - "dioxus-interpreter-js", - "futures-channel", - "futures-util", - "generational-box", - "rustc-hash", - "serde", - "serde_json", - "slab", - "thiserror", - "tokio", - "tokio-stream", - "tokio-util", - "tracing", -] - -[[package]] -name = "dioxus-logger" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe09dc9773dc1f1bb0d38529203d6555d08f67aadca5cf955ac3d1a9e69880" -dependencies = [ - "console_error_panic_hook", - "tracing", - "tracing-subscriber", - "tracing-wasm", -] - -[[package]] -name = "dioxus-router" -version = "0.5.2" -dependencies = [ - "dioxus-cli-config", - "dioxus-fullstack", - "dioxus-lib", - "dioxus-router-macro", - "dioxus-ssr", - "gloo", - "gloo-utils 0.1.7", - "http 1.1.0", - "js-sys", - "tokio", - "tracing", - "url", - "urlencoding", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "dioxus-router-macro" -version = "0.5.2" -dependencies = [ - "proc-macro2", - "quote", - "slab", - "syn 2.0.66", -] - -[[package]] -name = "dioxus-rsx" -version = "0.5.2" -dependencies = [ - "dioxus-core", - "internment", - "krates", - "proc-macro2", - "quote", - "syn 2.0.66", - "tracing", -] - -[[package]] -name = "dioxus-sdk" -version = "0.5.0" -source = "git+https://github.com/Dioxuslabs/sdk#d49812fbc9d4506bd3b1ec994f45ef4447f34c79" -dependencies = [ - "cfg-if", - "dioxus", - "dioxus-signals", - "directories", - "futures-util", - "js-sys", - "once_cell", - "postcard", - "rustc-hash", - "serde", - "tokio", - "tracing", - "uuid", - "wasm-bindgen", - "web-sys", - "yazi", -] - -[[package]] -name = "dioxus-signals" -version = "0.5.2" -dependencies = [ - "dioxus-core", - "futures-channel", - "futures-util", - "generational-box", - "once_cell", - "parking_lot", - "rustc-hash", - "serde", - "tracing", -] - -[[package]] -name = "dioxus-ssr" -version = "0.5.2" -dependencies = [ - "askama_escape", - "async-trait", - "chrono", - "dioxus-cli-config", - "dioxus-core", - "dioxus-html", - "generational-box", - "http 1.1.0", - "lru", - "rustc-hash", - "serde_json", - "thiserror", - "tokio", - "tracing", -] - -[[package]] -name = "dioxus-static-site-generation" -version = "0.5.2" -dependencies = [ - "axum", - "dioxus-cli-config", - "dioxus-fullstack", - "dioxus-hot-reload", - "dioxus-lib", - "dioxus-router", - "dioxus-ssr", - "dioxus-web", - "http 1.1.0", - "tokio", - "tower", - "tower-http", - "tracing", -] - -[[package]] -name = "dioxus-web" -version = "0.5.2" -dependencies = [ - "async-trait", - "console_error_panic_hook", - "dioxus-core", - "dioxus-html", - "dioxus-interpreter-js", - "futures-channel", - "futures-util", - "generational-box", - "js-sys", - "rustc-hash", - "serde", - "serde-wasm-bindgen", - "serde_json", - "tracing", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "dioxus_server_macro" -version = "0.5.2" -dependencies = [ - "convert_case 0.6.0", - "proc-macro2", - "quote", - "server_fn_macro", - "syn 2.0.66", -] - -[[package]] -name = "directories" -version = "4.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-sys" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - -[[package]] -name = "dispatch" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" - -[[package]] -name = "dlopen2" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1297103d2bbaea85724fcee6294c2d50b1081f9ad47d0f6f6f61eda65315a6" -dependencies = [ - "dlopen2_derive", - "libc", - "once_cell", - "winapi", -] - -[[package]] -name = "dlopen2_derive" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "dtoa" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" - -[[package]] -name = "dtoa-short" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbaceec3c6e4211c79e7b1800fb9680527106beb2f9c51904a3210c03a448c74" -dependencies = [ - "dtoa", -] - -[[package]] -name = "dunce" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" - -[[package]] -name = "dyn-clone" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" - -[[package]] -name = "ecdsa" -version = "0.16.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" -dependencies = [ - "der", - "digest", - "elliptic-curve", - "rfc6979", - "signature", - "spki", -] - -[[package]] -name = "ed25519" -version = "2.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" -dependencies = [ - "pkcs8", - "signature", -] - -[[package]] -name = "ed25519-dalek" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" -dependencies = [ - "curve25519-dalek", - "ed25519", - "serde", - "sha2", - "subtle", - "zeroize", -] - -[[package]] -name = "either" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" - -[[package]] -name = "elliptic-curve" -version = "0.13.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" -dependencies = [ - "base16ct", - "crypto-bigint", - "digest", - "ff", - "generic-array 0.14.7", - "group", - "hkdf", - "pem-rfc7468", - "pkcs8", - "rand_core 0.6.4", - "sec1", - "subtle", - "zeroize", -] - -[[package]] -name = "embedded-io" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" - -[[package]] -name = "encoding_rs" -version = "0.8.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" -dependencies = [ - "cfg-if", -] - -[[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.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3278c9d5fb675e0a51dabcf4c0d355f692b064171535ba72361be1528a9d8e8d" -dependencies = [ - "enumflags2_derive", - "serde", -] - -[[package]] -name = "enumflags2_derive" -version = "0.7.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c785274071b1b420972453b306eeca06acf4633829db4223b58a2a8c5953bc4" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "enumset" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "226c0da7462c13fb57e5cc9e0dc8f0635e7d27f276a3a7fd30054647f669007d" -dependencies = [ - "enumset_derive", -] - -[[package]] -name = "enumset_derive" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08b6c6ab82d70f08844964ba10c7babb716de2ecaeab9be5717918a5177d3af" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[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.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "euclid" -version = "0.22.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0f0eb73b934648cd7a4a61f1b15391cd95dab0b4da6e2e66c2a072c144b4a20" -dependencies = [ - "num-traits", - "serde", -] - -[[package]] -name = "event-listener" -version = "5.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener-strategy" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" -dependencies = [ - "event-listener", - "pin-project-lite", -] - -[[package]] -name = "execute" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a82608ee96ce76aeab659e9b8d3c2b787bffd223199af88c674923d861ada10" -dependencies = [ - "execute-command-macro", - "execute-command-tokens", - "generic-array 1.0.0", -] - -[[package]] -name = "execute-command-macro" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90dec53d547564e911dc4ff3ecb726a64cf41a6fa01a2370ebc0d95175dd08bd" -dependencies = [ - "execute-command-macro-impl", -] - -[[package]] -name = "execute-command-macro-impl" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce8cd46a041ad005ab9c71263f9a0ff5b529eac0fe4cc9b4a20f4f0765d8cf4b" -dependencies = [ - "execute-command-tokens", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "execute-command-tokens" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69dc321eb6be977f44674620ca3aa21703cb20ffbe560e1ae97da08401ffbcad" - -[[package]] -name = "fastrand" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" - -[[package]] -name = "fdeflate" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" -dependencies = [ - "simd-adler32", -] - -[[package]] -name = "ff" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" -dependencies = [ - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "fiat-crypto" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" - -[[package]] -name = "field-offset" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" -dependencies = [ - "memoffset", - "rustc_version", -] - -[[package]] -name = "filetime" -version = "0.2.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall 0.4.1", - "windows-sys 0.52.0", -] - -[[package]] -name = "fixedbitset" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" - -[[package]] -name = "flate2" -version = "1.0.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foreign-types" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" -dependencies = [ - "foreign-types-macros", - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-macros" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[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 = "fsevent-sys" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" -dependencies = [ - "libc", -] - -[[package]] -name = "futf" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" -dependencies = [ - "mac", - "new_debug_unreachable", -] - -[[package]] -name = "futures" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" - -[[package]] -name = "futures-executor" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" - -[[package]] -name = "futures-lite" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" -dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "parking", - "pin-project-lite", -] - -[[package]] -name = "futures-macro" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "futures-sink" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" - -[[package]] -name = "futures-task" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" - -[[package]] -name = "futures-util" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "fxhash" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" -dependencies = [ - "byteorder", -] - -[[package]] -name = "gdk" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5ba081bdef3b75ebcdbfc953699ed2d7417d6bd853347a42a37d76406a33646" -dependencies = [ - "cairo-rs", - "gdk-pixbuf", - "gdk-sys", - "gio", - "glib", - "libc", - "pango", -] - -[[package]] -name = "gdk-pixbuf" -version = "0.18.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" -dependencies = [ - "gdk-pixbuf-sys", - "gio", - "glib", - "libc", - "once_cell", -] - -[[package]] -name = "gdk-pixbuf-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" -dependencies = [ - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "gdk-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31ff856cb3386dae1703a920f803abafcc580e9b5f711ca62ed1620c25b51ff2" -dependencies = [ - "cairo-sys-rs", - "gdk-pixbuf-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "pango-sys", - "pkg-config", - "system-deps", -] - -[[package]] -name = "gdkwayland-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a90fbf5c033c65d93792192a49a8efb5bb1e640c419682a58bb96f5ae77f3d4a" -dependencies = [ - "gdk-sys", - "glib-sys", - "gobject-sys", - "libc", - "pkg-config", - "system-deps", -] - -[[package]] -name = "gdkx11" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2ea8a4909d530f79921290389cbd7c34cb9d623bfe970eaae65ca5f9cd9cce" -dependencies = [ - "gdk", - "gdkx11-sys", - "gio", - "glib", - "libc", - "x11", -] - -[[package]] -name = "gdkx11-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fee8f00f4ee46cad2939b8990f5c70c94ff882c3028f3cc5abf950fa4ab53043" -dependencies = [ - "gdk-sys", - "glib-sys", - "libc", - "system-deps", - "x11", -] - -[[package]] -name = "generational-box" -version = "0.5.2" -dependencies = [ - "parking_lot", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", - "zeroize", -] - -[[package]] -name = "generic-array" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe739944a5406424e080edccb6add95685130b9f160d5407c639c7df0c5836b0" -dependencies = [ - "typenum", -] - -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - -[[package]] -name = "getrandom" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi 0.11.0+wasi-snapshot-preview1", - "wasm-bindgen", -] - -[[package]] -name = "gimli" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" - -[[package]] -name = "gio" -version = "0.18.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-util", - "gio-sys", - "glib", - "libc", - "once_cell", - "pin-project-lite", - "smallvec", - "thiserror", -] - -[[package]] -name = "gio-sys" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", - "winapi", -] - -[[package]] -name = "glib" -version = "0.18.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" -dependencies = [ - "bitflags 2.5.0", - "futures-channel", - "futures-core", - "futures-executor", - "futures-task", - "futures-util", - "gio-sys", - "glib-macros", - "glib-sys", - "gobject-sys", - "libc", - "memchr", - "once_cell", - "smallvec", - "thiserror", -] - -[[package]] -name = "glib-macros" -version = "0.18.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" -dependencies = [ - "heck 0.4.1", - "proc-macro-crate 2.0.2", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "glib-sys" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" -dependencies = [ - "libc", - "system-deps", -] - -[[package]] -name = "global-hotkey" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89cb13e8c52c87e28a46eae3e5e65b8f0cd465c4c9e67b13d56c70412e792bc3" -dependencies = [ - "bitflags 2.5.0", - "cocoa", - "crossbeam-channel", - "keyboard-types", - "objc", - "once_cell", - "thiserror", - "windows-sys 0.52.0", - "x11-dl", -] - -[[package]] -name = "globset" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" -dependencies = [ - "aho-corasick", - "bstr", - "log", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "gloo" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28999cda5ef6916ffd33fb4a7b87e1de633c47c0dc6d97905fee1cdaa142b94d" -dependencies = [ - "gloo-console", - "gloo-dialogs", - "gloo-events", - "gloo-file", - "gloo-history", - "gloo-net 0.3.1", - "gloo-render", - "gloo-storage", - "gloo-timers", - "gloo-utils 0.1.7", - "gloo-worker", -] - -[[package]] -name = "gloo-console" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b7ce3c05debe147233596904981848862b068862e9ec3e34be446077190d3f" -dependencies = [ - "gloo-utils 0.1.7", - "js-sys", - "serde", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gloo-dialogs" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67062364ac72d27f08445a46cab428188e2e224ec9e37efdba48ae8c289002e6" -dependencies = [ - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gloo-events" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b107f8abed8105e4182de63845afcc7b69c098b7852a813ea7462a320992fc" -dependencies = [ - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gloo-file" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d5564e570a38b43d78bdc063374a0c3098c4f0d64005b12f9bbe87e869b6d7" -dependencies = [ - "gloo-events", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gloo-history" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85725d90bf0ed47063b3930ef28e863658a7905989e9929a8708aab74a1d5e7f" -dependencies = [ - "gloo-events", - "gloo-utils 0.1.7", - "serde", - "serde-wasm-bindgen", - "serde_urlencoded", - "thiserror", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gloo-net" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a66b4e3c7d9ed8d315fd6b97c8b1f74a7c6ecbbc2320e65ae7ed38b7068cc620" -dependencies = [ - "futures-channel", - "futures-core", - "futures-sink", - "gloo-utils 0.1.7", - "http 0.2.12", - "js-sys", - "pin-project", - "serde", - "serde_json", - "thiserror", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "gloo-net" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43aaa242d1239a8822c15c645f02166398da4f8b5c4bae795c1f5b44e9eee173" -dependencies = [ - "futures-channel", - "futures-core", - "futures-sink", - "gloo-utils 0.2.0", - "http 0.2.12", - "js-sys", - "pin-project", - "serde", - "serde_json", - "thiserror", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "gloo-render" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd9306aef67cfd4449823aadcd14e3958e0800aa2183955a309112a84ec7764" -dependencies = [ - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gloo-storage" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d6ab60bf5dbfd6f0ed1f7843da31b41010515c745735c970e821945ca91e480" -dependencies = [ - "gloo-utils 0.1.7", - "js-sys", - "serde", - "serde_json", - "thiserror", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gloo-timers" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "gloo-utils" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037fcb07216cb3a30f7292bd0176b050b7b9a052ba830ef7d5d65f6dc64ba58e" -dependencies = [ - "js-sys", - "serde", - "serde_json", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gloo-utils" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" -dependencies = [ - "js-sys", - "serde", - "serde_json", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gloo-worker" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13471584da78061a28306d1359dd0178d8d6fc1c7c80e5e35d27260346e0516a" -dependencies = [ - "anymap2", - "bincode", - "gloo-console", - "gloo-utils 0.1.7", - "js-sys", - "serde", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "gobject-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" -dependencies = [ - "glib-sys", - "libc", - "system-deps", -] - -[[package]] -name = "group" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" -dependencies = [ - "ff", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "gtk" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93c4f5e0e20b60e10631a5f06da7fe3dda744b05ad0ea71fee2f47adf865890c" -dependencies = [ - "atk", - "cairo-rs", - "field-offset", - "futures-channel", - "gdk", - "gdk-pixbuf", - "gio", - "glib", - "gtk-sys", - "gtk3-macros", - "libc", - "pango", - "pkg-config", -] - -[[package]] -name = "gtk-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771437bf1de2c1c0b496c11505bdf748e26066bbe942dfc8f614c9460f6d7722" -dependencies = [ - "atk-sys", - "cairo-sys-rs", - "gdk-pixbuf-sys", - "gdk-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "pango-sys", - "system-deps", -] - -[[package]] -name = "gtk3-macros" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6063efb63db582968fb7df72e1ae68aa6360dcfb0a75143f34fc7d616bad75e" -dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "h2" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http 0.2.12", - "indexmap 2.2.6", - "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", - "crunchy", -] - -[[package]] -name = "hash32" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" -dependencies = [ - "byteorder", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", - "allocator-api2", -] - -[[package]] -name = "heapless" -version = "0.7.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" -dependencies = [ - "atomic-polyfill", - "hash32", - "rustc_version", - "serde", - "spin 0.9.8", - "stable_deref_trait", -] - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hkdf" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" -dependencies = [ - "hmac", -] - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] - -[[package]] -name = "home" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" -dependencies = [ - "windows-sys 0.52.0", -] - -[[package]] -name = "html5ever" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" -dependencies = [ - "log", - "mac", - "markup5ever", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "http" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" -dependencies = [ - "bytes", - "fnv", - "itoa 1.0.11", -] - -[[package]] -name = "http" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" -dependencies = [ - "bytes", - "fnv", - "itoa 1.0.11", -] - -[[package]] -name = "http-body" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" -dependencies = [ - "bytes", - "http 0.2.12", - "pin-project-lite", -] - -[[package]] -name = "http-body" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" -dependencies = [ - "bytes", - "http 1.1.0", -] - -[[package]] -name = "http-body-util" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" -dependencies = [ - "bytes", - "futures-core", - "http 1.1.0", - "http-body 1.0.0", - "pin-project-lite", -] - -[[package]] -name = "http-range-header" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08a397c49fec283e3d6211adbe480be95aae5f304cfb923e9970e08956d5168a" - -[[package]] -name = "httparse" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" - -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - -[[package]] -name = "hyper" -version = "0.14.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http 0.2.12", - "http-body 0.4.6", - "httparse", - "httpdate", - "itoa 1.0.11", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "http 1.1.0", - "http-body 1.0.0", - "httparse", - "httpdate", - "itoa 1.0.11", - "pin-project-lite", - "smallvec", - "tokio", -] - -[[package]] -name = "hyper-rustls" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" -dependencies = [ - "futures-util", - "http 0.2.12", - "hyper 0.14.29", - "rustls", - "tokio", - "tokio-rustls", -] - -[[package]] -name = "hyper-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b875924a60b96e5d7b9ae7b066540b1dd1cbd90d1828f54c92e02a283351c56" -dependencies = [ - "bytes", - "futures-util", - "http 1.1.0", - "http-body 1.0.0", - "hyper 1.3.1", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" -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 = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "idna" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "ignore" -version = "0.4.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1" -dependencies = [ - "crossbeam-deque", - "globset", - "log", - "memchr", - "regex-automata", - "same-file", - "walkdir", - "winapi-util", -] - -[[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.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" -dependencies = [ - "equivalent", - "hashbrown 0.14.5", - "serde", -] - -[[package]] -name = "infer" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6c16b11a665b26aeeb9b1d7f954cdeb034be38dd00adab4f2ae921a8fee804" -dependencies = [ - "cfb", -] - -[[package]] -name = "inotify" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" -dependencies = [ - "bitflags 1.3.2", - "inotify-sys", - "libc", -] - -[[package]] -name = "inotify-sys" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" -dependencies = [ - "libc", -] - -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "internment" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04e8e537b529b8674e97e9fb82c10ff168a290ac3867a0295f112061ffbca1ef" -dependencies = [ - "hashbrown 0.14.5", - "parking_lot", -] - -[[package]] -name = "interprocess-docfix" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b84ee245c606aeb0841649a9288e3eae8c61b853a8cd5c0e14450e96d53d28f" -dependencies = [ - "blocking", - "cfg-if", - "futures-core", - "futures-io", - "intmap", - "libc", - "once_cell", - "rustc_version", - "spinning", - "thiserror", - "to_method", - "winapi", -] - -[[package]] -name = "intmap" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae52f28f45ac2bc96edb7714de995cffc174a395fb0abf5bff453587c980d7b9" - -[[package]] -name = "inventory" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f958d3d68f4167080a18141e10381e7634563984a537f2a49a30fd8e53ac5767" - -[[package]] -name = "ipnet" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" - -[[package]] -name = "itoa" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" - -[[package]] -name = "javascriptcore-rs" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca5671e9ffce8ffba57afc24070e906da7fc4b1ba66f2cabebf61bf2ea257fcc" -dependencies = [ - "bitflags 1.3.2", - "glib", - "javascriptcore-rs-sys", -] - -[[package]] -name = "javascriptcore-rs-sys" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1be78d14ffa4b75b66df31840478fef72b51f8c2465d4ca7c194da9f7a5124" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "jni" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" -dependencies = [ - "cesu8", - "cfg-if", - "combine", - "jni-sys", - "log", - "thiserror", - "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 = "js-sys" -version = "0.3.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "keyboard-types" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" -dependencies = [ - "bitflags 2.5.0", - "serde", - "unicode-segmentation", -] - -[[package]] -name = "kqueue" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" -dependencies = [ - "kqueue-sys", - "libc", -] - -[[package]] -name = "kqueue-sys" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" -dependencies = [ - "bitflags 1.3.2", - "libc", -] - -[[package]] -name = "krates" -version = "0.16.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcb3baf2360eb25ad31f0ada3add63927ada6db457791979b82ac199f835cb9" -dependencies = [ - "cargo-platform", - "cargo_metadata", - "cfg-expr", - "petgraph", - "semver", -] - -[[package]] -name = "kuchikiki" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e4755b7b995046f510a7520c42b2fed58b77bd94d5a87a8eb43d2fd126da8" -dependencies = [ - "cssparser", - "html5ever", - "indexmap 1.9.3", - "matches", - "selectors", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -dependencies = [ - "spin 0.5.2", -] - -[[package]] -name = "libc" -version = "0.2.155" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" - -[[package]] -name = "libm" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" - -[[package]] -name = "libredox" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" -dependencies = [ - "bitflags 2.5.0", - "libc", -] - -[[package]] -name = "libxdo" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00333b8756a3d28e78def82067a377de7fa61b24909000aeaa2b446a948d14db" -dependencies = [ - "libxdo-sys", -] - -[[package]] -name = "libxdo-sys" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db23b9e7e2b7831bbd8aac0bbeeeb7b68cbebc162b227e7052e8e55829a09212" -dependencies = [ - "libc", - "x11", -] - -[[package]] -name = "linux-raw-sys" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" - -[[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 = "log" -version = "0.4.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" - -[[package]] -name = "longest-increasing-subsequence" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3bd0dd2cd90571056fdb71f6275fada10131182f84899f4b2a916e565d81d86" - -[[package]] -name = "lru" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" -dependencies = [ - "hashbrown 0.14.5", -] - -[[package]] -name = "mac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" - -[[package]] -name = "malloc_buf" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" -dependencies = [ - "libc", -] - -[[package]] -name = "markup5ever" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" -dependencies = [ - "log", - "phf 0.10.1", - "phf_codegen 0.10.0", - "string_cache", - "string_cache_codegen", - "tendril", -] - -[[package]] -name = "matches" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" - -[[package]] -name = "matchit" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" - -[[package]] -name = "md5" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" - -[[package]] -name = "memchr" -version = "2.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" - -[[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 = "mime_guess" -version = "2.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" -dependencies = [ - "mime", - "unicase", -] - -[[package]] -name = "miniz_oxide" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" -dependencies = [ - "adler", - "simd-adler32", -] - -[[package]] -name = "mio" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" -dependencies = [ - "libc", - "log", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.48.0", -] - -[[package]] -name = "muda" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c47e7625990fc1af2226ea4f34fb2412b03c12639fcb91868581eb3a6893453" -dependencies = [ - "cocoa", - "crossbeam-channel", - "gtk", - "keyboard-types", - "libxdo", - "objc", - "once_cell", - "png", - "thiserror", - "windows-sys 0.52.0", -] - -[[package]] -name = "multer" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b" -dependencies = [ - "bytes", - "encoding_rs", - "futures-util", - "http 1.1.0", - "httparse", - "memchr", - "mime", - "spin 0.9.8", - "version_check", -] - -[[package]] -name = "ndk" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" -dependencies = [ - "bitflags 1.3.2", - "jni-sys", - "ndk-sys", - "num_enum", - "raw-window-handle 0.5.2", - "thiserror", -] - -[[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.4.1+23.1.7779620" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cf2aae958bd232cac5069850591667ad422d263686d75b52a065f9badeee5a3" -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.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" -dependencies = [ - "bitflags 2.5.0", - "cfg-if", - "libc", - "memoffset", -] - -[[package]] -name = "nodrop" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" - -[[package]] -name = "notify" -version = "5.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "729f63e1ca555a43fe3efa4f3efdf4801c479da85b432242a7b726f353c88486" -dependencies = [ - "bitflags 1.3.2", - "crossbeam-channel", - "filetime", - "fsevent-sys", - "inotify", - "kqueue", - "libc", - "mio", - "walkdir", - "windows-sys 0.45.0", -] - -[[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "winapi", -] - -[[package]] -name = "num-bigint-dig" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" -dependencies = [ - "byteorder", - "lazy_static", - "libm", - "num-integer", - "num-iter", - "num-traits", - "rand 0.8.5", - "smallvec", - "zeroize", -] - -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - -[[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-iter" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" -dependencies = [ - "autocfg", - "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", - "libm", -] - -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "num_enum" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" -dependencies = [ - "num_enum_derive", -] - -[[package]] -name = "num_enum_derive" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" -dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "oauth2" -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c38841cdd844847e3e7c8d29cef9dcfed8877f8f56f9071f77843ecf3baf937f" -dependencies = [ - "base64 0.13.1", - "chrono", - "getrandom 0.2.15", - "http 0.2.12", - "rand 0.8.5", - "reqwest", - "serde", - "serde_json", - "serde_path_to_error", - "sha2", - "thiserror", - "url", -] - -[[package]] -name = "objc" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" -dependencies = [ - "malloc_buf", - "objc_exception", -] - -[[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_exception" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" -dependencies = [ - "cc", -] - -[[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.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" - -[[package]] -name = "openid_auth_demo" -version = "0.1.0" -dependencies = [ - "anyhow", - "console_error_panic_hook", - "dioxus", - "dioxus-logger", - "dioxus-sdk", - "form_urlencoded", - "log", - "openidconnect", - "serde", - "uuid", -] - -[[package]] -name = "openidconnect" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f47e80a9cfae4462dd29c41e987edd228971d6565553fbc14b8a11e666d91590" -dependencies = [ - "base64 0.13.1", - "chrono", - "dyn-clone", - "ed25519-dalek", - "hmac", - "http 0.2.12", - "itertools", - "log", - "oauth2", - "p256", - "p384", - "rand 0.8.5", - "rsa", - "serde", - "serde-value", - "serde_derive", - "serde_json", - "serde_path_to_error", - "serde_plain", - "serde_with", - "sha2", - "subtle", - "thiserror", - "url", -] - -[[package]] -name = "ordered-float" -version = "2.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" -dependencies = [ - "num-traits", -] - -[[package]] -name = "ordered-stream" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" -dependencies = [ - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - -[[package]] -name = "p256" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" -dependencies = [ - "ecdsa", - "elliptic-curve", - "primeorder", - "sha2", -] - -[[package]] -name = "p384" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" -dependencies = [ - "ecdsa", - "elliptic-curve", - "primeorder", - "sha2", -] - -[[package]] -name = "pango" -version = "0.18.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" -dependencies = [ - "gio", - "glib", - "libc", - "once_cell", - "pango-sys", -] - -[[package]] -name = "pango-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "parking" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" - -[[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", - "libc", - "redox_syscall 0.5.1", - "smallvec", - "windows-targets 0.52.5", -] - -[[package]] -name = "pem-rfc7468" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" -dependencies = [ - "base64ct", -] - -[[package]] -name = "percent-encoding" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" - -[[package]] -name = "petgraph" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" -dependencies = [ - "fixedbitset", - "indexmap 2.2.6", -] - -[[package]] -name = "phf" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" -dependencies = [ - "phf_macros", - "phf_shared 0.8.0", - "proc-macro-hack", -] - -[[package]] -name = "phf" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" -dependencies = [ - "phf_shared 0.10.0", -] - -[[package]] -name = "phf_codegen" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" -dependencies = [ - "phf_generator 0.8.0", - "phf_shared 0.8.0", -] - -[[package]] -name = "phf_codegen" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" -dependencies = [ - "phf_generator 0.10.0", - "phf_shared 0.10.0", -] - -[[package]] -name = "phf_generator" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" -dependencies = [ - "phf_shared 0.8.0", - "rand 0.7.3", -] - -[[package]] -name = "phf_generator" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" -dependencies = [ - "phf_shared 0.10.0", - "rand 0.8.5", -] - -[[package]] -name = "phf_macros" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" -dependencies = [ - "phf_generator 0.8.0", - "phf_shared 0.8.0", - "proc-macro-hack", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "phf_shared" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" -dependencies = [ - "siphasher", -] - -[[package]] -name = "phf_shared" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" -dependencies = [ - "siphasher", -] - -[[package]] -name = "pin-project" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" - -[[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.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1d5c74c9876f070d3e8fd503d748c7d974c3e48da8f41350fa5222ef9b4391" -dependencies = [ - "atomic-waker", - "fastrand", - "futures-io", -] - -[[package]] -name = "pkcs1" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" -dependencies = [ - "der", - "pkcs8", - "spki", -] - -[[package]] -name = "pkcs8" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" -dependencies = [ - "der", - "spki", -] - -[[package]] -name = "pkg-config" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" - -[[package]] -name = "platforms" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7" - -[[package]] -name = "png" -version = "0.17.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" -dependencies = [ - "bitflags 1.3.2", - "crc32fast", - "fdeflate", - "flate2", - "miniz_oxide", -] - -[[package]] -name = "polling" -version = "3.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6a007746f34ed64099e88783b0ae369eaa3da6392868ba262e2af9b8fbaea1" -dependencies = [ - "cfg-if", - "concurrent-queue", - "hermit-abi", - "pin-project-lite", - "rustix", - "tracing", - "windows-sys 0.52.0", -] - -[[package]] -name = "pollster" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2" - -[[package]] -name = "postcard" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a55c51ee6c0db07e68448e336cf8ea4131a620edefebf9893e759b2d793420f8" -dependencies = [ - "cobs", - "embedded-io", - "heapless", - "serde", -] - -[[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.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "precomputed-hash" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" - -[[package]] -name = "prettyplease" -version = "0.2.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" -dependencies = [ - "proc-macro2", - "syn 2.0.66", -] - -[[package]] -name = "primeorder" -version = "0.13.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" -dependencies = [ - "elliptic-curve", -] - -[[package]] -name = "proc-macro-crate" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" -dependencies = [ - "once_cell", - "toml_edit 0.19.15", -] - -[[package]] -name = "proc-macro-crate" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" -dependencies = [ - "toml_datetime", - "toml_edit 0.20.2", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro-hack" -version = "0.5.20+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" - -[[package]] -name = "proc-macro2" -version = "1.0.85" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", - "rand_pcg", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", -] - -[[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 0.6.4", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.15", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "rand_pcg" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "raw-window-handle" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" - -[[package]] -name = "raw-window-handle" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" - -[[package]] -name = "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.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" -dependencies = [ - "bitflags 2.5.0", -] - -[[package]] -name = "redox_users" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" -dependencies = [ - "getrandom 0.2.15", - "libredox", - "thiserror", -] - -[[package]] -name = "regex" -version = "1.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" - -[[package]] -name = "reqwest" -version = "0.11.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" -dependencies = [ - "base64 0.21.7", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.29", - "hyper-rustls", - "ipnet", - "js-sys", - "log", - "mime", - "mime_guess", - "once_cell", - "percent-encoding", - "pin-project-lite", - "rustls", - "rustls-pemfile", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper 0.1.2", - "system-configuration", - "tokio", - "tokio-rustls", - "tokio-util", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-streams", - "web-sys", - "webpki-roots", - "winreg", -] - -[[package]] -name = "rfc6979" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" -dependencies = [ - "hmac", - "subtle", -] - -[[package]] -name = "rfd" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25a73a7337fc24366edfca76ec521f51877b114e42dab584008209cca6719251" -dependencies = [ - "ashpd", - "block", - "dispatch", - "js-sys", - "log", - "objc", - "objc-foundation", - "objc_id", - "pollster", - "raw-window-handle 0.6.2", - "urlencoding", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "windows-sys 0.48.0", -] - -[[package]] -name = "ring" -version = "0.17.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" -dependencies = [ - "cc", - "cfg-if", - "getrandom 0.2.15", - "libc", - "spin 0.9.8", - "untrusted", - "windows-sys 0.52.0", -] - -[[package]] -name = "rsa" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" -dependencies = [ - "const-oid", - "digest", - "num-bigint-dig", - "num-integer", - "num-traits", - "pkcs1", - "pkcs8", - "rand_core 0.6.4", - "signature", - "spki", - "subtle", - "zeroize", -] - -[[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_version" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver", -] - -[[package]] -name = "rustix" -version = "0.38.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" -dependencies = [ - "bitflags 2.5.0", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.52.0", -] - -[[package]] -name = "rustls" -version = "0.21.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" -dependencies = [ - "log", - "ring", - "rustls-webpki", - "sct", -] - -[[package]] -name = "rustls-pemfile" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" -dependencies = [ - "base64 0.21.7", -] - -[[package]] -name = "rustls-webpki" -version = "0.101.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "rustversion" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" - -[[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 = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "sct" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "sec1" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" -dependencies = [ - "base16ct", - "der", - "generic-array 0.14.7", - "pkcs8", - "subtle", - "zeroize", -] - -[[package]] -name = "selectors" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe" -dependencies = [ - "bitflags 1.3.2", - "cssparser", - "derive_more", - "fxhash", - "log", - "matches", - "phf 0.8.0", - "phf_codegen 0.8.0", - "precomputed-hash", - "servo_arc", - "smallvec", - "thin-slice", -] - -[[package]] -name = "semver" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" -dependencies = [ - "serde", -] - -[[package]] -name = "send_wrapper" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" -dependencies = [ - "futures-core", -] - -[[package]] -name = "serde" -version = "1.0.203" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde-value" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" -dependencies = [ - "ordered-float", - "serde", -] - -[[package]] -name = "serde-wasm-bindgen" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3b143e2833c57ab9ad3ea280d21fd34e285a42837aeb0ee301f4f41890fa00e" -dependencies = [ - "js-sys", - "serde", - "wasm-bindgen", -] - -[[package]] -name = "serde_derive" -version = "1.0.203" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "serde_json" -version = "1.0.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" -dependencies = [ - "itoa 1.0.11", - "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 1.0.11", - "serde", -] - -[[package]] -name = "serde_plain" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1fc6db65a611022b23a0dec6975d63fb80a302cb3388835ff02c097258d50" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_qs" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0431a35568651e363364210c91983c1da5eb29404d9f0928b67d4ebcfa7d330c" -dependencies = [ - "percent-encoding", - "serde", - "thiserror", -] - -[[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.66", -] - -[[package]] -name = "serde_spanned" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" -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 1.0.11", - "ryu", - "serde", -] - -[[package]] -name = "serde_with" -version = "3.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad483d2ab0149d5a5ebcd9972a3852711e0153d863bf5a5d0391d28883c4a20" -dependencies = [ - "base64 0.22.1", - "chrono", - "hex", - "indexmap 1.9.3", - "indexmap 2.2.6", - "serde", - "serde_derive", - "serde_json", - "serde_with_macros", - "time", -] - -[[package]] -name = "serde_with_macros" -version = "3.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65569b702f41443e8bc8bbb1c5779bd0450bbe723b56198980e80ec45780bce2" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "server_fn" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06e6e5467a2cd93ce1accfdfd8b859404f0b3b2041131ffd774fabf666b8219" -dependencies = [ - "axum", - "bytes", - "const_format", - "dashmap", - "futures", - "gloo-net 0.5.0", - "http 1.1.0", - "http-body-util", - "hyper 1.3.1", - "inventory", - "js-sys", - "once_cell", - "reqwest", - "send_wrapper", - "serde", - "serde_json", - "serde_qs", - "server_fn_macro_default", - "thiserror", - "tower", - "tower-layer", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-streams", - "web-sys", - "xxhash-rust", -] - -[[package]] -name = "server_fn_macro" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09c216bb1c1ac890151397643c663c875a1836adf0b269be4e389cb1b48c173c" -dependencies = [ - "const_format", - "convert_case 0.6.0", - "proc-macro2", - "quote", - "syn 2.0.66", - "xxhash-rust", -] - -[[package]] -name = "server_fn_macro_default" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00783df297ec85ea605779f2fef9cbec98981dffe2e01e1a9845c102ee1f1ae6" -dependencies = [ - "server_fn_macro", - "syn 2.0.66", -] - -[[package]] -name = "servo_arc" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432" -dependencies = [ - "nodrop", - "stable_deref_trait", -] - -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha2" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "signal-hook" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" -dependencies = [ - "libc", - "signal-hook-registry", -] - -[[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 = "signature" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" -dependencies = [ - "digest", - "rand_core 0.6.4", -] - -[[package]] -name = "simd-adler32" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" - -[[package]] -name = "siphasher" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" - -[[package]] -name = "slab" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] - -[[package]] -name = "sledgehammer_bindgen" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcfaf791ff02f48f3518ce825d32cf419c13a43c1d8b1232f74ac89f339c46d2" -dependencies = [ - "sledgehammer_bindgen_macro", - "wasm-bindgen", -] - -[[package]] -name = "sledgehammer_bindgen_macro" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdd941cc539bd3dc694edaf9d0c4e1221d02baa67c6b45ec04fad1024d9e8139" -dependencies = [ - "quote", - "syn 2.0.66", -] - -[[package]] -name = "sledgehammer_utils" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f20798defa0e9d4eff9ca451c7f84774c7378a9c3b5a40112cfa2b3eadb97ae2" -dependencies = [ - "lru", - "once_cell", - "rustc-hash", -] - -[[package]] -name = "smallvec" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" - -[[package]] -name = "socket2" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "soup3" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "471f924a40f31251afc77450e781cb26d55c0b650842efafc9c6cbd2f7cc4f9f" -dependencies = [ - "futures-channel", - "gio", - "glib", - "libc", - "soup3-sys", -] - -[[package]] -name = "soup3-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ebe8950a680a12f24f15ebe1bf70db7af98ad242d9db43596ad3108aab86c27" -dependencies = [ - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] - -[[package]] -name = "spinning" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d4f0e86297cad2658d92a707320d87bf4e6ae1050287f51d19b67ef3f153a7b" -dependencies = [ - "lock_api", -] - -[[package]] -name = "spki" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" -dependencies = [ - "base64ct", - "der", -] - -[[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 = "string_cache" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" -dependencies = [ - "new_debug_unreachable", - "once_cell", - "parking_lot", - "phf_shared 0.10.0", - "precomputed-hash", - "serde", -] - -[[package]] -name = "string_cache_codegen" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" -dependencies = [ - "phf_generator 0.10.0", - "phf_shared 0.10.0", - "proc-macro2", - "quote", -] - -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - -[[package]] -name = "subtle" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" - -[[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.66" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "sync_wrapper" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - -[[package]] -name = "sync_wrapper" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" - -[[package]] -name = "system-configuration" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" -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 0.5.0", - "pkg-config", - "toml", - "version-compare", -] - -[[package]] -name = "tao" -version = "0.26.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69ebbccb78deb5a36744c079eea2981b4a48ecbbe6b1b2ffbaa528bea3f5e5db" -dependencies = [ - "bitflags 1.3.2", - "cocoa", - "core-foundation", - "core-graphics", - "crossbeam-channel", - "dispatch", - "dlopen2", - "gdkwayland-sys", - "gdkx11-sys", - "gtk", - "instant", - "jni", - "lazy_static", - "libc", - "log", - "ndk", - "ndk-context", - "ndk-sys", - "objc", - "once_cell", - "parking_lot", - "raw-window-handle 0.5.2", - "raw-window-handle 0.6.2", - "scopeguard", - "tao-macros", - "unicode-segmentation", - "url", - "windows 0.54.0", - "windows-version", - "x11-dl", -] - -[[package]] -name = "tao-macros" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec114582505d158b669b136e6851f85840c109819d77c42bb7c0709f727d18c2" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "target-lexicon" -version = "0.12.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" - -[[package]] -name = "tempfile" -version = "3.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" -dependencies = [ - "cfg-if", - "fastrand", - "rustix", - "windows-sys 0.52.0", -] - -[[package]] -name = "tendril" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" -dependencies = [ - "futf", - "mac", - "utf-8", -] - -[[package]] -name = "thin-slice" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" - -[[package]] -name = "thiserror" -version = "1.0.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "thread_local" -version = "1.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" -dependencies = [ - "cfg-if", - "once_cell", -] - -[[package]] -name = "time" -version = "0.3.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" -dependencies = [ - "deranged", - "itoa 1.0.11", - "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.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" -dependencies = [ - "num-conv", - "time-core", -] - -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "to_method" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7c4ceeeca15c8384bbc3e011dbd8fccb7f068a440b752b7d9b32ceb0ca0e2e8" - -[[package]] -name = "tokio" -version = "1.38.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" -dependencies = [ - "backtrace", - "bytes", - "libc", - "mio", - "num_cpus", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "windows-sys 0.48.0", -] - -[[package]] -name = "tokio-macros" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "tokio-rustls" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" -dependencies = [ - "rustls", - "tokio", -] - -[[package]] -name = "tokio-stream" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", - "tokio-util", -] - -[[package]] -name = "tokio-tungstenite" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" -dependencies = [ - "futures-util", - "log", - "tokio", - "tungstenite", -] - -[[package]] -name = "tokio-util" -version = "0.7.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "futures-util", - "hashbrown 0.14.5", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "toml" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit 0.20.2", -] - -[[package]] -name = "toml_datetime" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_edit" -version = "0.19.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" -dependencies = [ - "indexmap 2.2.6", - "toml_datetime", - "winnow", -] - -[[package]] -name = "toml_edit" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" -dependencies = [ - "indexmap 2.2.6", - "serde", - "serde_spanned", - "toml_datetime", - "winnow", -] - -[[package]] -name = "tower" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" -dependencies = [ - "futures-core", - "futures-util", - "pin-project", - "pin-project-lite", - "tokio", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-http" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" -dependencies = [ - "bitflags 2.5.0", - "bytes", - "futures-util", - "http 1.1.0", - "http-body 1.0.0", - "http-body-util", - "http-range-header", - "httpdate", - "mime", - "mime_guess", - "percent-encoding", - "pin-project-lite", - "tokio", - "tokio-util", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-layer" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" - -[[package]] -name = "tower-service" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" - -[[package]] -name = "tracing" -version = "0.1.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" -dependencies = [ - "log", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "tracing-core" -version = "0.1.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-futures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" -dependencies = [ - "pin-project", - "tracing", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" -dependencies = [ - "nu-ansi-term", - "sharded-slab", - "smallvec", - "thread_local", - "tracing-core", - "tracing-log", -] - -[[package]] -name = "tracing-wasm" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4575c663a174420fa2d78f4108ff68f65bf2fbb7dd89f33749b6e826b3626e07" -dependencies = [ - "tracing", - "tracing-subscriber", - "wasm-bindgen", -] - -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - -[[package]] -name = "tungstenite" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" -dependencies = [ - "byteorder", - "bytes", - "data-encoding", - "http 1.1.0", - "httparse", - "log", - "rand 0.8.5", - "sha1", - "thiserror", - "url", - "utf-8", -] - -[[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", -] - -[[package]] -name = "unicase" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" -dependencies = [ - "version_check", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" - -[[package]] -name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "unicode-normalization" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-segmentation" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" - -[[package]] -name = "unicode-xid" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "url" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" -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 = "uuid" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" -dependencies = [ - "getrandom 0.2.15", - "wasm-bindgen", -] - -[[package]] -name = "valuable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - -[[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.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[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 = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - -[[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.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn 2.0.66", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" - -[[package]] -name = "wasm-streams" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" -dependencies = [ - "futures-util", - "js-sys", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "web-sys" -version = "0.3.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webbrowser" -version = "0.8.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db67ae75a9405634f5882791678772c94ff5f16a66535aae186e26aa0841fc8b" -dependencies = [ - "core-foundation", - "home", - "jni", - "log", - "ndk-context", - "objc", - "raw-window-handle 0.5.2", - "url", - "web-sys", -] - -[[package]] -name = "webkit2gtk" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76b1bc1e54c581da1e9f179d0b38512ba358fb1af2d634a1affe42e37172361a" -dependencies = [ - "bitflags 1.3.2", - "cairo-rs", - "gdk", - "gdk-sys", - "gio", - "gio-sys", - "glib", - "glib-sys", - "gobject-sys", - "gtk", - "gtk-sys", - "javascriptcore-rs", - "libc", - "once_cell", - "soup3", - "webkit2gtk-sys", -] - -[[package]] -name = "webkit2gtk-sys" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62daa38afc514d1f8f12b8693d30d5993ff77ced33ce30cd04deebc267a6d57c" -dependencies = [ - "bitflags 1.3.2", - "cairo-sys-rs", - "gdk-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "gtk-sys", - "javascriptcore-rs-sys", - "libc", - "pkg-config", - "soup3-sys", - "system-deps", -] - -[[package]] -name = "webpki-roots" -version = "0.25.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" - -[[package]] -name = "webview2-com" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0ae9c7e420783826cf769d2c06ac9ba462f450eca5893bb8c6c6529a4e5dd33" -dependencies = [ - "webview2-com-macros", - "webview2-com-sys", - "windows 0.52.0", - "windows-core 0.52.0", - "windows-implement 0.52.0", - "windows-interface 0.52.0", -] - -[[package]] -name = "webview2-com-macros" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1345798ecd8122468840bcdf1b95e5dc6d2206c5e4b0eafa078d061f59c9bc" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "webview2-com-sys" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6ad85fceee6c42fa3d61239eba5a11401bf38407a849ed5ea1b407df08cca72" -dependencies = [ - "thiserror", - "windows 0.52.0", - "windows-core 0.52.0", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" -dependencies = [ - "windows-sys 0.52.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.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" -dependencies = [ - "windows-core 0.52.0", - "windows-implement 0.52.0", - "windows-interface 0.52.0", - "windows-targets 0.52.5", -] - -[[package]] -name = "windows" -version = "0.54.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49" -dependencies = [ - "windows-core 0.54.0", - "windows-implement 0.53.0", - "windows-interface 0.53.0", - "windows-targets 0.52.5", -] - -[[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.5", -] - -[[package]] -name = "windows-core" -version = "0.54.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65" -dependencies = [ - "windows-result", - "windows-targets 0.52.5", -] - -[[package]] -name = "windows-implement" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12168c33176773b86799be25e2a2ba07c7aab9968b37541f1094dbd7a60c8946" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "windows-implement" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942ac266be9249c84ca862f0a164a39533dc2f6f33dc98ec89c8da99b82ea0bd" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "windows-interface" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d8dc32e0095a7eeccebd0e3f09e9509365ecb3fc6ac4d6f5f14a3f6392942d1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "windows-interface" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da33557140a288fae4e1d5f8873aaf9eb6613a9cf82c3e070223ff177f598b60" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "windows-result" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "749f0da9cc72d82e600d8d2e44cadd0b9eedb9038f71a1c58556ac1c5791813b" -dependencies = [ - "windows-targets 0.52.5", -] - -[[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.5", -] - -[[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.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" -dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", - "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", -] - -[[package]] -name = "windows-version" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6998aa457c9ba8ff2fb9f13e9d2a930dabcea28f1d0ab94d687d8b3654844515" -dependencies = [ - "windows-targets 0.52.5", -] - -[[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.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" - -[[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.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" - -[[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.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" - -[[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.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" - -[[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.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" - -[[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.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" - -[[package]] -name = "winnow" -version = "0.5.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" -dependencies = [ - "memchr", -] - -[[package]] -name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - -[[package]] -name = "wry" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b717040ba9771fd88eb428c6ea6b555f8e734ff8534f02c13e8f10d97f5935e" -dependencies = [ - "base64 0.21.7", - "block", - "cfg_aliases", - "cocoa", - "core-graphics", - "crossbeam-channel", - "dunce", - "gdkx11", - "gtk", - "html5ever", - "http 0.2.12", - "javascriptcore-rs", - "jni", - "kuchikiki", - "libc", - "log", - "ndk", - "ndk-context", - "ndk-sys", - "objc", - "objc_id", - "once_cell", - "percent-encoding", - "raw-window-handle 0.6.2", - "serde", - "serde_json", - "sha2", - "soup3", - "tao-macros", - "thiserror", - "webkit2gtk", - "webkit2gtk-sys", - "webview2-com", - "windows 0.52.0", - "windows-implement 0.52.0", - "windows-version", - "x11-dl", -] - -[[package]] -name = "x11" -version = "2.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" -dependencies = [ - "libc", - "pkg-config", -] - -[[package]] -name = "x11-dl" -version = "2.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" -dependencies = [ - "libc", - "once_cell", - "pkg-config", -] - -[[package]] -name = "xdg-home" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21e5a325c3cb8398ad6cf859c1135b25dd29e186679cf2da7581d9679f63b38e" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "xxhash-rust" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927da81e25be1e1a2901d59b81b37dd2efd1fc9c9345a55007f09bf5a2d3ee03" - -[[package]] -name = "yazi" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c94451ac9513335b5e23d7a8a2b61a7102398b8cca5160829d313e84c9d98be1" - -[[package]] -name = "zbus" -version = "4.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b8e3d6ae3342792a6cc2340e4394334c7402f3d793b390d2c5494a4032b3030" -dependencies = [ - "async-broadcast", - "async-executor", - "async-fs", - "async-io", - "async-lock", - "async-process", - "async-recursion", - "async-task", - "async-trait", - "blocking", - "derivative", - "enumflags2", - "event-listener", - "futures-core", - "futures-sink", - "futures-util", - "hex", - "nix", - "ordered-stream", - "rand 0.8.5", - "serde", - "serde_repr", - "sha1", - "static_assertions", - "tracing", - "uds_windows", - "windows-sys 0.52.0", - "xdg-home", - "zbus_macros", - "zbus_names", - "zvariant", -] - -[[package]] -name = "zbus_macros" -version = "4.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7a3e850ff1e7217a3b7a07eba90d37fe9bb9e89a310f718afcde5885ca9b6d7" -dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro2", - "quote", - "regex", - "syn 1.0.109", - "zvariant_utils", -] - -[[package]] -name = "zbus_names" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" -dependencies = [ - "serde", - "static_assertions", - "zvariant", -] - -[[package]] -name = "zerocopy" -version = "0.7.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "zeroize" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" - -[[package]] -name = "zvariant" -version = "4.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e09e8be97d44eeab994d752f341e67b3b0d80512a8b315a0671d47232ef1b65" -dependencies = [ - "endi", - "enumflags2", - "serde", - "static_assertions", - "url", - "zvariant_derive", -] - -[[package]] -name = "zvariant_derive" -version = "4.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72a5857e2856435331636a9fbb415b09243df4521a267c5bedcd5289b4d5799e" -dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro2", - "quote", - "syn 1.0.109", - "zvariant_utils", -] - -[[package]] -name = "zvariant_utils" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00bedb16a193cc12451873fee2a1bc6550225acece0e36f333e68326c73c8172" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] diff --git a/examples/openid_connect_demo/Cargo.toml b/examples/openid_connect_demo/Cargo.toml deleted file mode 100644 index 72ae74dc98..0000000000 --- a/examples/openid_connect_demo/Cargo.toml +++ /dev/null @@ -1,36 +0,0 @@ -[package] -name = "openid_auth_demo" -version = "0.1.0" -edition = "2021" -publish = false - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -anyhow = "1.0.86" -console_error_panic_hook = "0.1" -dioxus = { path = "../../packages/dioxus", default_features = true, features = [ - "router", - "signals", -], version = "*" } -dioxus-logger = "0.5.1" -dioxus-sdk = { git = "https://github.com/Dioxuslabs/sdk", features = [ - "storage", -] } -form_urlencoded = "1.2.1" -log = "0.4" -openidconnect = "3.5.0" -serde = { version = "1.0.203", features = ["derive"] } -uuid = "1.8" - -[features] -default = ["web"] -server = ["dioxus/axum"] -web = ["dioxus/web"] -desktop = ["dioxus/desktop"] -fullstack = ["dioxus/fullstack"] - -# since we're using dioxus from local path, inform dioxus-sdk to use it as well -[patch.crates-io] -dioxus = { path = "../../packages/dioxus" } -dioxus-signals = { path = "../../packages/signals" } diff --git a/examples/openid_connect_demo/Dioxus.toml b/examples/openid_connect_demo/Dioxus.toml deleted file mode 100644 index 3a130e2ea3..0000000000 --- a/examples/openid_connect_demo/Dioxus.toml +++ /dev/null @@ -1,35 +0,0 @@ -[application] - -# dioxus project name -name = "OpenID Connect authentication demo" - -# default platform -# you can also use `dioxus serve/build --platform XXX` to use other platform -# value: web | desktop -default_platform = "web" - -# Web `build` & `serve` dist path -out_dir = "dist" - -# resource (static) file folder -asset_dir = "public" - -# hot reload by default -hot_reload = true - -[web.app] - -# HTML title tag content -title = "OpenID Connect authentication demo" - -[web.watcher] - -index_on_404 = true - -watch_path = ["src"] - -[application.plugins] - -available = true - -required = [] diff --git a/examples/openid_connect_demo/README.md b/examples/openid_connect_demo/README.md deleted file mode 100644 index 7b0d100fd9..0000000000 --- a/examples/openid_connect_demo/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# OpenID Connect example to show how to authenticate an user - -The environment variables in [`.cargo/config.toml`](./.cargo/config.toml) must be set in order for this example to work. - -Once they are set, you can run `dx serve --platform web` or `dx serve --platform desktop`. - -### Environment variables summary - -- `DIOXUS_FRONT_ISSUER_URL`: The openid-connect's issuer url -- `DIOXUS_FRONT_CLIENT_ID`: The openid-connect's client id -- `DIOXUS_FRONT_CLIENT_SECRET`: The openid-connect's client secret -- `DIOXUS_FRONT_URL`: The url the frontend is supposed to be running on, it could be for example `http://localhost:8080` diff --git a/examples/openid_connect_demo/src/constants.rs b/examples/openid_connect_demo/src/constants.rs deleted file mode 100644 index 0a0b4950a6..0000000000 --- a/examples/openid_connect_demo/src/constants.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub const DIOXUS_FRONT_AUTH_TOKEN: &str = "auth_token"; -pub const DIOXUS_FRONT_AUTH_REQUEST: &str = "auth_request"; diff --git a/examples/openid_connect_demo/src/main.rs b/examples/openid_connect_demo/src/main.rs deleted file mode 100644 index 8487758595..0000000000 --- a/examples/openid_connect_demo/src/main.rs +++ /dev/null @@ -1,36 +0,0 @@ -#![allow(non_snake_case)] -use dioxus::prelude::*; -use dioxus_logger::tracing::Level; -use router::Route; - -use crate::oidc::ClientState; -use crate::storage::{use_auth_request_provider, use_auth_token_provider}; - -pub(crate) mod constants; -pub(crate) mod model; -pub(crate) mod oidc; -pub(crate) mod props; -pub(crate) mod router; -pub(crate) mod storage; -pub(crate) mod views; - -pub static CLIENT: GlobalSignal = Signal::global(ClientState::default); - -pub static DIOXUS_FRONT_ISSUER_URL: &str = env!("DIOXUS_FRONT_ISSUER_URL"); -pub static DIOXUS_FRONT_CLIENT_ID: &str = env!("DIOXUS_FRONT_CLIENT_ID"); -pub static DIOXUS_FRONT_CLIENT_SECRET: &str = env!("DIOXUS_FRONT_CLIENT_SECRET"); -pub static DIOXUS_FRONT_URL: &str = env!("DIOXUS_FRONT_URL"); - -fn App() -> Element { - use_auth_request_provider(); - use_auth_token_provider(); - rsx! { Router:: {} } -} - -fn main() { - dioxus_logger::init(Level::DEBUG).expect("failed to init logger"); - dioxus_sdk::set_dir!(); - console_error_panic_hook::set_once(); - log::info!("starting app"); - launch(App); -} diff --git a/examples/openid_connect_demo/src/model/mod.rs b/examples/openid_connect_demo/src/model/mod.rs deleted file mode 100644 index 68a79f5c0c..0000000000 --- a/examples/openid_connect_demo/src/model/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub(crate) mod user; diff --git a/examples/openid_connect_demo/src/model/user.rs b/examples/openid_connect_demo/src/model/user.rs deleted file mode 100644 index b1005fdd24..0000000000 --- a/examples/openid_connect_demo/src/model/user.rs +++ /dev/null @@ -1,7 +0,0 @@ -use uuid::Uuid; - -#[derive(PartialEq)] -pub struct User { - pub id: Uuid, - pub name: String, -} diff --git a/examples/openid_connect_demo/src/oidc.rs b/examples/openid_connect_demo/src/oidc.rs deleted file mode 100644 index 2c20c4e49b..0000000000 --- a/examples/openid_connect_demo/src/oidc.rs +++ /dev/null @@ -1,127 +0,0 @@ -use anyhow::Result; -use openidconnect::{ - core::{CoreClient, CoreIdToken, CoreResponseType, CoreTokenResponse}, - reqwest::async_http_client, - url::Url, - AuthenticationFlow, AuthorizationCode, ClaimsVerificationError, ClientId, ClientSecret, - CsrfToken, IssuerUrl, LogoutRequest, Nonce, ProviderMetadataWithLogout, RedirectUrl, - RefreshToken, -}; -use serde::{Deserialize, Serialize}; - -use crate::{props::client::ClientProps, DIOXUS_FRONT_CLIENT_ID}; - -#[derive(Clone, Debug, Default)] -pub struct ClientState { - pub oidc_client: Option, -} - -/// State that holds the nonce and authorization url and the nonce generated to log in an user -#[derive(Clone, PartialEq, Deserialize, Serialize, Default)] -pub struct AuthRequestState { - pub auth_request: Option, -} - -#[derive(Clone, PartialEq, Deserialize, Serialize)] -pub struct AuthRequest { - pub nonce: Nonce, - pub authorize_url: String, -} - -/// State the tokens returned once the user is authenticated -#[derive(Debug, Deserialize, Serialize, Default, Clone)] -pub struct AuthTokenState { - /// Token used to identify the user - pub id_token: Option, - /// Token used to refresh the tokens if they expire - pub refresh_token: Option, -} - -impl PartialEq for AuthTokenState { - fn eq(&self, other: &Self) -> bool { - self.id_token == other.id_token - && self.refresh_token.as_ref().map(|t| t.secret().clone()) - == other.refresh_token.as_ref().map(|t| t.secret().clone()) - } -} - -pub fn email( - client: CoreClient, - id_token: CoreIdToken, - nonce: Nonce, -) -> Result { - match id_token.claims(&client.id_token_verifier(), &nonce) { - Ok(claims) => Ok(claims.clone().email().unwrap().to_string()), - Err(error) => Err(error), - } -} - -pub fn authorize_url(client: CoreClient) -> AuthRequest { - let (authorize_url, _csrf_state, nonce) = client - .authorize_url( - AuthenticationFlow::::AuthorizationCode, - CsrfToken::new_random, - Nonce::new_random, - ) - .add_scope(openidconnect::Scope::new("email".to_string())) - .add_scope(openidconnect::Scope::new("profile".to_string())) - .url(); - AuthRequest { - authorize_url: authorize_url.to_string(), - nonce, - } -} - -pub async fn init_provider_metadata() -> Result { - let issuer_url = IssuerUrl::new(crate::DIOXUS_FRONT_ISSUER_URL.to_string())?; - Ok(ProviderMetadataWithLogout::discover_async(issuer_url, async_http_client).await?) -} - -pub async fn init_oidc_client() -> Result<(ClientId, CoreClient)> { - let client_id = ClientId::new(crate::DIOXUS_FRONT_CLIENT_ID.to_string()); - let provider_metadata = init_provider_metadata().await?; - let client_secret = Some(ClientSecret::new( - crate::DIOXUS_FRONT_CLIENT_SECRET.to_string(), - )); - let redirect_url = RedirectUrl::new(format!("{}/login", crate::DIOXUS_FRONT_URL))?; - - Ok(( - client_id.clone(), - CoreClient::from_provider_metadata(provider_metadata, client_id, client_secret) - .set_redirect_uri(redirect_url), - )) -} - -///TODO: Add pkce_pacifier -pub async fn token_response(oidc_client: CoreClient, code: String) -> Result { - // let (pkce_challenge, pkce_verifier) = PkceCodeChallenge::new_random_sha256(); - Ok(oidc_client - .exchange_code(AuthorizationCode::new(code.clone())) - // .set_pkce_verifier(pkce_verifier) - .request_async(async_http_client) - .await?) -} - -pub async fn exchange_refresh_token( - oidc_client: CoreClient, - refresh_token: RefreshToken, -) -> Result { - Ok(oidc_client - .exchange_refresh_token(&refresh_token) - .request_async(async_http_client) - .await?) -} - -pub async fn log_out_url(id_token_hint: CoreIdToken) -> Result { - let provider_metadata = init_provider_metadata().await?; - let end_session_url = provider_metadata - .additional_metadata() - .clone() - .end_session_endpoint - .unwrap(); - let logout_request: LogoutRequest = LogoutRequest::from(end_session_url); - Ok(logout_request - .set_client_id(ClientId::new(DIOXUS_FRONT_CLIENT_ID.to_string())) - .set_id_token_hint(&id_token_hint) - .http_get_url()) -} diff --git a/examples/openid_connect_demo/src/props/client.rs b/examples/openid_connect_demo/src/props/client.rs deleted file mode 100644 index 2e02111e16..0000000000 --- a/examples/openid_connect_demo/src/props/client.rs +++ /dev/null @@ -1,20 +0,0 @@ -use dioxus::prelude::*; -use openidconnect::{core::CoreClient, ClientId}; - -#[derive(Props, Clone, Debug)] -pub struct ClientProps { - pub client: CoreClient, - pub client_id: ClientId, -} - -impl PartialEq for ClientProps { - fn eq(&self, other: &Self) -> bool { - self.client_id == other.client_id - } -} - -impl ClientProps { - pub fn new(client_id: ClientId, client: CoreClient) -> Self { - ClientProps { client_id, client } - } -} diff --git a/examples/openid_connect_demo/src/props/mod.rs b/examples/openid_connect_demo/src/props/mod.rs deleted file mode 100644 index 1d33131531..0000000000 --- a/examples/openid_connect_demo/src/props/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub(crate) mod client; diff --git a/examples/openid_connect_demo/src/router.rs b/examples/openid_connect_demo/src/router.rs deleted file mode 100644 index 8f4b1b36a6..0000000000 --- a/examples/openid_connect_demo/src/router.rs +++ /dev/null @@ -1,16 +0,0 @@ -use crate::views::{header::AuthHeader, home::Home, login::Login, not_found::NotFound}; -use dioxus::prelude::*; - -#[derive(Routable, Clone)] -pub enum Route { - #[layout(AuthHeader)] - #[route("/")] - Home {}, - - // https://dioxuslabs.com/learn/0.4/router/reference/routes#query-segments - #[route("/login?:query_string")] - Login { query_string: String }, - #[end_layout] - #[route("/:..route")] - NotFound { route: Vec }, -} diff --git a/examples/openid_connect_demo/src/storage.rs b/examples/openid_connect_demo/src/storage.rs deleted file mode 100644 index c34819b965..0000000000 --- a/examples/openid_connect_demo/src/storage.rs +++ /dev/null @@ -1,35 +0,0 @@ -use dioxus::prelude::*; -use dioxus_sdk::storage::*; - -use crate::{ - constants::{DIOXUS_FRONT_AUTH_REQUEST, DIOXUS_FRONT_AUTH_TOKEN}, - oidc::{AuthRequestState, AuthTokenState}, -}; - -pub fn use_auth_token_provider() { - let stored_token = - use_storage::(DIOXUS_FRONT_AUTH_TOKEN.to_owned(), AuthTokenState::default); - - use_context_provider(move || stored_token); -} - -pub fn use_auth_token() -> Signal { - use_context() -} - -pub fn use_auth_request_provider() { - let stored_req = use_storage::( - DIOXUS_FRONT_AUTH_REQUEST.to_owned(), - AuthRequestState::default, - ); - - use_context_provider(move || stored_req); -} - -pub fn use_auth_request() -> Signal { - use_context() -} - -pub fn auth_request() -> Signal { - consume_context() -} diff --git a/examples/openid_connect_demo/src/views/header.rs b/examples/openid_connect_demo/src/views/header.rs deleted file mode 100644 index 274a0d22f0..0000000000 --- a/examples/openid_connect_demo/src/views/header.rs +++ /dev/null @@ -1,221 +0,0 @@ -use crate::storage::{auth_request, use_auth_request, use_auth_token}; -use crate::{ - oidc::{ - authorize_url, email, exchange_refresh_token, init_oidc_client, log_out_url, - AuthRequestState, AuthTokenState, ClientState, - }, - props::client::ClientProps, - router::Route, - CLIENT, -}; -use anyhow::Result; -use dioxus::prelude::*; -use dioxus::router::prelude::{Link, Outlet}; -use openidconnect::{url::Url, OAuth2TokenResponse, TokenResponse}; - -#[component] -pub fn LogOut() -> Element { - let mut auth_token = use_auth_token(); - let log_out_url_state = use_signal(|| None::>>); - match auth_token().id_token { - Some(id_token) => match &*log_out_url_state.read() { - Some(log_out_url_result) => match log_out_url_result { - Some(uri) => match uri { - Ok(uri) => { - rsx! { - Link { - onclick: move |_| { - auth_token.take(); - }, - to: uri.to_string(), - "Log out" - } - } - } - Err(error) => { - rsx! { div { "Failed to load disconnection url: {error:?}" } } - } - }, - None => { - rsx! { div { "Loading... Please wait" } } - } - }, - None => { - let logout_url_task = move || { - spawn({ - let mut log_out_url_state = log_out_url_state.to_owned(); - async move { - let logout_url = log_out_url(id_token).await; - let logout_url_option = Some(logout_url); - log_out_url_state.set(Some(logout_url_option)); - } - }) - }; - logout_url_task(); - rsx! { div { "Loading log out url... Please wait" } } - } - }, - None => { - rsx! {{}} - } - } -} - -#[component] -pub fn RefreshToken(props: ClientProps) -> Element { - let mut auth_token = use_auth_token(); - match auth_token().refresh_token { - Some(refresh_token) => { - rsx! { div { - onmounted: { - move |_| { - let client = props.client.clone(); - let refresh_token = refresh_token.clone(); - async move { - let exchange_refresh_token = - exchange_refresh_token(client, refresh_token).await; - match exchange_refresh_token { - Ok(response_token) => { - auth_token.set(AuthTokenState { - id_token: response_token.id_token().cloned(), - refresh_token: response_token.refresh_token().cloned(), - }); - } - Err(_error) => { - auth_token.take(); - auth_request().take(); - } - } - } - } - }, - "Refreshing session, please wait" - } } - } - None => { - rsx! { div { "Id token expired and no refresh token found" } } - } - } -} - -#[component] -pub fn LoadClient() -> Element { - let init_client_future = use_resource(move || async move { init_oidc_client().await }); - match &*init_client_future.read_unchecked() { - Some(Ok((client_id, client))) => rsx! { - div { - onmounted: { - let client_id = client_id.clone(); - let client = client.clone(); - move |_| { - *CLIENT.write() = ClientState { - oidc_client: Some(ClientProps::new(client_id.clone(), client.clone())), - }; - } - }, - "Client successfully loaded" - } - Outlet:: {} - }, - Some(Err(error)) => { - log::info! {"Failed to load client: {:?}", error}; - rsx! { - div { "Failed to load client: {error:?}" } - Outlet:: {} - } - } - None => { - rsx! { - div { - div { "Loading client, please wait" } - Outlet:: {} - } - } - } - } -} - -#[component] -pub fn AuthHeader() -> Element { - let client = CLIENT.read().oidc_client.clone(); - let mut auth_request = use_auth_request(); - let auth_token = use_auth_token(); - match (client, auth_request(), auth_token()) { - // We have everything we need to attempt to authenticate the user - (Some(client_props), current_auth_request, current_auth_token) => { - match current_auth_request.auth_request { - Some(new_auth_request) => { - match current_auth_token.id_token { - Some(id_token) => { - match email( - client_props.client.clone(), - id_token.clone(), - new_auth_request.nonce.clone(), - ) { - Ok(email) => { - rsx! { - div { - div { {email} } - LogOut {} - Outlet:: {} - } - } - } - // Id token failed to be decoded - Err(error) => match error { - // Id token failed to be decoded because it expired, we refresh it - openidconnect::ClaimsVerificationError::Expired(_message) => { - log::info!("Token expired"); - rsx! { - div { - RefreshToken { client_id: client_props.client_id, client: client_props.client } - Outlet:: {} - } - } - } - // Other issue with token decoding - _ => { - log::info!("Other issue with token"); - rsx! { - div { - div { "{error}" } - Outlet:: {} - } - } - } - }, - } - } - // User is not logged in - None => { - rsx! { - div { - Link { to: new_auth_request.authorize_url.clone(), "Log in" } - Outlet:: {} - } - } - } - } - } - None => { - rsx! { div { - onmounted: { - let client = client_props.client; - move |_| { - let new_auth_request = authorize_url(client.clone()); - auth_request.set(AuthRequestState { - auth_request: Some(new_auth_request), - }); - } - }, - "Loading nonce" - } } - } - } - } - // Client is not initialized yet, we need it for everything - (None, _, _) => { - rsx! { LoadClient {} } - } - } -} diff --git a/examples/openid_connect_demo/src/views/home.rs b/examples/openid_connect_demo/src/views/home.rs deleted file mode 100644 index d91b5c2037..0000000000 --- a/examples/openid_connect_demo/src/views/home.rs +++ /dev/null @@ -1,5 +0,0 @@ -use dioxus::prelude::*; - -pub fn Home() -> Element { - rsx! { div { "Hello world" } } -} diff --git a/examples/openid_connect_demo/src/views/login.rs b/examples/openid_connect_demo/src/views/login.rs deleted file mode 100644 index e3a4e0025b..0000000000 --- a/examples/openid_connect_demo/src/views/login.rs +++ /dev/null @@ -1,86 +0,0 @@ -use crate::{ - oidc::{token_response, AuthTokenState}, - router::Route, - storage::{auth_request, use_auth_token}, - CLIENT, -}; -use dioxus::prelude::*; -use dioxus::router::prelude::Link; -use openidconnect::{OAuth2TokenResponse, TokenResponse}; - -#[component] -pub fn Login(query_string: String) -> Element { - let client = CLIENT.read().oidc_client.clone(); - let mut auth_token = use_auth_token(); - let current_auth_token = auth_token(); - match client { - Some(client_props) => { - match ( - current_auth_token.id_token, - current_auth_token.refresh_token, - ) { - (Some(_id_token), Some(_refresh_token)) => { - rsx! { - div { "Sign in successful" } - Link { to: Route::Home {}, "Go back home" } - } - } - // If the refresh token is set but not the id_token, there was an error, we just go back home and reset their value - (None, Some(_)) | (Some(_), None) => { - rsx! { - div { "Error while attempting to log in" } - Link { - to: Route::Home {}, - onclick: move |_| { - auth_token.take(); - auth_request().take(); - }, - "Go back home" - } - } - } - (None, None) => { - let mut query_pairs = form_urlencoded::parse(query_string.as_bytes()); - let code_pair = query_pairs.find(|(key, _value)| key == "code"); - match code_pair { - Some((_key, code)) => { - let code = code.to_string(); - rsx! { div { - onmounted: { - move |_| { - let auth_code = code.to_string(); - let client_props = client_props.clone(); - async move { - let token_response_result = - token_response(client_props.client, auth_code).await; - match token_response_result { - Ok(token_response) => { - let id_token = token_response.id_token().unwrap(); - auth_token.set(AuthTokenState { - id_token: Some(id_token.clone()), - refresh_token: token_response - .refresh_token() - .cloned(), - }); - } - Err(error) => { - log::warn! {"{error}"}; - } - } - } - } - } - }} - } - None => { - rsx! { div { "No code provided" } } - } - } - } - } - } - _ => { - rsx! {{}} - } - } -} diff --git a/examples/openid_connect_demo/src/views/mod.rs b/examples/openid_connect_demo/src/views/mod.rs deleted file mode 100644 index 1b8e08a585..0000000000 --- a/examples/openid_connect_demo/src/views/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub(crate) mod header; -pub(crate) mod home; -pub(crate) mod login; -pub(crate) mod not_found; diff --git a/examples/openid_connect_demo/src/views/not_found.rs b/examples/openid_connect_demo/src/views/not_found.rs deleted file mode 100644 index 833c610674..0000000000 --- a/examples/openid_connect_demo/src/views/not_found.rs +++ /dev/null @@ -1,10 +0,0 @@ -use dioxus::prelude::*; - -#[component] -pub fn NotFound(route: Vec) -> Element { - rsx! { - div{ - {route.join("")} - } - } -} diff --git a/packages/cli-config/build.rs b/packages/cli-config/build.rs index 7186269b08..35a7ffaa56 100644 --- a/packages/cli-config/build.rs +++ b/packages/cli-config/build.rs @@ -1,13 +1,4 @@ -// warn if the "read-config" feature is enabled, but the DIOXUS_CONFIG environment variable is not set -// This means that some library is trying to access the crate's configuration, but the dioxus CLI was not used to build the application. fn main() { built::write_built_file().expect("Failed to acquire build-time information"); - - // println!("cargo:rerun-if-env-changed=DIOXUS_CONFIG"); - // let dioxus_config = std::env::var("DIOXUS_CONFIG"); - // let built_with_dioxus = dioxus_config.is_ok(); - // if cfg!(feature = "read-config") && !built_with_dioxus { - // println!("cargo:warning=A library is trying to access the crate's configuration, but the dioxus CLI was not used to build the application. Information about the Dioxus CLI is available at https://dioxuslabs.com/learn/0.5/CLI/installation"); - // } } diff --git a/packages/cli-config/src/lib.rs b/packages/cli-config/src/lib.rs index 36b588d76a..1578b88e89 100644 --- a/packages/cli-config/src/lib.rs +++ b/packages/cli-config/src/lib.rs @@ -13,49 +13,6 @@ pub use serve::*; mod build_info; -#[doc(hidden)] -pub mod __private { - use crate::DioxusConfig; - - pub(crate) const DIOXUS_CLI_VERSION: &str = "DIOXUS_CLI_VERSION"; - pub(crate) const CONFIG_ENV: &str = "DIOXUS_CONFIG"; - pub(crate) const CONFIG_BASE_PATH_ENV: &str = "DIOXUS_CONFIG_BASE_PATH"; - - pub fn env_args() -> Vec<(&'static str, &'static str)> { - vec![ - (CONFIG_ENV, "DIOXUS_CONFIG"), - (CONFIG_BASE_PATH_ENV, "DIOXUS_CONFIG_BASE_PATH"), - (DIOXUS_CLI_VERSION, "DIOXUS_CLI_VERSION"), - ] - } - - // pub fn save_config(config: &DioxusConfig, cli_version: &str) -> CrateConfigDropGuard { - // std::env::set_var(CONFIG_ENV, serde_json::to_string(config).unwrap()); - // std::env::set_var( - // CONFIG_BASE_PATH_ENV, - // config.web.app.base_path.clone().unwrap_or_default(), - // ); - // std::env::set_var(DIOXUS_CLI_VERSION, cli_version); - // CrateConfigDropGuard - // } - - // /// A guard that removes the config from the environment when dropped. - // pub struct CrateConfigDropGuard; - - // impl Drop for CrateConfigDropGuard { - // fn drop(&mut self) { - // std::env::remove_var(CONFIG_ENV); - // std::env::remove_var(CONFIG_BASE_PATH_ENV); - // std::env::remove_var(DIOXUS_CLI_VERSION); - // } - // } - - #[cfg(feature = "read-config")] - /// The environment variable that stores the CLIs serve configuration. - /// We use this to communicate between the CLI and the server for fullstack applications. - pub const SERVE_ENV: &str = "DIOXUS_SERVE_CONFIG"; -} - /// An error that occurs when the dioxus CLI was not used to build the application. #[derive(Debug)] pub struct DioxusCLINotUsed; @@ -73,37 +30,45 @@ impl std::error::Error for DioxusCLINotUsed {} pub static CURRENT_CONFIG: once_cell::sync::Lazy< Result, > = once_cell::sync::Lazy::new(|| { + // todo: somehow support wasm? std::env::var("DIOXUS_CONFIG") .ok() .and_then(|config| serde_json::from_str(&config).ok()) .ok_or_else(|| { + // let mut cli_version = crate::build_info::PKG_VERSION.to_string(); + + // if let Some(hash) = crate::build_info::GIT_COMMIT_HASH_SHORT { + // let hash = &hash.trim_start_matches('g')[..4]; + // cli_version.push_str(&format!("-{hash}")); + // } + + // tracing::warn!("Failed to parse the CLI config file. This is likely caused by a mismatch between the version of the CLI and the dioxus version.\nCLI version: {cli_version}\nDioxus version: {dioxus_version}\nSerialization error: {err}"); + // Err(DioxusCLINotUsed) + // #[doc(hidden)] + // pub mod __private { + // use crate::DioxusConfig; + + // pub(crate) const DIOXUS_CLI_VERSION: &str = "DIOXUS_CLI_VERSION"; + // pub(crate) const CONFIG_ENV: &str = "DIOXUS_CONFIG"; + // pub(crate) const CONFIG_BASE_PATH_ENV: &str = "DIOXUS_CONFIG_BASE_PATH"; + + // pub fn env_args() -> Vec<(&'static str, &'static str)> { + // vec![ + // (CONFIG_ENV, "DIOXUS_CONFIG"), + // (CONFIG_BASE_PATH_ENV, "DIOXUS_CONFIG_BASE_PATH"), + // (DIOXUS_CLI_VERSION, "DIOXUS_CLI_VERSION"), + // ] + // } + + // #[cfg(feature = "read-config")] + // /// The environment variable that stores the CLIs serve configuration. + // /// We use this to communicate between the CLI and the server for fullstack applications. + // pub const SERVE_ENV: &str = "DIOXUS_SERVE_CONFIG"; + // } + tracing::warn!("A library is trying to access the crate's configuration, but the dioxus CLI was not used to build the application."); DioxusCLINotUsed }) - - // CURRENT_CONFIG_JSON - // .ok_or_else(|| { - // tracing::warn!("A library is trying to access the crate's configuration, but the dioxus CLI was not used to build the application."); - // DioxusCLINotUsed - // }).and_then( - // |config| - // match serde_json::from_str(config) { - // Ok(config) => Ok(config), - // Err(err) => { - // let mut cli_version = crate::build_info::PKG_VERSION.to_string(); - - // if let Some(hash) = crate::build_info::GIT_COMMIT_HASH_SHORT { - // let hash = &hash.trim_start_matches('g')[..4]; - // cli_version.push_str(&format!("-{hash}")); - // } - - // let dioxus_version = std::option_env!("DIOXUS_CLI_VERSION").unwrap_or("unknown"); - - // tracing::warn!("Failed to parse the CLI config file. This is likely caused by a mismatch between the version of the CLI and the dioxus version.\nCLI version: {cli_version}\nDioxus version: {dioxus_version}\nSerialization error: {err}"); - // Err(DioxusCLINotUsed) - // } - // } - // ) }); /// Get the "base path" from the Dioxus.toml file @@ -112,11 +77,3 @@ pub static CURRENT_CONFIG: once_cell::sync::Lazy< pub fn base_path() -> Option<&'static str> { todo!() } - -// #[cfg(feature = "read-config")] -// /// The current crate's configuration. -// pub const CURRENT_CONFIG_JSON: Option<&str> = std::option_env!("DIOXUS_CONFIG"); - -// #[cfg(feature = "read-config")] -// /// The current crate's configuration. -// pub const BASE_PATH: Option<&str> = std::option_env!("DIOXUS_CONFIG_BASE_PATH"); diff --git a/packages/desktop/build.rs b/packages/desktop/build.rs index b08ad66cf5..0ee33fa4cc 100644 --- a/packages/desktop/build.rs +++ b/packages/desktop/build.rs @@ -1,5 +1,12 @@ use std::{io::Write as _, path::PathBuf}; + +fn main() { + check_gnu(); + + maybe_copy_doc_scrope(); +} + fn check_gnu() { // WARN about wry support on windows gnu targets. GNU windows targets don't work well in wry currently if std::env::var("CARGO_CFG_WINDOWS").is_ok() @@ -8,7 +15,9 @@ fn check_gnu() { { println!("cargo:warning=GNU windows targets have some limitations within Wry. Using the MSVC windows toolchain is recommended. If you would like to use continue using GNU, you can read https://github.com/wravery/webview2-rs#cross-compilation and disable this warning by adding the gnu feature to dioxus-desktop in your Cargo.toml") } +} +fn maybe_copy_doc_scrope() { // To prepare for a release, we add extra examples to desktop for doc scraping and copy assets from the workspace to make those examples compile if option_env!("DIOXUS_RELEASE").is_some() { // Append EXAMPLES_TOML to the cargo.toml @@ -36,9 +45,6 @@ fn check_gnu() { } } -fn main() { - check_gnu(); -} const EXAMPLES_TOML: &str = r#" # Most of the examples live in the workspace. We include some here so that docs.rs can scrape our examples for better inline docs From b661c3d1f85c762be7df6d42d77f5b12edd2f680 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 16 Aug 2024 22:48:32 -0700 Subject: [PATCH 010/139] nuke all the things that cause cache thrashing --- .cargo/config.toml | 5 -- .vscode/settings.json | 8 +- Cargo.toml | 3 +- examples/tailwind/src/main.rs | 3 +- packages/cli-config/src/serve.rs | 7 +- packages/cli/src/assets.rs | 84 +++++++------------ packages/cli/src/builder/cargo.rs | 13 ++- packages/cli/src/builder/mod.rs | 8 +- packages/core/src/nodes.rs | 11 ++- packages/desktop/src/protocol.rs | 48 +++++------ packages/dioxus/build.rs | 76 ++++++++--------- packages/dioxus/src/lib.rs | 2 +- .../fullstack/examples/desktop/src/main.rs | 2 +- .../fullstack/examples/hackernews/src/main.rs | 2 +- 14 files changed, 125 insertions(+), 147 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index b9d1ed3471..c24450e626 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,7 +1,2 @@ -# All of these variables are used in the `openid_connect_demo` example, they are set here for the -# CI to work, they are set here because as stated here for now: -# `https://doc.rust-lang.org/cargo/reference/config.html` -# the .cargo/config.toml of the inner workspaces are not read when being invoked from the root workspace. - [profile.dev.package."*"] opt-level = 3 diff --git a/.vscode/settings.json b/.vscode/settings.json index 319a969432..567f72b45f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,10 +4,10 @@ "editor.formatOnSave": false }, // "rust-analyzer.check.workspace": true, - "rust-analyzer.check.workspace": false, - "rust-analyzer.check.features": "all", - "rust-analyzer.cargo.features": "all", - "rust-analyzer.check.allTargets": true, + // "rust-analyzer.check.workspace": false, + // "rust-analyzer.check.features": "all", + // "rust-analyzer.cargo.features": "all", + // "rust-analyzer.check.allTargets": true, // we don't want the formatter to kick in while we're working on dioxus itself "dioxus.formatOnSave": "disabled", } diff --git a/Cargo.toml b/Cargo.toml index 8193b76089..9947d68c30 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -94,11 +94,12 @@ lazy-js-bundle = { path = "packages/lazy-js-bundle", version = "0.6.0-alpha.1" } manganis-cli-support = { version = "0.3.0-alpha.0", features = ["html"], path = "../ecosystem-dioxus/manganis/packages/cli-support" } manganis = { version = "0.3.0-alpha.0", default-features = false, features = ["macro"], path = "../ecosystem-dioxus/manganis/packages/manganis" } +warnings = { version = "0.2.0" } + # manganis-cli-support = { version = "0.3.0-alpha.0", features = ["html"] } # manganis = { version = "0.3.0-alpha.0", default-features = false } # manganis-cli-support = { version = "0.3.0-alpha.1", features = ["html"] } # manganis = { version = "0.3.0-alpha.1", default-features = false, features = ["html", "macro"]} -warnings = { version = "0.2.0" } # a fork of pretty please for tests diff --git a/examples/tailwind/src/main.rs b/examples/tailwind/src/main.rs index 70ce484c38..20ac467d68 100644 --- a/examples/tailwind/src/main.rs +++ b/examples/tailwind/src/main.rs @@ -2,8 +2,6 @@ use dioxus::prelude::*; -const _STYLE: &str = asset!("public/tailwind.css"); - fn main() { launch(app); } @@ -11,6 +9,7 @@ fn main() { pub fn app() -> Element { let grey_background = true; rsx!( + Stylesheet { href: asset!("/public/tailwind.css") } div { header { class: "text-gray-400 body-font", diff --git a/packages/cli-config/src/serve.rs b/packages/cli-config/src/serve.rs index a8894c72a5..6a29d45c56 100644 --- a/packages/cli-config/src/serve.rs +++ b/packages/cli-config/src/serve.rs @@ -58,9 +58,10 @@ impl RuntimeCLIArguments { /// Attempt to read the current serve settings from the CLI. This will only be set for the fullstack platform on recent versions of the CLI. pub fn from_cli() -> Option { - std::env::var(crate::__private::SERVE_ENV) - .ok() - .and_then(|json| serde_json::from_str(&json).ok()) + None + // std::env::var(crate::__private::SERVE_ENV) + // .ok() + // .and_then(|json| serde_json::from_str(&json).ok()) } /// Get the address the server should run on diff --git a/packages/cli/src/assets.rs b/packages/cli/src/assets.rs index d08d9eb6f5..39555bb3b5 100644 --- a/packages/cli/src/assets.rs +++ b/packages/cli/src/assets.rs @@ -54,36 +54,36 @@ pub(crate) fn process_assets( assets.par_iter().try_for_each_init( || progress.clone(), move |progress, asset| { - if let AssetType::File(file_asset) = asset { - match process_file(file_asset, &static_asset_output_dir) { - Ok(_) => { - // Update the progress - _ = progress.start_send(UpdateBuildProgress { - stage: Stage::OptimizingAssets, - update: UpdateStage::AddMessage(BuildMessage { - level: Level::INFO, - message: MessageType::Text(format!( - "Optimized static asset {}", - file_asset - )), - source: MessageSource::Build, - }), - }); - let assets_finished = - assets_finished.fetch_add(1, std::sync::atomic::Ordering::SeqCst); - _ = progress.start_send(UpdateBuildProgress { - stage: Stage::OptimizingAssets, - update: UpdateStage::SetProgress( - assets_finished as f64 / asset_count as f64, - ), - }); - } - Err(err) => { - tracing::error!("Failed to copy static asset: {}", err); - return Err(err); - } - } - } + // if let AssetType::File(file_asset) = asset { + // match process_file(file_asset, &static_asset_output_dir) { + // Ok(_) => { + // // Update the progress + // _ = progress.start_send(UpdateBuildProgress { + // stage: Stage::OptimizingAssets, + // update: UpdateStage::AddMessage(BuildMessage { + // level: Level::INFO, + // message: MessageType::Text(format!( + // "Optimized static asset {}", + // file_asset + // )), + // source: MessageSource::Build, + // }), + // }); + // let assets_finished = + // assets_finished.fetch_add(1, std::sync::atomic::Ordering::SeqCst); + // _ = progress.start_send(UpdateBuildProgress { + // stage: Stage::OptimizingAssets, + // update: UpdateStage::SetProgress( + // assets_finished as f64 / asset_count as f64, + // ), + // }); + // } + // Err(err) => { + // tracing::error!("Failed to copy static asset: {}", err); + // return Err(err); + // } + // } + // } Ok::<(), anyhow::Error>(()) }, )?; @@ -91,30 +91,6 @@ pub(crate) fn process_assets( Ok(()) } -/// A guard that sets up the environment for the web renderer to compile in. This guard sets the location that assets will be served from -pub(crate) struct AssetConfigDropGuard; - -impl AssetConfigDropGuard { - pub fn new(base_path: Option<&str>) -> Self { - // Set up the collect asset config - let base = match base_path { - Some(base) => format!("/{}/", base.trim_matches('/')), - None => "/".to_string(), - }; - manganis_cli_support::Config::default() - .with_assets_serve_location(base) - .save(); - Self {} - } -} - -impl Drop for AssetConfigDropGuard { - fn drop(&mut self) { - // Reset the config - manganis_cli_support::Config::default().save(); - } -} - pub(crate) fn copy_dir_to( src_dir: PathBuf, dest_dir: PathBuf, diff --git a/packages/cli/src/builder/cargo.rs b/packages/cli/src/builder/cargo.rs index cc157838b9..d2f627b471 100644 --- a/packages/cli/src/builder/cargo.rs +++ b/packages/cli/src/builder/cargo.rs @@ -4,7 +4,7 @@ use super::BuildResult; use super::TargetPlatform; use crate::assets::copy_dir_to; use crate::assets::create_assets_head; -use crate::assets::{asset_manifest, process_assets, AssetConfigDropGuard}; +use crate::assets::{asset_manifest, process_assets}; use crate::builder::progress::build_cargo; use crate::builder::progress::CargoBuildResult; use crate::builder::progress::Stage; @@ -108,13 +108,10 @@ impl BuildRequest { let hash = &hash.trim_start_matches('g')[..4]; dioxus_version.push_str(&format!("-{hash}")); } - // let _guard = dioxus_cli_config::__private::save_config( - // &self.dioxus_crate.dioxus_config, - // &dioxus_version, - // ); - // let _manganis_support = ManganisSupportGuard::default(); - let _asset_guard = - AssetConfigDropGuard::new(self.dioxus_crate.dioxus_config.web.app.base_path.as_deref()); + + // todo: put the base path here? + // let _asset_guard = + // AssetConfigDropGuard::new(self.dioxus_crate.dioxus_config.web.app.base_path.as_deref()); // If this is a web, build make sure we have the web build tooling set up if self.targeting_web() { diff --git a/packages/cli/src/builder/mod.rs b/packages/cli/src/builder/mod.rs index c6c84f67b7..61b059cc19 100644 --- a/packages/cli/src/builder/mod.rs +++ b/packages/cli/src/builder/mod.rs @@ -164,10 +164,10 @@ impl BuildResult { let mut cmd = Command::new(executable); cmd // When building the fullstack server, we need to forward the serve arguments (like port) to the fullstack server through env vars - .env( - dioxus_cli_config::__private::SERVE_ENV, - serde_json::to_string(&arguments).unwrap(), - ) + // .env( + // dioxus_cli_config::__private::SERVE_ENV, + // serde_json::to_string(&arguments).unwrap(), + // ) .stderr(Stdio::piped()) .stdout(Stdio::piped()) .kill_on_drop(true) diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index aec3c08ca1..dadc10105e 100644 --- a/packages/core/src/nodes.rs +++ b/packages/core/src/nodes.rs @@ -1081,7 +1081,16 @@ impl IntoAttributeValue for Option { #[cfg(feature = "manganis")] impl IntoAttributeValue for manganis::ImageAsset { fn into_value(self) -> AttributeValue { - AttributeValue::Text(self.path().to_string()) + todo!() + // AttributeValue::Text(self.path().to_string()) + } +} + +#[cfg(feature = "manganis")] +impl IntoAttributeValue for manganis::Asset { + fn into_value(self) -> AttributeValue { + todo!() + // AttributeValue::Text(self.path().to_string()) } } diff --git a/packages/desktop/src/protocol.rs b/packages/desktop/src/protocol.rs index 87549dbbf9..0749eee888 100644 --- a/packages/desktop/src/protocol.rs +++ b/packages/desktop/src/protocol.rs @@ -126,30 +126,30 @@ fn running_in_dev_mode() -> bool { fn resolve_resource(path: &Path) -> PathBuf { let mut base_path = get_asset_root_or_default(); - // Even in dev mode, mobile needs to resolve the asset path from the bundle - if running_in_dev_mode() || cfg!(any(target_os = "ios", target_os = "android")) { - base_path.push(path); - // Special handler for Manganis filesystem fallback. - // We need this since Manganis provides assets from workspace root. - if !base_path.exists() { - let workspace_root = get_workspace_root_from_cargo(); - let asset_path = workspace_root.join(path); - return asset_path; - } - } else { - let mut resource_path = PathBuf::new(); - for component in path.components() { - // Tauri-bundle inserts special path segments for abnormal component paths - match component { - Component::Prefix(_) => {} - Component::RootDir => resource_path.push("_root_"), - Component::CurDir => {} - Component::ParentDir => resource_path.push("_up_"), - Component::Normal(p) => resource_path.push(p), - } - } - base_path.push(resource_path); - } + // // Even in dev mode, mobile needs to resolve the asset path from the bundle + // if running_in_dev_mode() || cfg!(any(target_os = "ios", target_os = "android")) { + // base_path.push(path); + // // Special handler for Manganis filesystem fallback. + // // We need this since Manganis provides assets from workspace root. + // if !base_path.exists() { + // let workspace_root = get_workspace_root_from_cargo(); + // let asset_path = workspace_root.join(path); + // return asset_path; + // } + // } else { + // let mut resource_path = PathBuf::new(); + // for component in path.components() { + // // Tauri-bundle inserts special path segments for abnormal component paths + // match component { + // Component::Prefix(_) => {} + // Component::RootDir => resource_path.push("_root_"), + // Component::CurDir => {} + // Component::ParentDir => resource_path.push("_up_"), + // Component::Normal(p) => resource_path.push(p), + // } + // } + // base_path.push(resource_path); + // } base_path } diff --git a/packages/dioxus/build.rs b/packages/dioxus/build.rs index 48ffdf467a..59c348665c 100644 --- a/packages/dioxus/build.rs +++ b/packages/dioxus/build.rs @@ -1,46 +1,46 @@ fn main() { // Warn the user if they enabled the launch feature without any renderers - if feature_enabled("launch") { - if feature_enabled("third-party-renderer") { - return; - } + // if feature_enabled("launch") { + // if feature_enabled("third-party-renderer") { + // return; + // } - let liveview_renderers = ["liveview", "server"]; - let fullstack_renderers = ["server"]; - let client_renderers = ["desktop", "mobile", "web"]; - let client_renderer_selected = client_renderers - .iter() - .any(|renderer| feature_enabled(renderer)); - if feature_enabled("fullstack") { - let server_fullstack_enabled = fullstack_renderers - .iter() - .any(|renderer| feature_enabled(renderer)); - if !server_fullstack_enabled && !client_renderer_selected { - println!("cargo:warning=You have enabled the launch and fullstack features, but have not enabled any renderers. The application will not be able to launch. Try enabling one of the following renderers: {} for the server or one of the following renderers: {} for the client.", fullstack_renderers.join(", "), client_renderers.join(", ")); - } - } + // let liveview_renderers = ["liveview", "server"]; + // let fullstack_renderers = ["server"]; + // let client_renderers = ["desktop", "mobile", "web"]; + // let client_renderer_selected = client_renderers + // .iter() + // .any(|renderer| feature_enabled(renderer)); + // if feature_enabled("fullstack") { + // let server_fullstack_enabled = fullstack_renderers + // .iter() + // .any(|renderer| feature_enabled(renderer)); + // if !server_fullstack_enabled && !client_renderer_selected { + // println!("cargo:warning=You have enabled the launch and fullstack features, but have not enabled any renderers. The application will not be able to launch. Try enabling one of the following renderers: {} for the server or one of the following renderers: {} for the client.", fullstack_renderers.join(", "), client_renderers.join(", ")); + // } + // } - if feature_enabled("liveview") { - let server_selected = liveview_renderers - .iter() - .any(|renderer| feature_enabled(renderer)); - if !server_selected { - println!("cargo:warning=You have enabled the launch and liveview features, but have not enabled any liveview renderers. The application will not be able to launch. Try enabling one of the following renderers: {}", liveview_renderers.join(", ")); - } - } + // if feature_enabled("liveview") { + // let server_selected = liveview_renderers + // .iter() + // .any(|renderer| feature_enabled(renderer)); + // if !server_selected { + // println!("cargo:warning=You have enabled the launch and liveview features, but have not enabled any liveview renderers. The application will not be able to launch. Try enabling one of the following renderers: {}", liveview_renderers.join(", ")); + // } + // } - if !client_renderer_selected { - println!("cargo:warning=You have enabled the launch feature, but have not enabled any client renderers. The application will not be able to launch. Try enabling one of the following renderers: {}, fullstack or liveview", client_renderers.join(", ")); - } - } + // if !client_renderer_selected { + // println!("cargo:warning=You have enabled the launch feature, but have not enabled any client renderers. The application will not be able to launch. Try enabling one of the following renderers: {}, fullstack or liveview", client_renderers.join(", ")); + // } + // } - if feature_enabled("axum") { - println!("cargo:warning=The axum feature has been renamed to server and will be removed in a future release. Please update your code to use server feature instead."); - } + // if feature_enabled("axum") { + // println!("cargo:warning=The axum feature has been renamed to server and will be removed in a future release. Please update your code to use server feature instead."); + // } } -fn feature_enabled(feature: &str) -> bool { - let feature = "CARGO_FEATURE_".to_owned() + &feature.to_uppercase().replace('-', "_"); - println!("cargo:rerun-if-env-changed={}", feature); - std::env::var(feature).is_ok() -} +// fn feature_enabled(feature: &str) -> bool { +// let feature = "CARGO_FEATURE_".to_owned() + &feature.to_uppercase().replace('-', "_"); +// println!("cargo:rerun-if-env-changed={}", feature); +// std::env::var(feature).is_ok() +// } diff --git a/packages/dioxus/src/lib.rs b/packages/dioxus/src/lib.rs index 45c0a0dcb8..35eb4dbf6f 100644 --- a/packages/dioxus/src/lib.rs +++ b/packages/dioxus/src/lib.rs @@ -125,7 +125,7 @@ pub mod prelude { #[cfg(feature = "asset")] #[cfg_attr(docsrs, doc(cfg(feature = "asset")))] - pub use manganis::{self, self as assets, asset, classes, ImageAsset, ImageType}; + pub use manganis::{self, self as assets, asset, classes, Asset, ImageAsset, ImageType}; } #[cfg(feature = "web")] diff --git a/packages/fullstack/examples/desktop/src/main.rs b/packages/fullstack/examples/desktop/src/main.rs index 091923e247..8132792811 100644 --- a/packages/fullstack/examples/desktop/src/main.rs +++ b/packages/fullstack/examples/desktop/src/main.rs @@ -12,7 +12,7 @@ fn main() { } /// Run with `cargo run --features server` -#[cfg(not(feature = "desktop"))] +#[cfg(all(not(feature = "desktop"), feature = "server"))] #[tokio::main] async fn main() { use server_fn::axum::register_explicit; diff --git a/packages/fullstack/examples/hackernews/src/main.rs b/packages/fullstack/examples/hackernews/src/main.rs index 62bf9d8f7b..4ea769dd15 100644 --- a/packages/fullstack/examples/hackernews/src/main.rs +++ b/packages/fullstack/examples/hackernews/src/main.rs @@ -36,7 +36,7 @@ pub fn App() -> Element { #[component] fn Homepage(story: ReadOnlySignal) -> Element { rsx! { - head::Link { rel: "stylesheet", href: asset!("./assets/hackernews.css") } + head::Link { rel: "stylesheet", href: asset!("/assets/hackernews.css") } div { display: "flex", flex_direction: "row", width: "100%", div { width: "50%", From aa047a61a8f4ad719ee1601e3e02b947576ac33e Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 16 Aug 2024 22:57:06 -0700 Subject: [PATCH 011/139] shrink the diff --- examples/custom_assets.rs | 2 +- packages/cli/src/cli/bundle.rs | 1 - packages/core/docs/common_spawn_errors.md | 10 +++++----- packages/core/docs/reactivity.md | 4 ++-- packages/core/src/error_boundary.rs | 6 +++--- packages/core/src/global_context.rs | 4 ++-- packages/core/src/scope_context.rs | 4 ++-- packages/dioxus/README.md | 2 +- packages/fullstack/src/hooks/server_cached.rs | 2 +- packages/fullstack/src/hooks/server_future.rs | 2 +- packages/hooks/docs/derived_state.md | 2 +- packages/hooks/docs/rules_of_hooks.md | 10 +++++----- packages/hooks/docs/side_effects.md | 2 +- packages/signals/docs/memo.md | 2 +- packages/signals/docs/signals.md | 2 +- packages/ssr/README.md | 4 ++-- 16 files changed, 29 insertions(+), 30 deletions(-) diff --git a/examples/custom_assets.rs b/examples/custom_assets.rs index 271a52e4bb..5bebddc01d 100644 --- a/examples/custom_assets.rs +++ b/examples/custom_assets.rs @@ -17,7 +17,7 @@ use dioxus::prelude::*; /// /// When used with web apps, manganis will detect the import of the image, optimize it, and put it /// in the output dist folder in the right location, ensuring no two images have the same name. -static ASSET_PATH: &str = asset!("examples/assets/logo.png".format(ImageType::Avif)); +static ASSET_PATH: ImageAsset = asset!("/examples/assets/logo.png".image().format(ImageType::Avif)); fn main() { launch(app); diff --git a/packages/cli/src/cli/bundle.rs b/packages/cli/src/cli/bundle.rs index 5e61294ff5..9b4597a8b9 100644 --- a/packages/cli/src/cli/bundle.rs +++ b/packages/cli/src/cli/bundle.rs @@ -15,7 +15,6 @@ use super::*; pub struct Bundle { #[clap(long)] pub package: Option>, - /// The arguments for the dioxus build #[clap(flatten)] pub build_arguments: Build, diff --git a/packages/core/docs/common_spawn_errors.md b/packages/core/docs/common_spawn_errors.md index 287dfa2493..257d7c54d9 100644 --- a/packages/core/docs/common_spawn_errors.md +++ b/packages/core/docs/common_spawn_errors.md @@ -20,7 +20,7 @@ fn App() -> Element { }) }); - unimplemented!() + todo!() } ``` @@ -39,7 +39,7 @@ fn App() -> Element { }) }); - unimplemented!() + todo!() } ``` @@ -68,7 +68,7 @@ fn MyComponent(string: String) -> Element { }) }); - unimplemented!() + todo!() } ``` @@ -92,7 +92,7 @@ fn MyComponent(string: ReadOnlySignal) -> Element { }) }); - unimplemented!() + todo!() } ``` @@ -119,7 +119,7 @@ fn MyComponent(string: String) -> Element { }) }); - unimplemented!() + todo!() } ``` diff --git a/packages/core/docs/reactivity.md b/packages/core/docs/reactivity.md index 03048183c3..fdb0fe8c8b 100644 --- a/packages/core/docs/reactivity.md +++ b/packages/core/docs/reactivity.md @@ -89,14 +89,14 @@ let doubled = use_memo(move || state() * 2); #[component] fn MyComponent(state: i32) -> Element { let doubled = use_memo(move || state * 2); - unimplemented!() + todo!() } // ✅ Wrap your props in ReadOnlySignal to make them reactive #[component] fn MyReactiveComponent(state: ReadOnlySignal) -> Element { let doubled = use_memo(move || state() * 2); - unimplemented!() + todo!() } ``` diff --git a/packages/core/src/error_boundary.rs b/packages/core/src/error_boundary.rs index f9a23b935e..332c7f406a 100644 --- a/packages/core/src/error_boundary.rs +++ b/packages/core/src/error_boundary.rs @@ -106,7 +106,7 @@ pub trait Context: private::Sealed { /// "Error parsing number: {error}" /// } /// })?; - /// unimplemented!() + /// todo!() /// } /// ``` fn show(self, display_error: impl FnOnce(&E) -> Element) -> Result; @@ -120,7 +120,7 @@ pub trait Context: private::Sealed { /// // You can bubble up errors with `?` inside components, and event handlers /// // Along with the error itself, you can provide a way to display the error by calling `context` /// let number = "-1234".parse::().context("Parsing number inside of the NumberParser")?; - /// unimplemented!() + /// todo!() /// } /// ``` fn context(self, context: C) -> Result; @@ -134,7 +134,7 @@ pub trait Context: private::Sealed { /// // You can bubble up errors with `?` inside components, and event handlers /// // Along with the error itself, you can provide a way to display the error by calling `context` /// let number = "-1234".parse::().with_context(|| format!("Timestamp: {:?}", std::time::Instant::now()))?; - /// unimplemented!() + /// todo!() /// } /// ``` fn with_context(self, context: impl FnOnce() -> C) -> Result; diff --git a/packages/core/src/global_context.rs b/packages/core/src/global_context.rs index 3318fb458b..92a4dfd13b 100644 --- a/packages/core/src/global_context.rs +++ b/packages/core/src/global_context.rs @@ -25,13 +25,13 @@ pub fn vdom_is_rendering() -> bool { /// fn Component() -> Element { /// let request = spawn(async move { /// match reqwest::get("https://api.example.com").await { -/// Ok(_) => unimplemented!(), +/// Ok(_) => todo!(), /// // You can explicitly throw an error into a scope with throw_error /// Err(err) => ScopeId::APP.throw_error(err) /// } /// }); /// -/// unimplemented!() +/// todo!() /// } /// ``` pub fn throw_error(error: impl Into + 'static) { diff --git a/packages/core/src/scope_context.rs b/packages/core/src/scope_context.rs index 5d740f20d0..4fae8c1e47 100644 --- a/packages/core/src/scope_context.rs +++ b/packages/core/src/scope_context.rs @@ -556,13 +556,13 @@ impl ScopeId { /// fn Component() -> Element { /// let request = spawn(async move { /// match reqwest::get("https://api.example.com").await { - /// Ok(_) => unimplemented!(), + /// Ok(_) => todo!(), /// // You can explicitly throw an error into a scope with throw_error /// Err(err) => ScopeId::APP.throw_error(err) /// } /// }); /// - /// unimplemented!() + /// todo!() /// } /// ``` pub fn throw_error(self, error: impl Into + 'static) { diff --git a/packages/dioxus/README.md b/packages/dioxus/README.md index 1df30e3eda..a841353c09 100644 --- a/packages/dioxus/README.md +++ b/packages/dioxus/README.md @@ -114,7 +114,7 @@ In Dioxus, all properties are memorized by default with Clone and PartialEq. For ```rust, no_run # use dioxus::prelude::*; -# #[component] fn Header(title: String, color: String) -> Element { unimplemented!() } +# #[component] fn Header(title: String, color: String) -> Element { todo!() } #[component] fn App() -> Element { rsx! { diff --git a/packages/fullstack/src/hooks/server_cached.rs b/packages/fullstack/src/hooks/server_cached.rs index 7e3b55a99e..499db03623 100644 --- a/packages/fullstack/src/hooks/server_cached.rs +++ b/packages/fullstack/src/hooks/server_cached.rs @@ -17,7 +17,7 @@ use serde::{de::DeserializeOwned, Serialize}; /// 1234 /// }); /// -/// unimplemented!() +/// todo!() /// } /// ``` pub fn use_server_cached( diff --git a/packages/fullstack/src/hooks/server_future.rs b/packages/fullstack/src/hooks/server_future.rs index bf6893cc99..2f910c5ec7 100644 --- a/packages/fullstack/src/hooks/server_future.rs +++ b/packages/fullstack/src/hooks/server_future.rs @@ -40,7 +40,7 @@ use std::future::Future; /// /// ```rust, no_run /// # use dioxus::prelude::*; -/// # async fn fetch_article(id: u32) -> String { unimplemented!() } +/// # async fn fetch_article(id: u32) -> String { todo!() } /// use dioxus::prelude::*; /// /// fn App() -> Element { diff --git a/packages/hooks/docs/derived_state.md b/packages/hooks/docs/derived_state.md index 8db95724ac..7f0db4f6dd 100644 --- a/packages/hooks/docs/derived_state.md +++ b/packages/hooks/docs/derived_state.md @@ -72,6 +72,6 @@ Signals will automatically be added as dependencies, so you don't need to call t fn Comp(count: u32) -> Element { // Because the memo subscribes to `count` by adding it as a dependency, the memo will rerun every time `count` changes. let new_count = use_memo(use_reactive((&count,), |(count,)| count + 1)); - unimplemented!() + todo!() } ``` diff --git a/packages/hooks/docs/rules_of_hooks.md b/packages/hooks/docs/rules_of_hooks.md index 7d87c2676f..9ae67840dd 100644 --- a/packages/hooks/docs/rules_of_hooks.md +++ b/packages/hooks/docs/rules_of_hooks.md @@ -18,7 +18,7 @@ fn App() -> Element { let string = use_signal(|| "hello world".to_string()); } - unimplemented!() + todo!() } fn use_my_hook() -> Signal { @@ -56,7 +56,7 @@ fn App() -> Element { _ => (), } - unimplemented!() + todo!() } ``` @@ -71,7 +71,7 @@ fn App() -> Element { let string = use_signal(|| "hello world".to_string()); } - unimplemented!() + todo!() } ``` @@ -103,7 +103,7 @@ fn App() -> Element { string() }); - unimplemented!() + todo!() } ``` @@ -121,7 +121,7 @@ fn App() -> Element { let string = use_signal(|| "hello world".to_string()); // Hook 2 let doubled = use_memo(move || number() * 2); // Hook 3 - unimplemented!() + todo!() } ``` diff --git a/packages/hooks/docs/side_effects.md b/packages/hooks/docs/side_effects.md index 5765d0ebf7..1f63877b8d 100644 --- a/packages/hooks/docs/side_effects.md +++ b/packages/hooks/docs/side_effects.md @@ -52,7 +52,7 @@ fn Comp(count: u32) -> Element { // Because the memo subscribes to `count` by adding it as a dependency, the memo will rerun every time `count` changes. use_effect(use_reactive((&count,), |(count,)| println!("Manually manipulate the dom") )); - unimplemented!() + todo!() } ``` diff --git a/packages/signals/docs/memo.md b/packages/signals/docs/memo.md index 25ef4a185b..e812fdf522 100644 --- a/packages/signals/docs/memo.md +++ b/packages/signals/docs/memo.md @@ -142,6 +142,6 @@ fn IncrementButton(mut child_signal: Signal>>) -> Element { // Don't do this: it may cause issues if you drop the child component child_signal.set(Some(memo_owned_by_child)); - unimplemented!() + todo!() } ``` diff --git a/packages/signals/docs/signals.md b/packages/signals/docs/signals.md index 4ca21bb01f..bf5f025eac 100644 --- a/packages/signals/docs/signals.md +++ b/packages/signals/docs/signals.md @@ -160,6 +160,6 @@ fn IncrementButton(mut child_signal: Signal>>) -> Element { // Don't do this: it may cause issues if you drop the child component child_signal.set(Some(signal_owned_by_child)); - unimplemented!() + todo!() } ``` diff --git a/packages/ssr/README.md b/packages/ssr/README.md index 765726d879..aeab037858 100644 --- a/packages/ssr/README.md +++ b/packages/ssr/README.md @@ -51,7 +51,7 @@ let content = dioxus_ssr::render_element(rsx!{ ```rust, no_run # use dioxus::prelude::*; -# fn app() -> Element { unimplemented!() } +# fn app() -> Element { todo!() } let mut vdom = VirtualDom::new(app); vdom.rebuild_in_place(); @@ -70,7 +70,7 @@ To enable pre-rendering, simply set the pre-rendering flag to true. ```rust, no_run # use dioxus::prelude::*; -# fn App() -> Element { unimplemented!() } +# fn App() -> Element { todo!() } let mut vdom = VirtualDom::new(App); vdom.rebuild_in_place(); From 49408492d48c20aa07e582d85bba66e3b8cae411 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 16 Aug 2024 22:59:53 -0700 Subject: [PATCH 012/139] swap to Asset type --- examples/all_events.rs | 2 +- examples/calculator.rs | 2 +- examples/calculator_mutable.rs | 2 +- examples/clock.rs | 2 +- examples/control_focus.rs | 2 +- examples/counters.rs | 2 +- examples/crm.rs | 4 ++-- examples/dynamic_asset.rs | 2 +- examples/file_explorer.rs | 2 +- examples/file_upload.rs | 2 +- examples/flat_router.rs | 2 +- examples/global.rs | 2 +- examples/link.rs | 2 +- examples/overlay.rs | 2 +- examples/read_size.rs | 2 +- examples/reducer.rs | 2 +- examples/resize.rs | 2 +- examples/router.rs | 2 +- examples/todomvc.rs | 2 +- packages/html/src/document/head.rs | 4 ++-- 20 files changed, 22 insertions(+), 22 deletions(-) diff --git a/examples/all_events.rs b/examples/all_events.rs index 2a04ee2bb3..f111eccc75 100644 --- a/examples/all_events.rs +++ b/examples/all_events.rs @@ -6,7 +6,7 @@ use dioxus::prelude::*; use std::{collections::VecDeque, fmt::Debug, rc::Rc}; -const STYLE: &str = asset!("./examples/assets/events.css"); +const STYLE: Asset = asset!("/examples/assets/events.css"); fn main() { launch(app); diff --git a/examples/calculator.rs b/examples/calculator.rs index 9bf2a04618..49e1d7d581 100644 --- a/examples/calculator.rs +++ b/examples/calculator.rs @@ -12,7 +12,7 @@ use dioxus::events::*; use dioxus::html::input_data::keyboard_types::Key; use dioxus::prelude::*; -const STYLE: &str = asset!("./examples/assets/calculator.css"); +const STYLE: Asset = asset!("/examples/assets/calculator.css"); fn main() { LaunchBuilder::desktop() diff --git a/examples/calculator_mutable.rs b/examples/calculator_mutable.rs index 2f933ebbea..049f535b33 100644 --- a/examples/calculator_mutable.rs +++ b/examples/calculator_mutable.rs @@ -29,7 +29,7 @@ fn app() -> Element { let mut state = use_signal(Calculator::new); rsx! { - head::Link { rel: "stylesheet", href: asset!("./examples/assets/calculator.css") } + head::Link { rel: "stylesheet", href: asset!("/examples/assets/calculator.css") } div { id: "wrapper", div { class: "app", div { diff --git a/examples/clock.rs b/examples/clock.rs index 048ceb56ee..6f59daf37c 100644 --- a/examples/clock.rs +++ b/examples/clock.rs @@ -5,7 +5,7 @@ use dioxus::prelude::*; -const STYLE: &str = asset!("./examples/assets/clock.css"); +const STYLE: Asset = asset!("/examples/assets/clock.css"); fn main() { launch_desktop(app); diff --git a/examples/control_focus.rs b/examples/control_focus.rs index 723318c3ff..208310088c 100644 --- a/examples/control_focus.rs +++ b/examples/control_focus.rs @@ -6,7 +6,7 @@ use dioxus::prelude::*; use std::rc::Rc; -const STYLE: &str = asset!("./examples/assets/roulette.css"); +const STYLE: Asset = asset!("/examples/assets/roulette.css"); fn main() { launch_desktop(app); diff --git a/examples/counters.rs b/examples/counters.rs index f2d8c4203c..bb9d5975a1 100644 --- a/examples/counters.rs +++ b/examples/counters.rs @@ -2,7 +2,7 @@ use dioxus::prelude::*; -const STYLE: &str = asset!("./examples/assets/counter.css"); +const STYLE: Asset = asset!("/examples/assets/counter.css"); fn main() { launch(app); diff --git a/examples/crm.rs b/examples/crm.rs index 21f07466cd..b05001859c 100644 --- a/examples/crm.rs +++ b/examples/crm.rs @@ -22,11 +22,11 @@ fn main() { rsx! { head::Link { rel: "stylesheet", - href: asset!("https://unpkg.com/purecss@2.0.6/build/pure-min.css"), + href: asset!("https://unpkg.com/purecss@2.0.6/build/pure-min.css"), integrity: "sha384-Uu6IeWbM+gzNVXJcM9XV3SohHtmWE+3VGi496jvgX1jyvDTXfdK+rfZc8C1Aehk5", crossorigin: "anonymous" } - head::Link { rel: "stylesheet", href: asset!("./examples/assets/crm.css") } + head::Link { rel: "stylesheet", href: asset!("/examples/assets/crm.css") } h1 { "Dioxus CRM Example" } Router:: {} } diff --git a/examples/dynamic_asset.rs b/examples/dynamic_asset.rs index 14a33c4809..b254b7047f 100644 --- a/examples/dynamic_asset.rs +++ b/examples/dynamic_asset.rs @@ -7,7 +7,7 @@ use dioxus::desktop::{use_asset_handler, wry::http::Response}; use dioxus::prelude::*; -const STYLE: &str = asset!("./examples/assets/custom_assets.css"); +const STYLE: Asset = asset!("/examples/assets/custom_assets.css"); fn main() { launch_desktop(app); diff --git a/examples/file_explorer.rs b/examples/file_explorer.rs index 1ac571d2e8..e6f7c789d8 100644 --- a/examples/file_explorer.rs +++ b/examples/file_explorer.rs @@ -20,7 +20,7 @@ fn app() -> Element { rsx! { head::Link { rel: "stylesheet", - href: asset!("./examples/assets/fileexplorer.css") + href: asset!("/examples/assets/fileexplorer.css") } div { head::Link { href: "https://fonts.googleapis.com/icon?family=Material+Icons", rel: "stylesheet" } diff --git a/examples/file_upload.rs b/examples/file_upload.rs index 9fb74072bb..982e8a0b06 100644 --- a/examples/file_upload.rs +++ b/examples/file_upload.rs @@ -8,7 +8,7 @@ use std::sync::Arc; use dioxus::prelude::*; use dioxus::{html::HasFileData, prelude::dioxus_elements::FileEngine}; -const STYLE: &str = asset!("./examples/assets/file_upload.css"); +const STYLE: Asset = asset!("/examples/assets/file_upload.css"); fn main() { launch(app); diff --git a/examples/flat_router.rs b/examples/flat_router.rs index 79a9d2a3f2..c00e4600f4 100644 --- a/examples/flat_router.rs +++ b/examples/flat_router.rs @@ -9,7 +9,7 @@ use dioxus::prelude::*; -const STYLE: &str = asset!("./examples/assets/flat_router.css"); +const STYLE: Asset = asset!("/examples/assets/flat_router.css"); fn main() { launch(|| { diff --git a/examples/global.rs b/examples/global.rs index 0a08485b02..d0269322ec 100644 --- a/examples/global.rs +++ b/examples/global.rs @@ -7,7 +7,7 @@ use dioxus::prelude::*; -const STYLE: &str = asset!("./examples/assets/counter.css"); +const STYLE: Asset = asset!("/examples/assets/counter.css"); static COUNT: GlobalSignal = Signal::global(|| 0); static DOUBLED_COUNT: GlobalMemo = Memo::global(|| COUNT() * 2); diff --git a/examples/link.rs b/examples/link.rs index ff29b65626..7c2b420705 100644 --- a/examples/link.rs +++ b/examples/link.rs @@ -8,7 +8,7 @@ use dioxus::prelude::*; -const STYLE: &str = asset!("./examples/assets/links.css"); +const STYLE: Asset = asset!("/examples/assets/links.css"); fn main() { launch(app); diff --git a/examples/overlay.rs b/examples/overlay.rs index e4e2e80592..e73db187e8 100644 --- a/examples/overlay.rs +++ b/examples/overlay.rs @@ -22,7 +22,7 @@ fn app() -> Element { rsx! { head::Link { rel: "stylesheet", - href: asset!("./examples/assets/overlay.css"), + href: asset!("/examples/assets/overlay.css"), } if show_overlay() { div { diff --git a/examples/read_size.rs b/examples/read_size.rs index 1965597cfa..076a73baac 100644 --- a/examples/read_size.rs +++ b/examples/read_size.rs @@ -28,7 +28,7 @@ fn app() -> Element { }; rsx!( - head::Link { rel: "stylesheet", href: asset!("./examples/assets/read_size.css") } + head::Link { rel: "stylesheet", href: asset!("/examples/assets/read_size.css") } div { width: "50%", height: "50%", diff --git a/examples/reducer.rs b/examples/reducer.rs index 7c434e5858..407f1de83b 100644 --- a/examples/reducer.rs +++ b/examples/reducer.rs @@ -7,7 +7,7 @@ use dioxus::prelude::*; -const STYLE: &str = asset!("./examples/assets/radio.css"); +const STYLE: Asset = asset!("/examples/assets/radio.css"); fn main() { launch(app); diff --git a/examples/resize.rs b/examples/resize.rs index eace774a0b..56b4bb798f 100644 --- a/examples/resize.rs +++ b/examples/resize.rs @@ -15,7 +15,7 @@ fn app() -> Element { let mut dimensions = use_signal(Size2D::zero); rsx!( - head::Link { rel: "stylesheet", href: asset!("./examples/assets/read_size.css") } + head::Link { rel: "stylesheet", href: asset!("/examples/assets/read_size.css") } div { width: "50%", height: "50%", diff --git a/examples/router.rs b/examples/router.rs index 13197f57e2..beed07b518 100644 --- a/examples/router.rs +++ b/examples/router.rs @@ -8,7 +8,7 @@ use dioxus::prelude::*; -const STYLE: &str = asset!("./examples/assets/router.css"); +const STYLE: Asset = asset!("/examples/assets/router.css"); fn main() { launch(|| { diff --git a/examples/todomvc.rs b/examples/todomvc.rs index bce2d2e9c6..6cb748dd4f 100644 --- a/examples/todomvc.rs +++ b/examples/todomvc.rs @@ -3,7 +3,7 @@ use dioxus::prelude::*; use std::collections::HashMap; -const STYLE: &str = asset!("./examples/assets/todomvc.css"); +const STYLE: Asset = asset!("/examples/assets/todomvc.css"); fn main() { launch(app); diff --git a/packages/html/src/document/head.rs b/packages/html/src/document/head.rs index 2787820157..2700ba67e1 100644 --- a/packages/html/src/document/head.rs +++ b/packages/html/src/document/head.rs @@ -245,7 +245,7 @@ impl ScriptProps { /// rsx! { /// // You can use the Script component to render a script tag into the head of the page /// Script { -/// src: asset!("./assets/script.js"), +/// src: asset!("/assets/script.js"), /// } /// } /// } @@ -435,7 +435,7 @@ impl LinkProps { /// // You can use the meta component to render a meta tag into the head of the page /// // This meta tag will redirect the user to the dioxuslabs homepage in 10 seconds /// head::Link { -/// href: asset!("./assets/style.css"), +/// href: asset!("/assets/style.css"), /// rel: "stylesheet", /// } /// } From 8db5d77f12e76351bce18c0e3d707e31a0d7936d Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Sat, 17 Aug 2024 00:25:36 -0700 Subject: [PATCH 013/139] add some more logs --- Cargo.lock | 522 +++++++----------- Cargo.toml | 16 + packages/cli/src/builder/web.rs | 3 + packages/desktop/Cargo.toml | 40 +- packages/html/Cargo.toml | 9 +- packages/html/src/lib.rs | 2 + .../suspense-carousel/Cargo.toml | 7 +- .../suspense-carousel/src/main.rs | 12 +- packages/web/Cargo.toml | 4 +- 9 files changed, 249 insertions(+), 366 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6219b026f3..dd54a9f67b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -360,17 +360,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "async-channel" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" -dependencies = [ - "concurrent-queue", - "event-listener 2.5.3", - "futures-core", -] - [[package]] name = "async-channel" version = "2.3.1" @@ -399,82 +388,25 @@ dependencies = [ "zstd-safe 7.2.1", ] -[[package]] -name = "async-executor" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7ebdfa2ebdab6b1760375fa7d6f382b9f486eac35fc994625a00e89280bdbb7" -dependencies = [ - "async-task", - "concurrent-queue", - "fastrand 2.1.0", - "futures-lite 2.3.0", - "slab", -] - -[[package]] -name = "async-global-executor" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" -dependencies = [ - "async-channel 2.3.1", - "async-executor", - "async-io 2.3.4", - "async-lock 3.4.0", - "blocking", - "futures-lite 2.3.0", - "once_cell", -] - -[[package]] -name = "async-io" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" -dependencies = [ - "async-lock 2.8.0", - "autocfg", - "cfg-if", - "concurrent-queue", - "futures-lite 1.13.0", - "log", - "parking", - "polling 2.8.0", - "rustix 0.37.27", - "slab", - "socket2 0.4.10", - "waker-fn", -] - [[package]] name = "async-io" version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "444b0228950ee6501b3568d3c93bf1176a1fdbc3b758dcd9475046d30f4dc7e8" dependencies = [ - "async-lock 3.4.0", + "async-lock", "cfg-if", "concurrent-queue", "futures-io", - "futures-lite 2.3.0", + "futures-lite", "parking", - "polling 3.7.3", - "rustix 0.38.34", + "polling", + "rustix", "slab", "tracing", "windows-sys 0.59.0", ] -[[package]] -name = "async-lock" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" -dependencies = [ - "event-listener 2.5.3", -] - [[package]] name = "async-lock" version = "3.4.0" @@ -492,16 +424,16 @@ version = "2.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8a07789659a4d385b79b18b9127fc27e1a59e1e89117c78c5ea3b806f016374" dependencies = [ - "async-channel 2.3.1", - "async-io 2.3.4", - "async-lock 3.4.0", + "async-channel", + "async-io", + "async-lock", "async-signal", "async-task", "blocking", "cfg-if", "event-listener 5.3.1", - "futures-lite 2.3.0", - "rustix 0.38.34", + "futures-lite", + "rustix", "tracing", "windows-sys 0.59.0", ] @@ -523,44 +455,18 @@ version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3" dependencies = [ - "async-io 2.3.4", - "async-lock 3.4.0", + "async-io", + "async-lock", "atomic-waker", "cfg-if", "futures-core", "futures-io", - "rustix 0.38.34", + "rustix", "signal-hook-registry", "slab", "windows-sys 0.59.0", ] -[[package]] -name = "async-std" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" -dependencies = [ - "async-channel 1.9.0", - "async-global-executor", - "async-io 1.13.0", - "async-lock 2.8.0", - "crossbeam-utils", - "futures-channel", - "futures-core", - "futures-io", - "futures-lite 1.13.0", - "gloo-timers", - "kv-log-macro", - "log", - "memchr", - "once_cell", - "pin-project-lite", - "pin-utils", - "slab", - "wasm-bindgen-futures", -] - [[package]] name = "async-stream" version = "0.3.5" @@ -1145,16 +1051,25 @@ dependencies = [ "generic-array 0.14.7", ] +[[package]] +name = "block2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" +dependencies = [ + "objc2", +] + [[package]] name = "blocking" version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" dependencies = [ - "async-channel 2.3.1", + "async-channel", "async-task", "futures-io", - "futures-lite 2.3.0", + "futures-lite", "piper", ] @@ -1699,14 +1614,14 @@ dependencies = [ [[package]] name = "cocoa" -version = "0.25.0" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" +checksum = "f79398230a6e2c08f5c9760610eb6924b52aa9e7950a619602baba59dcbbdbb2" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", "block", "cocoa-foundation", - "core-foundation", + "core-foundation 0.10.0", "core-graphics", "foreign-types 0.5.0", "libc", @@ -1715,13 +1630,13 @@ dependencies = [ [[package]] name = "cocoa-foundation" -version = "0.1.2" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" +checksum = "e14045fb83be07b5acf1c0884b2180461635b433455fa35d1cd6f17f1450679d" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", "block", - "core-foundation", + "core-foundation 0.10.0", "core-graphics-types", "libc", "objc", @@ -1975,6 +1890,16 @@ dependencies = [ "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" @@ -1983,12 +1908,12 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "core-graphics" -version = "0.23.2" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" +checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" dependencies = [ - "bitflags 1.3.2", - "core-foundation", + "bitflags 2.6.0", + "core-foundation 0.10.0", "core-graphics-types", "foreign-types 0.5.0", "libc", @@ -1996,12 +1921,12 @@ dependencies = [ [[package]] name = "core-graphics-types" -version = "0.1.3" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" dependencies = [ - "bitflags 1.3.2", - "core-foundation", + "bitflags 2.6.0", + "core-foundation 0.10.0", "libc", ] @@ -2772,7 +2697,7 @@ version = "0.6.0-alpha.2" dependencies = [ "async-trait", "cocoa", - "core-foundation", + "core-foundation 0.10.0", "dioxus", "dioxus-cli-config", "dioxus-core", @@ -2789,7 +2714,7 @@ dependencies = [ "generational-box", "global-hotkey", "http-range", - "infer 0.11.0", + "infer 0.16.0", "muda", "objc", "objc_id", @@ -3244,7 +3169,7 @@ dependencies = [ "futures-util", "generational-box", "gloo-dialogs", - "gloo-timers", + "gloo-timers 0.3.0", "js-sys", "rustc-hash 1.1.0", "serde", @@ -3735,15 +3660,6 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2a2b11eda1d40935b26cf18f6833c526845ae8c41e58d09af6adeb6f0269183" -[[package]] -name = "fastrand" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] - [[package]] name = "fastrand" version = "2.1.0" @@ -4120,28 +4036,13 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" -[[package]] -name = "futures-lite" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" -dependencies = [ - "fastrand 1.9.0", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", -] - [[package]] name = "futures-lite" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" dependencies = [ - "fastrand 2.1.0", + "fastrand", "futures-core", "futures-io", "parking", @@ -4652,7 +4553,7 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35192df7fd0fa112263bad8021e2df7167df4cc2a6e6d15892e1e55621d3d4dc" dependencies = [ - "fastrand 2.1.0", + "fastrand", "unicode-normalization", ] @@ -4721,9 +4622,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "global-hotkey" -version = "0.5.5" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b436093d1598b05e3b7fddc097b2bad32763f53a1beb25ab6f9718c6a60acd09" +checksum = "298a7667d6011efe6b35673c6b29001b88677ae1b3d6b2feccfbff4b44892866" dependencies = [ "bitflags 2.6.0", "cocoa", @@ -4732,7 +4633,7 @@ dependencies = [ "objc", "once_cell", "thiserror", - "windows-sys 0.52.0", + "windows-sys 0.59.0", "x11-dl", ] @@ -4763,7 +4664,7 @@ dependencies = [ "gloo-net 0.3.1", "gloo-render", "gloo-storage", - "gloo-timers", + "gloo-timers 0.2.6", "gloo-utils 0.1.7", "gloo-worker", ] @@ -4902,8 +4803,16 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" dependencies = [ - "futures-channel", - "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "gloo-timers" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" +dependencies = [ "js-sys", "wasm-bindgen", ] @@ -5410,7 +5319,7 @@ dependencies = [ "httpdate", "itoa 1.0.11", "pin-project-lite", - "socket2 0.5.7", + "socket2", "tokio", "tower-service", "tracing", @@ -5499,7 +5408,7 @@ dependencies = [ "http-body 1.0.1", "hyper 1.4.1", "pin-project-lite", - "socket2 0.5.7", + "socket2", "tokio", "tower", "tower-service", @@ -5693,15 +5602,6 @@ version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" -[[package]] -name = "infer" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6c16b11a665b26aeeb9b1d7f954cdeb034be38dd00adab4f2ae921a8fee804" -dependencies = [ - "cfb", -] - [[package]] name = "infer" version = "0.15.0" @@ -5785,17 +5685,6 @@ version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f958d3d68f4167080a18141e10381e7634563984a537f2a49a30fd8e53ac5767" -[[package]] -name = "io-lifetimes" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" -dependencies = [ - "hermit-abi 0.3.9", - "libc", - "windows-sys 0.48.0", -] - [[package]] name = "ipnet" version = "2.9.0" @@ -6103,15 +5992,6 @@ dependencies = [ "selectors", ] -[[package]] -name = "kv-log-macro" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" -dependencies = [ - "log", -] - [[package]] name = "lazy-js-bundle" version = "0.6.0-alpha.2" @@ -6310,12 +6190,6 @@ dependencies = [ "cc", ] -[[package]] -name = "linux-raw-sys" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" - [[package]] name = "linux-raw-sys" version = "0.4.14" @@ -6394,9 +6268,6 @@ name = "log" version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" -dependencies = [ - "value-bag", -] [[package]] name = "longest-increasing-subsequence" @@ -6471,7 +6342,7 @@ dependencies = [ name = "manganis" version = "0.3.0-alpha.0" dependencies = [ - "core-foundation", + "core-foundation 0.9.4", "dirs", "dunce", "infer 0.16.0", @@ -6516,8 +6387,6 @@ dependencies = [ "infer 0.16.0", "scratch", "serde", - "tracing", - "url", ] [[package]] @@ -6525,14 +6394,12 @@ name = "manganis-macro" version = "0.3.0-alpha.0" dependencies = [ "base64 0.22.1", - "manganis-cli-support", "manganis-common", "proc-macro2", "quote", "serde", "serde_json", "syn 2.0.74", - "tracing-subscriber", ] [[package]] @@ -6743,12 +6610,13 @@ dependencies = [ [[package]] name = "muda" -version = "0.11.5" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c47e7625990fc1af2226ea4f34fb2412b03c12639fcb91868581eb3a6893453" +checksum = "86c410a9d21523a819e84881603fbc00331c8001eb899964952046671deddb9c" dependencies = [ "cocoa", "crossbeam-channel", + "dpi", "gtk", "keyboard-types", "libxdo", @@ -6756,7 +6624,7 @@ dependencies = [ "once_cell", "png", "thiserror", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -6813,15 +6681,16 @@ dependencies = [ [[package]] name = "ndk" -version = "0.7.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", "jni-sys", + "log", "ndk-sys", - "num_enum 0.5.11", - "raw-window-handle 0.5.2", + "num_enum", + "raw-window-handle 0.6.2", "thiserror", ] @@ -6833,9 +6702,9 @@ checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" [[package]] name = "ndk-sys" -version = "0.4.1+23.1.7779620" +version = "0.6.0+11769913" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cf2aae958bd232cac5069850591667ad422d263686d75b52a065f9badeee5a3" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" dependencies = [ "jni-sys", ] @@ -7077,34 +6946,13 @@ dependencies = [ "libc", ] -[[package]] -name = "num_enum" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" -dependencies = [ - "num_enum_derive 0.5.11", -] - [[package]] name = "num_enum" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" dependencies = [ - "num_enum_derive 0.7.3", -] - -[[package]] -name = "num_enum_derive" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" -dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro2", - "quote", - "syn 1.0.109", + "num_enum_derive", ] [[package]] @@ -7155,6 +7003,40 @@ dependencies = [ "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-encode" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7891e71393cd1f227313c9379a26a584ff3d7e6e7159e988851f0934c993f0f8" + +[[package]] +name = "objc2-foundation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" +dependencies = [ + "bitflags 2.6.0", + "block2", + "libc", + "objc2", +] + [[package]] name = "objc_exception" version = "0.1.2" @@ -7607,7 +7489,7 @@ dependencies = [ "nom", "num-bigint-dig", "num-traits", - "num_enum 0.7.3", + "num_enum", "p256", "p384", "rand 0.8.5", @@ -7797,7 +7679,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" dependencies = [ "atomic-waker", - "fastrand 2.1.0", + "fastrand", "futures-io", ] @@ -7882,22 +7764,6 @@ dependencies = [ "miniz_oxide", ] -[[package]] -name = "polling" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" -dependencies = [ - "autocfg", - "bitflags 1.3.2", - "cfg-if", - "concurrent-queue", - "libc", - "log", - "pin-project-lite", - "windows-sys 0.48.0", -] - [[package]] name = "polling" version = "3.7.3" @@ -7908,7 +7774,7 @@ dependencies = [ "concurrent-queue", "hermit-abi 0.4.0", "pin-project-lite", - "rustix 0.38.34", + "rustix", "tracing", "windows-sys 0.59.0", ] @@ -8226,7 +8092,7 @@ dependencies = [ "quinn-udp", "rustc-hash 2.0.0", "rustls 0.23.12", - "socket2 0.5.7", + "socket2", "thiserror", "tokio", "tracing", @@ -8257,7 +8123,7 @@ checksum = "8bffec3605b73c6f1754535084a85229fa8a30f86014e6c81aeec4abb68b0285" dependencies = [ "libc", "once_cell", - "socket2 0.5.7", + "socket2", "tracing", "windows-sys 0.52.0", ] @@ -8911,20 +8777,6 @@ dependencies = [ "semver 1.0.23", ] -[[package]] -name = "rustix" -version = "0.37.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" -dependencies = [ - "bitflags 1.3.2", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys 0.3.8", - "windows-sys 0.48.0", -] - [[package]] name = "rustix" version = "0.38.34" @@ -8934,7 +8786,7 @@ dependencies = [ "bitflags 2.6.0", "errno", "libc", - "linux-raw-sys 0.4.14", + "linux-raw-sys", "windows-sys 0.52.0", ] @@ -9136,7 +8988,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ "bitflags 2.6.0", - "core-foundation", + "core-foundation 0.9.4", "core-foundation-sys", "libc", "security-framework-sys", @@ -9655,16 +9507,6 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" -[[package]] -name = "socket2" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "socket2" version = "0.5.7" @@ -10171,9 +10013,10 @@ dependencies = [ name = "suspense-carousel" version = "0.6.0-alpha.2" dependencies = [ - "async-std", "dioxus", + "gloo-timers 0.3.0", "serde", + "tokio", ] [[package]] @@ -11176,7 +11019,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ "bitflags 1.3.2", - "core-foundation", + "core-foundation 0.9.4", "system-configuration-sys", ] @@ -11205,13 +11048,13 @@ dependencies = [ [[package]] name = "tao" -version = "0.28.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea538df05fbc2dcbbd740ba0cfe8607688535f4798d213cbbfa13ce494f3451f" +checksum = "6775bcf3c1da33f848ede9cff5883ed1e45a29f66533ce42ad06c93ae514ed59" dependencies = [ "bitflags 2.6.0", "cocoa", - "core-foundation", + "core-foundation 0.10.0", "core-graphics", "crossbeam-channel", "dispatch", @@ -11238,7 +11081,7 @@ dependencies = [ "unicode-segmentation", "url", "windows", - "windows-core 0.57.0", + "windows-core 0.58.0", "windows-version", "x11-dl", ] @@ -11385,8 +11228,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", - "fastrand 2.1.0", - "rustix 0.38.34", + "fastrand", + "rustix", "windows-sys 0.52.0", ] @@ -11564,7 +11407,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.7", + "socket2", "tokio-macros", "tracing", "windows-sys 0.52.0", @@ -12295,12 +12138,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" -[[package]] -name = "value-bag" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a84c137d37ab0142f0f2ddfe332651fdbf252e7b7dbb4e67b6c1f1b2e925101" - [[package]] name = "vcpkg" version = "0.2.15" @@ -12325,12 +12162,6 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65dd7eed29412da847b0f78bcec0ac98588165988a8cfe41d4ea1d429f8ccfff" -[[package]] -name = "waker-fn" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" - [[package]] name = "walkdir" version = "2.5.0" @@ -12698,17 +12529,18 @@ dependencies = [ [[package]] name = "webbrowser" -version = "0.8.15" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db67ae75a9405634f5882791678772c94ff5f16a66535aae186e26aa0841fc8b" +checksum = "425ba64c1e13b1c6e8c5d2541c8fac10022ca584f33da781db01b5756aef1f4e" dependencies = [ - "core-foundation", + "block2", + "core-foundation 0.9.4", "home", "jni", "log", "ndk-context", - "objc", - "raw-window-handle 0.5.2", + "objc2", + "objc2-foundation", "url", "web-sys", ] @@ -12774,23 +12606,23 @@ dependencies = [ [[package]] name = "webview2-com" -version = "0.31.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6516cfa64c6b3212686080eeec378e662c2af54bb2a5b2a22749673f5cb2226f" +checksum = "6f61ff3d9d0ee4efcb461b14eb3acfda2702d10dc329f339303fc3e57215ae2c" dependencies = [ "webview2-com-macros", "webview2-com-sys", "windows", - "windows-core 0.57.0", + "windows-core 0.58.0", "windows-implement", "windows-interface", ] [[package]] name = "webview2-com-macros" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1345798ecd8122468840bcdf1b95e5dc6d2206c5e4b0eafa078d061f59c9bc" +checksum = "1d228f15bba3b9d56dde8bddbee66fa24545bd17b48d5128ccf4a8742b18e431" dependencies = [ "proc-macro2", "quote", @@ -12799,13 +12631,13 @@ dependencies = [ [[package]] name = "webview2-com-sys" -version = "0.31.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c76d5b77320ff155660be1df3e6588bc85c75f1a9feef938cc4dc4dd60d1d7cf" +checksum = "a3a3e2eeb58f82361c93f9777014668eb3d07e7d174ee4c819575a9208011886" dependencies = [ "thiserror", "windows", - "windows-core 0.57.0", + "windows-core 0.58.0", ] [[package]] @@ -12823,7 +12655,7 @@ dependencies = [ "either", "home", "once_cell", - "rustix 0.38.34", + "rustix", ] [[package]] @@ -12869,11 +12701,11 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.57.0" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" dependencies = [ - "windows-core 0.57.0", + "windows-core 0.58.0", "windows-targets 0.52.6", ] @@ -12888,21 +12720,22 @@ dependencies = [ [[package]] name = "windows-core" -version = "0.57.0" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" dependencies = [ "windows-implement", "windows-interface", - "windows-result", + "windows-result 0.2.0", + "windows-strings", "windows-targets 0.52.6", ] [[package]] name = "windows-implement" -version = "0.57.0" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", @@ -12911,9 +12744,9 @@ dependencies = [ [[package]] name = "windows-interface" -version = "0.57.0" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", @@ -12926,7 +12759,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acc134c90a0318d873ec962b13149e9c862ff0d2669082a709a4810167a3c6ee" dependencies = [ - "windows-result", + "windows-result 0.1.2", "windows-targets 0.52.6", ] @@ -12939,6 +12772,25 @@ 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" @@ -13192,9 +13044,9 @@ dependencies = [ [[package]] name = "wry" -version = "0.41.0" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b00c945786b02d7805d09a969fa36d0eee4e0bd4fb3ec2a79d2bf45a1b44cd" +checksum = "49b8049c8f239cdbfaaea4bacb9646f6b208938ceec0acd5b3e99cd05f70903f" dependencies = [ "base64 0.22.1", "block", @@ -13212,8 +13064,6 @@ dependencies = [ "kuchikiki", "libc", "ndk", - "ndk-context", - "ndk-sys", "objc", "objc_id", "once_cell", @@ -13227,7 +13077,7 @@ dependencies = [ "webkit2gtk-sys", "webview2-com", "windows", - "windows-core 0.57.0", + "windows-core 0.58.0", "windows-version", "x11-dl", ] @@ -13300,8 +13150,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" dependencies = [ "libc", - "linux-raw-sys 0.4.14", - "rustix 0.38.34", + "linux-raw-sys", + "rustix", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 9947d68c30..5233dceeff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -150,6 +150,22 @@ once_cell = "1.17.1" uuid = "1.9.1" convert_case = "0.6.0" tokio-tungstenite = { version = "0.23.1" } +gloo-timers = "0.3.0" + +# desktop +wry = { version = "0.42.0", default-features = false } +tao = { version = "0.29.0", features = ["rwh_05"] } +webbrowser = "1.0.1" +infer = "0.16.0" +dunce = "1.0.2" +urlencoding = "2.1.2" +global-hotkey = "0.6.0" +rfd = { version = "0.14", default-features = false } +muda = "0.14.0" +cocoa = "0.26" +core-foundation = "0.10.0" +objc = { version = "0.2.7", features = ["exception"] } +objc_id = "0.1.1" # cli, cli-config dirs = "5.0.1" diff --git a/packages/cli/src/builder/web.rs b/packages/cli/src/builder/web.rs index bb3f7e5e79..0830762b52 100644 --- a/packages/cli/src/builder/web.rs +++ b/packages/cli/src/builder/web.rs @@ -94,8 +94,11 @@ impl BuildRequest { .generate(&bindgen_outdir) .unwrap(); }; + let start = std::time::Instant::now(); let bindgen_result = tokio::task::spawn_blocking(run_wasm_bindgen.clone()).await; + tracing::info!("wasm-bindgen complete in {:?}", start.elapsed()); + // WASM bindgen requires the exact version of the bindgen schema to match the version the CLI was built with // If we get an error, we can try to recover by pinning the user's wasm-bindgen version to the version we used if let Err(err) = bindgen_result { diff --git a/packages/desktop/Cargo.toml b/packages/desktop/Cargo.toml index 67a9bfc164..3c89d18996 100644 --- a/packages/desktop/Cargo.toml +++ b/packages/desktop/Cargo.toml @@ -21,14 +21,11 @@ dioxus-signals = { workspace = true, optional = true } dioxus-interpreter-js = { workspace = true, features = ["binary-protocol", "serialize"] } dioxus-cli-config = { workspace = true, features = ["read-config"] } generational-box = { workspace = true } -# hotreload only works on desktop platforms.... mobile is still wip dioxus-hot-reload = { workspace = true, optional = true, features = ["serve", "client"]} - -serde = "1.0.136" -serde_json = "1.0.79" thiserror = { workspace = true } tracing = { workspace = true } -wry = { version = "0.41.0", default-features = false, features = [ +tao = { workspace = true, features = ["rwh_05"] } +wry = { workspace = true, default-features = false, features = [ "os-webview", "protocol", "drag-drop", @@ -42,27 +39,26 @@ tokio = { workspace = true, features = [ "macros", "fs", ], optional = true } -webbrowser = "0.8.0" -infer = "0.11.0" -dunce = "1.0.2" slab = { workspace = true } rustc-hash = { workspace = true } dioxus-hooks = { workspace = true } futures-util = { workspace = true } -urlencoding = "2.1.2" -async-trait = "0.1.68" -tao = { version = "0.28.1", features = ["rwh_05"] } +webbrowser = { workspace = true } +infer = { workspace = true } +dunce = { workspace = true } +urlencoding = { workspace = true } +async-trait = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } [target.'cfg(unix)'.dependencies] signal-hook = "0.3.17" [target.'cfg(any(target_os = "windows",target_os = "macos",target_os = "linux",target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))'.dependencies] -global-hotkey = "0.5.0" -rfd = { version = "0.14", default-features = false, features = ["xdg-portal", "tokio"] } -muda = "0.11.3" - - +global-hotkey = { workspace = true } +rfd = { workspace = true, default-features = false, features = ["xdg-portal", "tokio"] } +muda = { workspace = true } # use rustls on android [target.'cfg(target_os = "android")'.dependencies] @@ -73,10 +69,10 @@ tokio-tungstenite = { workspace = true, optional = true, features = ["rustls"]} tokio-tungstenite = { workspace = true, optional = true, features = ["native-tls"]} [target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies] -cocoa = "0.25" -core-foundation = "0.9.3" -objc = "0.2.7" -objc_id = "0.1.1" +cocoa = { workspace = true } +core-foundation = { workspace = true } +objc = { workspace = true } +objc_id = { workspace = true } [features] default = ["tokio_runtime", "wry/objc-exception", "hot-reload", "devtools"] @@ -104,10 +100,10 @@ rustdoc-args = [ "--cfg", "docsrs" ] [dev-dependencies] dioxus = { workspace = true, features = ["desktop"] } -exitcode = "1.1.2" reqwest = { workspace = true, features = ["json"] } -http-range = { version = "0.1.5" } dioxus-ssr = { workspace = true, default-features = false } +exitcode = "1.1.2" +http-range = { version = "0.1.5" } separator = "0.4.1" rand = { version = "0.8.4", features = ["small_rng"] } diff --git a/packages/html/Cargo.toml b/packages/html/Cargo.toml index bd95323cfa..4b127ed659 100644 --- a/packages/html/Cargo.toml +++ b/packages/html/Cargo.toml @@ -18,9 +18,6 @@ dioxus-hooks = { workspace = true } generational-box = { workspace = true } serde = { version = "1", features = ["derive"], optional = true } serde_repr = { version = "0.1", optional = true } -wasm-bindgen = { workspace = true, optional = true } -wasm-bindgen-futures = { workspace = true, optional = true } -js-sys = { version = "0.3.56", optional = true } euclid = "0.22.7" enumset = "1.1.2" keyboard-types = { version = "0.7", default-features = false } @@ -31,6 +28,12 @@ serde_json = { version = "1", optional = true } tracing.workspace = true rustversion = "1.0.17" +# todo: we shouldn't have any wasm-bindgen dependencies in the html cratea +# [target.'cfg(target_arch = "wasm32")'.dependencies] +wasm-bindgen = { workspace = true, optional = true } +js-sys = { version = "0.3.56", optional = true } +wasm-bindgen-futures = { workspace = true, optional = true } + [dependencies.web-sys] optional = true version = "0.3.56" diff --git a/packages/html/src/lib.rs b/packages/html/src/lib.rs index 962a30c4a4..18bcf6d5b6 100644 --- a/packages/html/src/lib.rs +++ b/packages/html/src/lib.rs @@ -31,8 +31,10 @@ pub mod input_data; pub mod native_bind; pub mod point_interaction; mod render_template; + #[cfg(feature = "wasm-bind")] mod web_sys_bind; + #[cfg(feature = "wasm-bind")] pub use web_sys_bind::*; diff --git a/packages/playwright-tests/suspense-carousel/Cargo.toml b/packages/playwright-tests/suspense-carousel/Cargo.toml index c71c29fe1c..062c396057 100644 --- a/packages/playwright-tests/suspense-carousel/Cargo.toml +++ b/packages/playwright-tests/suspense-carousel/Cargo.toml @@ -5,10 +5,15 @@ version.workspace = true publish = false [dependencies] -async-std = "1.12.0" dioxus = { workspace = true, features = ["fullstack"] } serde = "1.0.159" +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +tokio = { workspace = true, features = ["full"] } + +[target.'cfg(target_arch = "wasm32")'.dependencies] +gloo-timers = { workspace = true } + [features] default = [] server = ["dioxus/axum"] diff --git a/packages/playwright-tests/suspense-carousel/src/main.rs b/packages/playwright-tests/suspense-carousel/src/main.rs index 6d304ba88a..b3661abe61 100644 --- a/packages/playwright-tests/suspense-carousel/src/main.rs +++ b/packages/playwright-tests/suspense-carousel/src/main.rs @@ -58,7 +58,7 @@ impl ResolvedOn { #[component] fn SuspendedComponent(id: i32) -> Element { let resolved_on = use_server_future(move || async move { - async_std::task::sleep(std::time::Duration::from_secs(1)).await; + sleep(1000).await; ResolvedOn::CURRENT })?() .unwrap(); @@ -89,7 +89,7 @@ fn SuspendedComponent(id: i32) -> Element { #[component] fn NestedSuspendedComponent(id: i32) -> Element { let resolved_on = use_server_future(move || async move { - async_std::task::sleep(std::time::Duration::from_secs(1)).await; + sleep(1000).await; ResolvedOn::CURRENT })?() .unwrap(); @@ -106,6 +106,14 @@ fn NestedSuspendedComponent(id: i32) -> Element { } } +async fn sleep(millis: u64) { + #[cfg(target_arch = "wasm32")] + gloo_timers::future::sleep(std::time::Duration::from_millis(millis)).await; + + #[cfg(not(target_arch = "wasm32"))] + tokio::time::sleep(std::time::Duration::from_millis(millis)).await; +} + fn main() { launch(app); } diff --git a/packages/web/Cargo.toml b/packages/web/Cargo.toml index b404725cd5..df05cd51cc 100644 --- a/packages/web/Cargo.toml +++ b/packages/web/Cargo.toml @@ -27,7 +27,7 @@ tracing = { workspace = true } rustc-hash = { workspace = true } console_error_panic_hook = { version = "0.1.7", optional = true } futures-util = { workspace = true, features = [ - "std", + # "std", "async-await", "async-await-macro", ] } @@ -72,7 +72,7 @@ document = ["dioxus-html/document", "dep:serde-wasm-bindgen", "dep:serde_json", dioxus = { workspace = true, default-features = true } wasm-bindgen-test = "0.3.29" dioxus-ssr = { workspace = true, default-features = false } -gloo-timers = "0.2.3" +gloo-timers = { workspace = true } gloo-dialogs = "0.1.1" dioxus-web = { path = ".", features = ["hydrate"] } tracing-wasm = "0.2.1" From fa6651be76bc297695da66335cc447c96480abd2 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Sat, 17 Aug 2024 00:26:25 -0700 Subject: [PATCH 014/139] bump versions --- Cargo.toml | 56 +++++++++++++++++++++++++++--------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5233dceeff..06bb207a10 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,34 +63,34 @@ version = "0.6.0-alpha.2" # dependencies that are shared across packages [workspace.dependencies] -dioxus = { path = "packages/dioxus", version = "0.6.0-alpha.1" } -dioxus-lib = { path = "packages/dioxus-lib", version = "0.6.0-alpha.1" } -dioxus-core = { path = "packages/core", version = "0.6.0-alpha.1" } -dioxus-core-macro = { path = "packages/core-macro", version = "0.6.0-alpha.1" } -dioxus-config-macro = { path = "packages/config-macro", version = "0.6.0-alpha.1" } -dioxus-router = { path = "packages/router", version = "0.6.0-alpha.1" } -dioxus-router-macro = { path = "packages/router-macro", version = "0.6.0-alpha.1" } -dioxus-html = { path = "packages/html", version = "0.6.0-alpha.1", default-features = false } -dioxus-html-internal-macro = { path = "packages/html-internal-macro", version = "0.6.0-alpha.1" } -dioxus-hooks = { path = "packages/hooks", version = "0.6.0-alpha.1" } -dioxus-web = { path = "packages/web", version = "0.6.0-alpha.1", default-features = false } -dioxus-ssr = { path = "packages/ssr", version = "0.6.0-alpha.1", default-features = false } -dioxus-desktop = { path = "packages/desktop", version = "0.6.0-alpha.1", default-features = false } -dioxus-mobile = { path = "packages/mobile", version = "0.6.0-alpha.1" } -dioxus-interpreter-js = { path = "packages/interpreter", version = "0.6.0-alpha.1" } -dioxus-liveview = { path = "packages/liveview", version = "0.6.0-alpha.1" } -dioxus-autofmt = { path = "packages/autofmt", version = "0.6.0-alpha.1" } -dioxus-check = { path = "packages/check", version = "0.6.0-alpha.1" } -dioxus-rsx = { path = "packages/rsx", version = "0.6.0-alpha.1" } -rsx-rosetta = { path = "packages/rsx-rosetta", version = "0.6.0-alpha.1" } -dioxus-signals = { path = "packages/signals", version = "0.6.0-alpha.1" } -dioxus-cli-config = { path = "packages/cli-config", version = "0.6.0-alpha.1", default-features = false} -generational-box = { path = "packages/generational-box", version = "0.6.0-alpha.1" } -dioxus-hot-reload = { path = "packages/hot-reload", version = "0.6.0-alpha.1" } -dioxus-fullstack = { path = "packages/fullstack", version = "0.6.0-alpha.1" } -dioxus-static-site-generation = { path = "packages/static-generation", version = "0.6.0-alpha.1" } -dioxus_server_macro = { path = "packages/server-macro", version = "0.6.0-alpha.1", default-features = false } -lazy-js-bundle = { path = "packages/lazy-js-bundle", version = "0.6.0-alpha.1" } +dioxus = { path = "packages/dioxus", version = "0.6.0-alpha.2" } +dioxus-lib = { path = "packages/dioxus-lib", version = "0.6.0-alpha.2" } +dioxus-core = { path = "packages/core", version = "0.6.0-alpha.2" } +dioxus-core-macro = { path = "packages/core-macro", version = "0.6.0-alpha.2" } +dioxus-config-macro = { path = "packages/config-macro", version = "0.6.0-alpha.2" } +dioxus-router = { path = "packages/router", version = "0.6.0-alpha.2" } +dioxus-router-macro = { path = "packages/router-macro", version = "0.6.0-alpha.2" } +dioxus-html = { path = "packages/html", version = "0.6.0-alpha.2", default-features = false } +dioxus-html-internal-macro = { path = "packages/html-internal-macro", version = "0.6.0-alpha.2" } +dioxus-hooks = { path = "packages/hooks", version = "0.6.0-alpha.2" } +dioxus-web = { path = "packages/web", version = "0.6.0-alpha.2", default-features = false } +dioxus-ssr = { path = "packages/ssr", version = "0.6.0-alpha.2", default-features = false } +dioxus-desktop = { path = "packages/desktop", version = "0.6.0-alpha.2", default-features = false } +dioxus-mobile = { path = "packages/mobile", version = "0.6.0-alpha.2" } +dioxus-interpreter-js = { path = "packages/interpreter", version = "0.6.0-alpha.2" } +dioxus-liveview = { path = "packages/liveview", version = "0.6.0-alpha.2" } +dioxus-autofmt = { path = "packages/autofmt", version = "0.6.0-alpha.2" } +dioxus-check = { path = "packages/check", version = "0.6.0-alpha.2" } +dioxus-rsx = { path = "packages/rsx", version = "0.6.0-alpha.2" } +rsx-rosetta = { path = "packages/rsx-rosetta", version = "0.6.0-alpha.2" } +dioxus-signals = { path = "packages/signals", version = "0.6.0-alpha.2" } +dioxus-cli-config = { path = "packages/cli-config", version = "0.6.0-alpha.2", default-features = false} +generational-box = { path = "packages/generational-box", version = "0.6.0-alpha.2" } +dioxus-hot-reload = { path = "packages/hot-reload", version = "0.6.0-alpha.2" } +dioxus-fullstack = { path = "packages/fullstack", version = "0.6.0-alpha.2" } +dioxus-static-site-generation = { path = "packages/static-generation", version = "0.6.0-alpha.2" } +dioxus_server_macro = { path = "packages/server-macro", version = "0.6.0-alpha.2", default-features = false } +lazy-js-bundle = { path = "packages/lazy-js-bundle", version = "0.6.0-alpha.2" } manganis-cli-support = { version = "0.3.0-alpha.0", features = ["html"], path = "../ecosystem-dioxus/manganis/packages/cli-support" } manganis = { version = "0.3.0-alpha.0", default-features = false, features = ["macro"], path = "../ecosystem-dioxus/manganis/packages/manganis" } From 9db4eb610be07b20999f2c7f683e87a1f8bedd6f Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Sat, 17 Aug 2024 00:32:18 -0700 Subject: [PATCH 015/139] ... screw it, bring in manganis --- Cargo.lock | 57 +- Cargo.toml | 22 +- packages/manganis-cli-support/Cargo.toml | 62 ++ packages/manganis-cli-support/README.md | 58 ++ packages/manganis-cli-support/examples/cli.rs | 74 +++ packages/manganis-cli-support/src/file.rs | 278 +++++++++ packages/manganis-cli-support/src/folder.rs | 59 ++ packages/manganis-cli-support/src/lib.rs | 14 + .../src/linker_intercept.rs | 186 ++++++ packages/manganis-cli-support/src/manifest.rs | 272 +++++++++ .../tests/collects_assets.rs | 93 +++ packages/manganis-common/Cargo.toml | 34 ++ packages/manganis-common/build.rs | 3 + packages/manganis-common/src/asset.rs | 47 ++ packages/manganis-common/src/asset/error.rs | 62 ++ packages/manganis-common/src/asset/file.rs | 169 ++++++ packages/manganis-common/src/asset/folder.rs | 114 ++++ packages/manganis-common/src/asset/meta.rs | 38 ++ .../manganis-common/src/asset/resource.rs | 537 ++++++++++++++++ .../manganis-common/src/asset/tailwind.rs | 31 + packages/manganis-common/src/built.rs | 2 + packages/manganis-common/src/config.rs | 41 ++ packages/manganis-common/src/file.rs | 574 ++++++++++++++++++ packages/manganis-common/src/lib.rs | 12 + packages/manganis-common/src/linker.rs | 75 +++ packages/manganis-macro/Cargo.toml | 35 ++ packages/manganis-macro/README.md | 3 + packages/manganis-macro/src/asset.rs | 179 ++++++ packages/manganis-macro/src/css.rs | 130 ++++ packages/manganis-macro/src/font.rs | 173 ++++++ packages/manganis-macro/src/image.rs | 305 ++++++++++ packages/manganis-macro/src/js.rs | 150 +++++ packages/manganis-macro/src/json.rs | 70 +++ packages/manganis-macro/src/lib.rs | 242 ++++++++ packages/manganis-resolver/Cargo.toml | 15 + packages/manganis-resolver/src/lib.rs | 19 + packages/manganis-test-package/Cargo.toml | 12 + .../manganis-test-package/assets/asset.txt | 0 .../manganis-test-package/assets/data.json | 6 + .../manganis-test-package/assets/script.js | 3 + .../manganis-test-package/assets/test.mp4 | 0 packages/manganis-test-package/src/main.rs | 43 ++ .../test-package-dependency/Cargo.toml | 11 + .../test-package-dependency/src/asset.txt | 1 + .../test-package-dependency/src/file.rs | 7 + .../test-package-dependency/src/lib.rs | 8 + .../test-package-nested-dependency/Cargo.toml | 11 + .../all_the_assets/data.json | 6 + .../all_the_assets/rustacean-flat-gesture.png | Bin 0 -> 49537 bytes .../all_the_assets/script.js | 7 + .../all_the_assets/style.css | 8 + .../src/file.rs | 31 + .../src/folder.rs | 3 + .../src/font.rs | 16 + .../test-package-nested-dependency/src/js.rs | 4 + .../test-package-nested-dependency/src/lib.rs | 13 + packages/manganis/Cargo.toml | 33 + packages/manganis/src/asset.rs | 132 ++++ packages/manganis/src/builder.rs | 298 +++++++++ packages/manganis/src/csss.rs | 65 ++ packages/manganis/src/files.rs | 16 + packages/manganis/src/folder.rs | 24 + packages/manganis/src/fonts.rs | 75 +++ packages/manganis/src/images.rs | 177 ++++++ packages/manganis/src/jsons.rs | 36 ++ packages/manganis/src/jss.rs | 48 ++ packages/manganis/src/lib.rs | 56 ++ packages/manganis/src/video.rs | 16 + 68 files changed, 5394 insertions(+), 7 deletions(-) create mode 100644 packages/manganis-cli-support/Cargo.toml create mode 100644 packages/manganis-cli-support/README.md create mode 100644 packages/manganis-cli-support/examples/cli.rs create mode 100644 packages/manganis-cli-support/src/file.rs create mode 100644 packages/manganis-cli-support/src/folder.rs create mode 100644 packages/manganis-cli-support/src/lib.rs create mode 100644 packages/manganis-cli-support/src/linker_intercept.rs create mode 100644 packages/manganis-cli-support/src/manifest.rs create mode 100644 packages/manganis-cli-support/tests/collects_assets.rs create mode 100644 packages/manganis-common/Cargo.toml create mode 100644 packages/manganis-common/build.rs create mode 100644 packages/manganis-common/src/asset.rs create mode 100644 packages/manganis-common/src/asset/error.rs create mode 100644 packages/manganis-common/src/asset/file.rs create mode 100644 packages/manganis-common/src/asset/folder.rs create mode 100644 packages/manganis-common/src/asset/meta.rs create mode 100644 packages/manganis-common/src/asset/resource.rs create mode 100644 packages/manganis-common/src/asset/tailwind.rs create mode 100644 packages/manganis-common/src/built.rs create mode 100644 packages/manganis-common/src/config.rs create mode 100644 packages/manganis-common/src/file.rs create mode 100644 packages/manganis-common/src/lib.rs create mode 100644 packages/manganis-common/src/linker.rs create mode 100644 packages/manganis-macro/Cargo.toml create mode 100644 packages/manganis-macro/README.md create mode 100644 packages/manganis-macro/src/asset.rs create mode 100644 packages/manganis-macro/src/css.rs create mode 100644 packages/manganis-macro/src/font.rs create mode 100644 packages/manganis-macro/src/image.rs create mode 100644 packages/manganis-macro/src/js.rs create mode 100644 packages/manganis-macro/src/json.rs create mode 100644 packages/manganis-macro/src/lib.rs create mode 100644 packages/manganis-resolver/Cargo.toml create mode 100644 packages/manganis-resolver/src/lib.rs create mode 100644 packages/manganis-test-package/Cargo.toml create mode 100644 packages/manganis-test-package/assets/asset.txt create mode 100644 packages/manganis-test-package/assets/data.json create mode 100644 packages/manganis-test-package/assets/script.js create mode 100644 packages/manganis-test-package/assets/test.mp4 create mode 100644 packages/manganis-test-package/src/main.rs create mode 100644 packages/manganis-test-package/test-package-dependency/Cargo.toml create mode 100644 packages/manganis-test-package/test-package-dependency/src/asset.txt create mode 100644 packages/manganis-test-package/test-package-dependency/src/file.rs create mode 100644 packages/manganis-test-package/test-package-dependency/src/lib.rs create mode 100644 packages/manganis-test-package/test-package-nested-dependency/Cargo.toml create mode 100644 packages/manganis-test-package/test-package-nested-dependency/all_the_assets/data.json create mode 100644 packages/manganis-test-package/test-package-nested-dependency/all_the_assets/rustacean-flat-gesture.png create mode 100644 packages/manganis-test-package/test-package-nested-dependency/all_the_assets/script.js create mode 100644 packages/manganis-test-package/test-package-nested-dependency/all_the_assets/style.css create mode 100644 packages/manganis-test-package/test-package-nested-dependency/src/file.rs create mode 100644 packages/manganis-test-package/test-package-nested-dependency/src/folder.rs create mode 100644 packages/manganis-test-package/test-package-nested-dependency/src/font.rs create mode 100644 packages/manganis-test-package/test-package-nested-dependency/src/js.rs create mode 100644 packages/manganis-test-package/test-package-nested-dependency/src/lib.rs create mode 100644 packages/manganis/Cargo.toml create mode 100644 packages/manganis/src/asset.rs create mode 100644 packages/manganis/src/builder.rs create mode 100644 packages/manganis/src/csss.rs create mode 100644 packages/manganis/src/files.rs create mode 100644 packages/manganis/src/folder.rs create mode 100644 packages/manganis/src/fonts.rs create mode 100644 packages/manganis/src/images.rs create mode 100644 packages/manganis/src/jsons.rs create mode 100644 packages/manganis/src/jss.rs create mode 100644 packages/manganis/src/lib.rs create mode 100644 packages/manganis/src/video.rs diff --git a/Cargo.lock b/Cargo.lock index dd54a9f67b..5ef4594401 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6340,7 +6340,7 @@ dependencies = [ [[package]] name = "manganis" -version = "0.3.0-alpha.0" +version = "0.6.0-alpha.2" dependencies = [ "core-foundation 0.9.4", "dirs", @@ -6353,7 +6353,7 @@ dependencies = [ [[package]] name = "manganis-cli-support" -version = "0.3.0-alpha.0" +version = "0.6.0-alpha.2" dependencies = [ "anyhow", "image 0.25.2", @@ -6373,12 +6373,13 @@ dependencies = [ "swc", "swc_common", "tracing", + "tracing-subscriber", "url", ] [[package]] name = "manganis-common" -version = "0.3.0-alpha.0" +version = "0.6.0-alpha.2" dependencies = [ "anyhow", "base64 0.22.1", @@ -6391,7 +6392,7 @@ dependencies = [ [[package]] name = "manganis-macro" -version = "0.3.0-alpha.0" +version = "0.6.0-alpha.2" dependencies = [ "base64 0.22.1", "manganis-common", @@ -6402,6 +6403,17 @@ dependencies = [ "syn 2.0.74", ] +[[package]] +name = "manganis-resolver" +version = "0.6.0-alpha.2" +dependencies = [ + "core-foundation 0.9.4", + "dunce", + "infer 0.16.0", + "manganis", + "manganis-common", +] + [[package]] name = "markup5ever" version = "0.11.0" @@ -6605,7 +6617,7 @@ dependencies = [ "cc", "dunce", "libc", - "nasm-rs", + "nasm-rs 0.3.0", ] [[package]] @@ -6653,6 +6665,15 @@ dependencies = [ "rand 0.8.5", ] +[[package]] +name = "nasm-rs" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe4d98d0065f4b1daf164b3eafb11974c94662e5e2396cf03f32d0bb5c17da51" +dependencies = [ + "rayon", +] + [[package]] name = "nasm-rs" version = "0.3.0" @@ -8279,6 +8300,7 @@ dependencies = [ "av1-grain", "bitstream-io", "built", + "cc", "cfg-if", "interpolate_name", "itertools 0.12.1", @@ -8286,6 +8308,7 @@ dependencies = [ "libfuzzer-sys", "log", "maybe-rayon", + "nasm-rs 0.2.5", "new_debug_unreachable", "noop_proc_macro", "num-derive", @@ -11263,6 +11286,30 @@ dependencies = [ "winapi", ] +[[package]] +name = "test-package" +version = "0.2.1" +dependencies = [ + "manganis", + "test-package-dependency", + "tracing-subscriber", +] + +[[package]] +name = "test-package-dependency" +version = "0.2.1" +dependencies = [ + "manganis", + "test-package-nested-dependency", +] + +[[package]] +name = "test-package-nested-dependency" +version = "0.2.1" +dependencies = [ + "manganis", +] + [[package]] name = "textwrap" version = "0.16.1" diff --git a/Cargo.toml b/Cargo.toml index 06bb207a10..89b5388bc9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,6 +55,16 @@ members = [ "packages/playwright-tests/fullstack", "packages/playwright-tests/suspense-carousel", "packages/playwright-tests/nested-suspense", + + # manganis + "packages/manganis", + "packages/manganis-macro", + "packages/manganis-common", + "packages/manganis-cli-support", + "packages/manganis-resolver", + "packages/manganis-test-package", + "packages/manganis-test-package/test-package-dependency", + "packages/manganis-test-package/test-package-nested-dependency", ] exclude = ["examples/mobile_demo", "examples/openid_connect_demo"] @@ -92,10 +102,17 @@ dioxus-static-site-generation = { path = "packages/static-generation", version = dioxus_server_macro = { path = "packages/server-macro", version = "0.6.0-alpha.2", default-features = false } lazy-js-bundle = { path = "packages/lazy-js-bundle", version = "0.6.0-alpha.2" } -manganis-cli-support = { version = "0.3.0-alpha.0", features = ["html"], path = "../ecosystem-dioxus/manganis/packages/cli-support" } -manganis = { version = "0.3.0-alpha.0", default-features = false, features = ["macro"], path = "../ecosystem-dioxus/manganis/packages/manganis" } +manganis = { path = "packages/manganis", version = "0.6.0-alpha.2" } +manganis-common = { path = "packages/manganis-common", version = "0.6.0-alpha.2" } +manganis-cli-support = { path = "packages/manganis-cli-support", version = "0.6.0-alpha.2" } +manganis-macro = { path = "packages/manganis-macro", version = "0.6.0-alpha.2" } +manganis-resolver = { path = "packages/manganis-resolver", version = "0.6.0-alpha.2" } + + warnings = { version = "0.2.0" } +# manganis-cli-support = { version = "0.3.0-alpha.0", features = ["html"], path = "../ecosystem-dioxus/manganis/packages/cli-support" } +# manganis = { version = "0.3.0-alpha.0", default-features = false, features = ["macro"], path = "../ecosystem-dioxus/manganis/packages/manganis" } # manganis-cli-support = { version = "0.3.0-alpha.0", features = ["html"] } # manganis = { version = "0.3.0-alpha.0", default-features = false } # manganis-cli-support = { version = "0.3.0-alpha.1", features = ["html"] } @@ -151,6 +168,7 @@ uuid = "1.9.1" convert_case = "0.6.0" tokio-tungstenite = { version = "0.23.1" } gloo-timers = "0.3.0" +fluent-uri = { version = "0.2.0", features = ["serde"] } # desktop wry = { version = "0.42.0", default-features = false } diff --git a/packages/manganis-cli-support/Cargo.toml b/packages/manganis-cli-support/Cargo.toml new file mode 100644 index 0000000000..912f8ec5e7 --- /dev/null +++ b/packages/manganis-cli-support/Cargo.toml @@ -0,0 +1,62 @@ +[package] +name = "manganis-cli-support" +version.workspace = true +edition = "2021" +authors = ["Evan Almloff"] +description = "Ergonomic, automatic, cross crate asset collection and optimization" +license = "MIT OR Apache-2.0" +repository = "https://github.com/DioxusLabs/manganis/" +homepage = "https://dioxuslabs.com" +keywords = ["assets"] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +manganis-common = { workspace = true } + +serde = { version = "1.0.183", features = ["derive"] } +serde_json = {version="1.0.116"} +anyhow = "1" +rayon = "1.7.0" +rustc-hash = "1.1.0" + +# Tailwind +railwind = "0.1.5" + +# Image compression/conversion +# JPEG +mozjpeg = { version = "0.10.7", default-features = false, features = ["parallel"] } +# PNG +imagequant = "4.2.0" +png = "0.17.9" +# Conversion +image = { version = "0.25" } +ravif = { version = "0.11", default-features = false } + +# CSS Minification +lightningcss = "1.0.0-alpha.44" + +# Js minification +swc = "=0.283.0" +swc_common = "=0.37.1" + +# Remote assets +url = { version = "2.4.0", features = ["serde"] } +reqwest = { version = "0.12.5", features = ["blocking"] } +tracing = "0.1.37" + +# Extracting data from an executable +object = {version="0.36.0", features=["wasm"]} + +[dev-dependencies] +tracing-subscriber = "0.3.18" + +[features] +default = ["html"] +html = ["manganis-common/html"] + +asm = ["ravif/asm", "mozjpeg/nasm_simd"] + +# Note: this feature now enables nothing and should be removed in the next major version +webp = [] +avif = [] diff --git a/packages/manganis-cli-support/README.md b/packages/manganis-cli-support/README.md new file mode 100644 index 0000000000..5ad9e5d3ea --- /dev/null +++ b/packages/manganis-cli-support/README.md @@ -0,0 +1,58 @@ +# Manganis CLI Support + +This crate provides utilities to collect assets that integrate with the Manganis macro. It makes it easy to integrate an asset collection and optimization system into a build tool. + +```rust, no_run +use manganis_cli_support::{AssetManifestExt, ManganisSupportGuard}; +use manganis_common::{AssetManifest, Config}; +use std::process::Command; + +// This is the location where the assets will be copied to in the filesystem +let assets_file_location = "./assets"; +// This is the location where the assets will be served from +let assets_serve_location = "/assets"; + +// First set any settings you need for the build. +Config::default() + .with_assets_serve_location(assets_serve_location) + .save(); + +// Tell manganis that you support assets +let _guard = ManganisSupportGuard::default(); + +// Determine if Rust is trying to link: +if let Some((_working_dir, object_files)) = manganis_cli_support::linker_intercept(std::env::args()) { + // If it is, collect the assets. + let manifest = AssetManifest::load(object_files); + + // Remove the old assets + let _ = std::fs::remove_dir_all(assets_file_location); + + // And copy the static assets to the public directory + manifest + .copy_static_assets_to(assets_file_location) + .unwrap(); + + // Then collect the tailwind CSS + let css = manifest.collect_tailwind_css(true, &mut Vec::new()); + + // And write the CSS to the public directory + std::fs::write(format!("{}/tailwind.css", assets_file_location), css).unwrap(); + +} else { + // If it isn't, build your app and initiate the helper function `start_linker_intercept()` + + // Put any cargo args in a slice that should also be passed + // to manganis toreproduce the same build. e.g. the `--release` flag + let args: Vec<&str> = vec![]; + Command::new("cargo") + .arg("build") + .args(args.clone()) + .spawn() + .unwrap() + .wait() + .unwrap(); + + manganis_cli_support::start_linker_intercept(None, args).unwrap(); +} +``` diff --git a/packages/manganis-cli-support/examples/cli.rs b/packages/manganis-cli-support/examples/cli.rs new file mode 100644 index 0000000000..5a5599e710 --- /dev/null +++ b/packages/manganis-cli-support/examples/cli.rs @@ -0,0 +1,74 @@ +use manganis_cli_support::AssetManifestExt; +use std::{path::PathBuf, process::Command}; + +// This is the location where the assets will be copied to in the filesystem +const ASSETS_FILE_LOCATION: &str = "./assets"; + +// This is the location where the assets will be served from +const ASSETS_SERVE_LOCATION: &str = "./assets/"; + +fn main() { + tracing_subscriber::fmt::init(); + + // Handle the commands. + let args: Vec = std::env::args().collect(); + + if let Some(arg) = args.get(1) { + if arg == "link" { + link(); + return; + } else if arg == "build" { + println!("Building!"); + build(); + return; + } + } + + println!("Unknown Command"); +} + +fn build() { + // Build your application + let current_dir = std::env::current_dir().unwrap(); + + let args = ["--release"]; + Command::new("cargo") + .current_dir(¤t_dir) + .arg("build") + .args(args) + .env("MG_BASEPATH", "/blah/") + .spawn() + .unwrap() + .wait() + .unwrap(); + + // Call the helper function to intercept the Rust linker. + // We will pass the current working directory as it may get lost. + let work_dir = std::env::current_dir().unwrap(); + let link_args = vec![format!("{}", work_dir.display())]; + manganis_cli_support::start_linker_intercept("link", args, Some(link_args)).unwrap(); +} + +fn link() { + let (link_args, object_files) = + manganis_cli_support::linker_intercept(std::env::args()).unwrap(); + + // // Extract the assets + // let assets = AssetManifest::load_from_objects(object_files); + + // let working_dir = PathBuf::from(link_args.first().unwrap()); + // let assets_dir = working_dir.join(working_dir.join(ASSETS_FILE_LOCATION)); + + // // Remove the old assets + // let _ = std::fs::remove_dir_all(&assets_dir); + + // // And copy the static assets to the public directory + // assets.copy_static_assets_to(&assets_dir).unwrap(); + + // // Then collect the tailwind CSS + // let css = assets.collect_tailwind_css(true, &mut Vec::new()); + + // // And write the CSS to the public directory + // let tailwind_path = assets_dir.join("tailwind.css"); + // std::fs::write(tailwind_path, css).unwrap(); +} diff --git a/packages/manganis-cli-support/src/file.rs b/packages/manganis-cli-support/src/file.rs new file mode 100644 index 0000000000..d097dfaad2 --- /dev/null +++ b/packages/manganis-cli-support/src/file.rs @@ -0,0 +1,278 @@ +use anyhow::Context; +use image::{DynamicImage, EncodableLayout}; +use lightningcss::stylesheet::{MinifyOptions, ParserOptions, PrinterOptions, StyleSheet}; +use manganis_common::{ + CssOptions, FileOptions, ImageOptions, ImageType, JsOptions, JsonOptions, ResourceAsset, +}; +use std::{ + io::{BufWriter, Write}, + path::Path, + sync::Arc, +}; +use swc::{config::JsMinifyOptions, try_with_handler, BoolOrDataConfig}; +use swc_common::{sync::Lrc, FileName}; +use swc_common::{SourceMap, GLOBALS}; + +pub trait Process { + fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()>; +} + +/// Process a specific file asset +pub fn process_file(file: &ResourceAsset, output_folder: &Path) -> anyhow::Result<()> { + todo!() + // let location = file.location(); + // let source = location.source(); + // let output_path = output_folder.join(location.unique_name()); + // file.options().process(source, &output_path) +} + +impl Process for FileOptions { + fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { + if output_path.exists() { + return Ok(()); + } + match self { + Self::Other { .. } => { + let bytes = source.read_to_bytes()?; + std::fs::write(output_path, bytes).with_context(|| { + format!( + "Failed to write file to output location: {}", + output_path.display() + ) + })?; + } + Self::Css(options) => { + options.process(source, output_path)?; + } + Self::Js(options) => { + options.process(source, output_path)?; + } + Self::Json(options) => { + options.process(source, output_path)?; + } + Self::Image(options) => { + options.process(source, output_path)?; + } + _ => todo!(), + } + + Ok(()) + } +} + +impl Process for ImageOptions { + fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { + let mut image = image::ImageReader::new(std::io::Cursor::new(&*source.read_to_bytes()?)) + .with_guessed_format()? + .decode()?; + + if let Some(size) = self.size() { + image = image.resize_exact(size.0, size.1, image::imageops::FilterType::Lanczos3); + } + + match self.ty() { + ImageType::Png => { + compress_png(image, output_path); + } + ImageType::Jpg => { + compress_jpg(image, output_path)?; + } + ImageType::Avif => { + if let Err(error) = image.save(output_path) { + tracing::error!("Failed to save avif image: {} with path {}. You must have the avif feature enabled to use avif assets", error, output_path.display()); + } + } + ImageType::Webp => { + if let Err(err) = image.save(output_path) { + tracing::error!("Failed to save webp image: {}. You must have the avif feature enabled to use webp assets", err); + } + } + } + + Ok(()) + } +} + +fn compress_jpg(image: DynamicImage, output_location: &Path) -> anyhow::Result<()> { + let mut comp = mozjpeg::Compress::new(mozjpeg::ColorSpace::JCS_EXT_RGBX); + let width = image.width() as usize; + let height = image.height() as usize; + + comp.set_size(width, height); + let mut comp = comp.start_compress(Vec::new())?; // any io::Write will work + + comp.write_scanlines(image.to_rgba8().as_bytes())?; + + let jpeg_bytes = comp.finish()?; + + let file = std::fs::File::create(output_location)?; + let w = &mut BufWriter::new(file); + w.write_all(&jpeg_bytes)?; + Ok(()) +} + +fn compress_png(image: DynamicImage, output_location: &Path) { + // Image loading/saving is outside scope of this library + let width = image.width() as usize; + let height = image.height() as usize; + let bitmap: Vec<_> = image + .into_rgba8() + .pixels() + .map(|px| imagequant::RGBA::new(px[0], px[1], px[2], px[3])) + .collect(); + + // Configure the library + let mut liq = imagequant::new(); + liq.set_speed(5).unwrap(); + liq.set_quality(0, 99).unwrap(); + + // Describe the bitmap + let mut img = liq.new_image(&bitmap[..], width, height, 0.0).unwrap(); + + // The magic happens in quantize() + let mut res = match liq.quantize(&mut img) { + Ok(res) => res, + Err(err) => panic!("Quantization failed, because: {err:?}"), + }; + + let (palette, pixels) = res.remapped(&mut img).unwrap(); + + let file = std::fs::File::create(output_location).unwrap(); + let w = &mut BufWriter::new(file); + + let mut encoder = png::Encoder::new(w, width as u32, height as u32); + encoder.set_color(png::ColorType::Rgba); + let mut flattened_palette = Vec::new(); + let mut alpha_palette = Vec::new(); + for px in palette { + flattened_palette.push(px.r); + flattened_palette.push(px.g); + flattened_palette.push(px.b); + alpha_palette.push(px.a); + } + encoder.set_palette(flattened_palette); + encoder.set_trns(alpha_palette); + encoder.set_depth(png::BitDepth::Eight); + encoder.set_color(png::ColorType::Indexed); + encoder.set_compression(png::Compression::Best); + let mut writer = encoder.write_header().unwrap(); + writer.write_image_data(&pixels).unwrap(); + writer.finish().unwrap(); +} + +impl Process for CssOptions { + fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { + let css = source.read_to_string()?; + + let css = if self.minify() { minify_css(&css) } else { css }; + + std::fs::write(output_path, css).with_context(|| { + format!( + "Failed to write css to output location: {}", + output_path.display() + ) + })?; + + Ok(()) + } +} + +pub(crate) fn minify_css(css: &str) -> String { + let mut stylesheet = StyleSheet::parse(css, ParserOptions::default()).unwrap(); + stylesheet.minify(MinifyOptions::default()).unwrap(); + let printer = PrinterOptions { + minify: true, + ..Default::default() + }; + let res = stylesheet.to_css(printer).unwrap(); + res.code +} + +pub(crate) fn minify_js(source: &ResourceAsset) -> anyhow::Result { + let cm = Arc::::default(); + + let js = source.read_to_string()?; + let c = swc::Compiler::new(cm.clone()); + let output = GLOBALS + .set(&Default::default(), || { + try_with_handler(cm.clone(), Default::default(), |handler| { + // let filename = Lrc::new(match source { + // manganis_common::ResourceAsset::Local(path) => { + // FileName::Real(path.canonicalized.clone()) + // } + // manganis_common::ResourceAsset::Remote(url) => FileName::Url(url.clone()), + // }); + let filename = todo!(); + let fm = cm.new_source_file(filename, js.to_string()); + + c.minify( + fm, + handler, + &JsMinifyOptions { + compress: BoolOrDataConfig::from_bool(true), + mangle: BoolOrDataConfig::from_bool(true), + ..Default::default() + }, + ) + .context("failed to minify javascript") + }) + }) + .map(|output| output.code); + + match output { + Ok(output) => Ok(output), + Err(err) => { + tracing::error!("Failed to minify javascript: {}", err); + Ok(js) + } + } +} + +impl Process for JsOptions { + fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { + let js = if self.minify() { + minify_js(source)? + } else { + source.read_to_string()? + }; + + std::fs::write(output_path, js).with_context(|| { + format!( + "Failed to write js to output location: {}", + output_path.display() + ) + })?; + + Ok(()) + } +} + +pub(crate) fn minify_json(source: &str) -> anyhow::Result { + // First try to parse the json + let json: serde_json::Value = serde_json::from_str(source)?; + // Then print it in a minified format + let json = serde_json::to_string(&json)?; + Ok(json) +} + +impl Process for JsonOptions { + fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { + let source = source.read_to_string()?; + let json = match minify_json(&source) { + Ok(json) => json, + Err(err) => { + tracing::error!("Failed to minify json: {}", err); + source + } + }; + + std::fs::write(output_path, json).with_context(|| { + format!( + "Failed to write json to output location: {}", + output_path.display() + ) + })?; + + Ok(()) + } +} diff --git a/packages/manganis-cli-support/src/folder.rs b/packages/manganis-cli-support/src/folder.rs new file mode 100644 index 0000000000..ddb4c1b900 --- /dev/null +++ b/packages/manganis-cli-support/src/folder.rs @@ -0,0 +1,59 @@ +use std::path::Path; + +use manganis_common::{FileOptions, FolderAsset}; +use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; + +use crate::file::Process; + +/// Process a folder, optimizing and copying all assets into the output folder +pub fn process_folder(folder: &FolderAsset, output_folder: &Path) -> anyhow::Result<()> { + // Push the unique name of the folder to the output folder + let output_folder = output_folder.join(folder.unique_name()); + + if output_folder.exists() { + return Ok(()); + } + + // .location() + // // .source() + // .as_path() + let folder = folder.path(); + + // Optimize and copy all assets in the folder in parallel + process_folder_inner(folder, &output_folder) +} + +fn process_folder_inner(folder: &Path, output_folder: &Path) -> anyhow::Result<()> { + // Create the folder + std::fs::create_dir_all(output_folder)?; + + // Then optimize children + let files: Vec<_> = std::fs::read_dir(folder) + .into_iter() + .flatten() + .flatten() + .collect(); + + files.par_iter().try_for_each(|file| { + let file = file.path(); + let metadata = file.metadata()?; + let output_path = output_folder.join(file.strip_prefix(folder)?); + if metadata.is_dir() { + process_folder_inner(&file, &output_path) + } else { + process_file_minimal(&file, &output_path) + } + })?; + + Ok(()) +} + +/// Optimize a file without changing any of its contents significantly (e.g. by changing the extension) +fn process_file_minimal(input_path: &Path, output_path: &Path) -> anyhow::Result<()> { + todo!() + // let options = + // FileOptions::default_for_extension(input_path.extension().and_then(|e| e.to_str())); + // let source = input_path.to_path_buf(); + // options.process(&source, output_path)?; + // Ok(()) +} diff --git a/packages/manganis-cli-support/src/lib.rs b/packages/manganis-cli-support/src/lib.rs new file mode 100644 index 0000000000..ef3b85e52c --- /dev/null +++ b/packages/manganis-cli-support/src/lib.rs @@ -0,0 +1,14 @@ +#![doc = include_str!("../README.md")] +#![deny(missing_docs)] + +#[allow(hidden_glob_reexports)] +mod file; +mod folder; +mod linker_intercept; +mod manifest; + +pub use file::process_file; +pub use folder::process_folder; +pub use linker_intercept::*; +pub use manganis_common::*; +pub use manifest::*; diff --git a/packages/manganis-cli-support/src/linker_intercept.rs b/packages/manganis-cli-support/src/linker_intercept.rs new file mode 100644 index 0000000000..39e15d76bc --- /dev/null +++ b/packages/manganis-cli-support/src/linker_intercept.rs @@ -0,0 +1,186 @@ +use std::{ffi::OsStr, fs, path::PathBuf, process::Stdio}; + +// The prefix to link args passed from parent process. +const MG_ARG_NAME: &str = "mg-arg="; + +/// Intercept the linker for object files. +/// +/// Takes the arguments used in a CLI and returns a list of paths to `.rlib` or `.o` files to be searched for asset sections. +pub fn linker_intercept(args: I) -> Option<(Vec, Vec)> +where + I: IntoIterator, + T: ToString, +{ + let args: Vec = args.into_iter().map(|x| x.to_string()).collect(); + + // Check if we were provided with a command file. + let mut is_command_file = None; + for arg in args.iter() { + // On windows the linker args are passed in a file that is referenced by `@` + if arg.starts_with('@') { + is_command_file = Some(arg.clone()); + break; + } + } + + let linker_args = match is_command_file { + // On unix/linux/mac the linker args are passed directly. + None => args, + // Handle windows here - uf16le and utf8 files are supported. + Some(arg) => { + let path = arg.trim().trim_start_matches('@'); + let file_binary = fs::read(path).unwrap(); + + // This may be a utf-16le file. Let's try utf-8 first. + let content = match String::from_utf8(file_binary.clone()) { + Ok(s) => s, + Err(_) => { + // Convert Vec to Vec to convert into a String + let binary_u16le: Vec = file_binary + .chunks_exact(2) + .map(|a| u16::from_le_bytes([a[0], a[1]])) + .collect(); + + String::from_utf16_lossy(&binary_u16le) + } + }; + + // Gather linker args + let mut linker_args = Vec::new(); + let lines = content.lines(); + + for line in lines { + // Remove quotes from the line - windows link args files are quoted + let line_parsed = line.to_string(); + let line_parsed = line_parsed.trim_end_matches('"').to_string(); + let line_parsed = line_parsed.trim_start_matches('"').to_string(); + + linker_args.push(line_parsed); + } + + linker_args + } + }; + + let mut link_args = Vec::new(); + + // Parse through linker args for `.o` or `.rlib` files. + let mut object_files: Vec = Vec::new(); + for item in linker_args { + // Get the working directory so it isn't lost. + // When rust calls the linker it doesn't pass the working dir so we need to recover it. + // "{MG_WORKDIR_ARG_NAME}path" + if item.starts_with(MG_ARG_NAME) { + let split: Vec<_> = item.split('=').collect(); + link_args.push(split[1].to_string()); + continue; + } + + if item.ends_with(".o") || item.ends_with(".rlib") { + object_files.push(PathBuf::from(item)); + } + } + + if object_files.is_empty() { + return None; + } + + Some((link_args, object_files)) +} + +/// Calls cargo to build the project with a linker intercept script. +/// +/// The linker intercept script will call the current executable with the specified subcommand +/// and a list of arguments provided by rustc. +pub fn start_linker_intercept( + subcommand: &str, + args: I, + link_args: Option, +) -> Result<(), std::io::Error> +where + I: IntoIterator, + I::Item: AsRef, + J: IntoIterator, + J::Item: ToString, +{ + let exec_path = std::env::current_exe().unwrap(); + + let mut cmd = std::process::Command::new("cargo"); + cmd.arg("rustc"); + cmd.args(args); + cmd.arg("--"); + + // Build a temporary redirect script. + let script_path = create_linker_script(exec_path, subcommand).unwrap(); + let linker_arg = format!("-Clinker={}", script_path.display()); + cmd.arg(linker_arg); + + // Handle passing any arguments back to the current executable. + if let Some(link_args) = link_args { + let link_args: Vec = link_args.into_iter().map(|x| x.to_string()).collect(); + for link_arg in link_args { + let arg = format!("-Clink-arg={}{}", MG_ARG_NAME, link_arg); + cmd.arg(arg); + } + } + + cmd.stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn()? + .wait()?; + Ok(()) +} + +const LINK_SCRIPT_NAME: &str = "mg-link"; + +/// Creates a temporary script that re-routes rustc linker args to a subcommand of an executable. +fn create_linker_script(exec: PathBuf, subcommand: &str) -> Result { + #[cfg(windows)] + let (script, ext) = ( + format!("echo off\n{} {} %*", exec.display(), subcommand), + "bat", + ); + #[cfg(not(windows))] + let (script, ext) = ( + format!("#!/usr/bin/env bash\n{} {} $@", exec.display(), subcommand), + "sh", + ); + + let temp_path = std::env::temp_dir(); + let out_name = format!("{LINK_SCRIPT_NAME}.{ext}"); + let out = temp_path.join(out_name); + fs::write(&out, script)?; + + // Set executable permissions. + let mut perms = fs::metadata(&out)?.permissions(); + + // We give windows RW and implied X perms. + // Clippy complains on any platform about this even if it's not *nix. + // https://rust-lang.github.io/rust-clippy/master/index.html#permissions_set_readonly_false + #[cfg(windows)] + #[allow(clippy::permissions_set_readonly_false)] + perms.set_readonly(false); + + // We give nix user-RWX perms. + #[cfg(not(windows))] + { + use std::os::unix::fs::PermissionsExt; + perms.set_mode(0o700); + } + fs::set_permissions(&out, perms)?; + + Ok(out) +} + +/// Deletes the temporary script created by `create_linker_script`. +pub fn delete_linker_script() -> Result<(), std::io::Error> { + #[cfg(windows)] + let ext = "bat"; + #[cfg(not(windows))] + let ext = "sh"; + + let temp_path = std::env::temp_dir(); + let file_name = format!("{LINK_SCRIPT_NAME}.{ext}"); + let file = temp_path.join(file_name); + fs::remove_file(file) +} diff --git a/packages/manganis-cli-support/src/manifest.rs b/packages/manganis-cli-support/src/manifest.rs new file mode 100644 index 0000000000..e0da1fc320 --- /dev/null +++ b/packages/manganis-cli-support/src/manifest.rs @@ -0,0 +1,272 @@ +pub use railwind::warning::Warning as TailwindWarning; +use std::path::PathBuf; + +use manganis_common::{linker, AssetType}; + +use crate::{file::process_file, process_folder}; + +use object::{File, Object, ObjectSection}; +use std::fs; + +// get the text containing all the asset descriptions +// in the "link section" of the binary +fn get_string_manganis(file: &File) -> Option { + for section in file.sections() { + if let Ok(section_name) = section.name() { + // Check if the link section matches the asset section for one of the platforms we support. This may not be the current platform if the user is cross compiling + if linker::LinkSection::ALL + .iter() + .any(|x| x.link_section == section_name) + { + let bytes = section.uncompressed_data().ok()?; + // Some platforms (e.g. macOS) start the manganis section with a null byte, we need to filter that out before we deserialize the JSON + return Some( + std::str::from_utf8(&bytes) + .ok()? + .chars() + .filter(|c| !c.is_control()) + .collect::(), + ); + } + } + } + None +} + +/// A manifest of all assets collected from dependencies +#[derive(Debug, PartialEq, Default, Clone)] +pub struct AssetManifest { + pub(crate) assets: Vec, +} + +impl AssetManifest { + /// Creates a new asset manifest + pub fn new(assets: Vec) -> Self { + Self { assets } + } + + /// Returns all assets collected from dependencies + pub fn assets(&self) -> &Vec { + &self.assets + } + + #[cfg(feature = "html")] + /// Returns the HTML that should be injected into the head of the page + pub fn head(&self) -> String { + let mut head = String::new(); + for asset in &self.assets { + if let crate::AssetType::Resource(file) = asset { + match file.options() { + crate::FileOptions::Css(css_options) => { + if css_options.preload() { + if let Ok(asset_path) = file.served_location() { + head.push_str(&format!( + "\n" + )) + } + } + } + crate::FileOptions::Image(image_options) => { + if image_options.preload() { + if let Ok(asset_path) = file.served_location() { + head.push_str(&format!( + "\n" + )) + } + } + } + crate::FileOptions::Js(js_options) => { + if js_options.preload() { + if let Ok(asset_path) = file.served_location() { + head.push_str(&format!( + "\n" + )) + } + } + } + _ => {} + } + } + } + head + } +} + +/// An extension trait CLI support for the asset manifest +pub trait AssetManifestExt { + /// Load a manifest from a list of Manganis JSON strings. + /// + /// The asset descriptions are stored inside a manifest file that is produced when the linker is intercepted. + fn load(json: Vec) -> Self; + /// Load a manifest from the assets propogated through object files. + /// + /// The asset descriptions are stored inside a manifest file that is produced when the linker is intercepted. + fn load_from_objects(object_paths: Vec) -> Self; + /// Optimize and copy all assets in the manifest to a folder + fn copy_static_assets_to(&self, location: impl Into) -> anyhow::Result<()>; + /// Collect all tailwind classes and generate string with the output css + fn collect_tailwind_css( + &self, + include_preflight: bool, + warnings: &mut Vec, + ) -> String; +} + +impl AssetManifestExt for AssetManifest { + fn load(json: Vec) -> Self { + let mut all_assets = Vec::new(); + + // Collect all assets for each manganis string found. + for item in json { + let mut assets = deserialize_assets(item.as_str()); + all_assets.append(&mut assets); + } + + // If we don't see any manganis assets used in the binary, just return an empty manifest + if all_assets.is_empty() { + return Self::default(); + }; + + Self::new(all_assets) + } + + fn load_from_objects(object_files: Vec) -> Self { + let json = get_json_from_object_files(object_files); + Self::load(json) + } + + fn copy_static_assets_to(&self, location: impl Into) -> anyhow::Result<()> { + let location = location.into(); + match std::fs::create_dir_all(&location) { + Ok(_) => {} + Err(err) => { + tracing::error!("Failed to create directory for static assets: {}", err); + return Err(err.into()); + } + } + + self.assets().iter().try_for_each(|asset| { + match asset { + AssetType::Resource(file_asset) => { + tracing::info!("Optimizing and bundling {:?}", file_asset); + tracing::trace!("Copying asset from {:?} to {:?}", file_asset, location); + match process_file(file_asset, &location) { + Ok(_) => {} + Err(err) => { + tracing::error!("Failed to copy static asset: {}", err); + return Err(err); + } + } + + // tracing::info!("Copying folder asset {}", folder_asset); + // match process_folder(folder_asset, &location) { + // Ok(_) => {} + // Err(err) => { + // tracing::error!("Failed to copy static asset: {}", err); + // return Err(err); + // } + // } + } + + _ => {} + } + Ok::<(), anyhow::Error>(()) + }) + } + + fn collect_tailwind_css( + self: &AssetManifest, + include_preflight: bool, + warnings: &mut Vec, + ) -> String { + let mut all_classes = String::new(); + + for asset in self.assets() { + if let AssetType::Tailwind(classes) = asset { + all_classes.push_str(classes.classes()); + all_classes.push(' '); + } + } + + let source = railwind::Source::String(all_classes, railwind::CollectionOptions::String); + + let css = railwind::parse_to_string(source, include_preflight, warnings); + + crate::file::minify_css(&css) + } +} + +fn deserialize_assets(json: &str) -> Vec { + let deserializer = serde_json::Deserializer::from_str(json); + deserializer + .into_iter::() + .flat_map(|x| x.ok()) + // .map(|x| x.unwrap()) + .collect() +} + +/// Extract JSON Manganis strings from a list of object files. +pub fn get_json_from_object_files(object_paths: Vec) -> Vec { + let mut all_json = Vec::new(); + + for path in object_paths { + let Some(ext) = path.extension() else { + continue; + }; + + let Some(ext) = ext.to_str() else { + continue; + }; + + let is_rlib = match ext { + "rlib" => true, + "o" => false, + _ => continue, + }; + + // Read binary data and try getting assets from manganis string + let binary_data = fs::read(path).unwrap(); + + // rlibs are archives with object files inside. + let mut data = match is_rlib { + false => { + // Parse an unarchived object file. We use a Vec to match the return types. + let file = object::File::parse(&*binary_data).unwrap(); + let mut data = Vec::new(); + if let Some(string) = get_string_manganis(&file) { + data.push(string); + } + data + } + true => { + let file = object::read::archive::ArchiveFile::parse(&*binary_data).unwrap(); + + // rlibs can contain many object files so we collect each manganis string here. + let mut manganis_strings = Vec::new(); + + // Look through each archive member for object files. + // Read the archive member's binary data (we know it's an object file) + // And parse it with the normal `object::File::parse` to find the manganis string. + for member in file.members() { + let member = member.unwrap(); + let name = String::from_utf8_lossy(member.name()).to_string(); + + // Check if the archive member is an object file and parse it. + if name.ends_with(".o") { + let data = member.data(&*binary_data).unwrap(); + let o_file = object::File::parse(data).unwrap(); + if let Some(manganis_str) = get_string_manganis(&o_file) { + manganis_strings.push(manganis_str); + } + } + } + + manganis_strings + } + }; + + all_json.append(&mut data); + } + + all_json +} diff --git a/packages/manganis-cli-support/tests/collects_assets.rs b/packages/manganis-cli-support/tests/collects_assets.rs new file mode 100644 index 0000000000..9cffd29eda --- /dev/null +++ b/packages/manganis-cli-support/tests/collects_assets.rs @@ -0,0 +1,93 @@ +use manganis_cli_support::AssetManifestExt; +use manganis_common::AssetType; +use std::path::PathBuf; +use std::process::{Command, Stdio}; + +// #[test] +// fn collects_assets() { +// tracing_subscriber::fmt::init(); + +// // Get args and default to "build" +// let args: Vec = std::env::args().collect(); +// let command = match args.get(1) { +// Some(a) => a.clone(), +// None => "build".to_string(), +// }; + +// // Check if rustc is trying to link +// if command == "link" { +// link(); +// } else { +// build(); +// } +// } + +// fn build() { +// // Find the test package directory which is up one directory from this package +// let mut test_package_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")) +// .parent() +// .unwrap() +// .to_path_buf(); +// test_package_dir.push("test-package"); + +// println!("running the CLI from {test_package_dir:?}"); + +// // Then build your application +// let args = ["--target", "wasm32-unknown-unknown", "--release"]; +// Command::new("cargo") +// .arg("build") +// .args(args) +// .current_dir(&test_package_dir) +// .stdout(Stdio::piped()) +// .spawn() +// .unwrap() +// .wait() +// .unwrap(); + +// println!("Collecting Assets"); + +// // Call the helper function to intercept the Rust linker. +// // We will pass the current working directory as it may get lost. +// let link_args = vec![format!("{}", test_package_dir.display())]; +// manganis_cli_support::start_linker_intercept("link", args, Some(link_args)).unwrap(); +// } + +// fn link() { +// let (link_args, object_files) = +// manganis_cli_support::linker_intercept(std::env::args()).unwrap(); + +// // Recover the working directory from the link args. +// let working_dir = PathBuf::from(link_args.first().unwrap()); + +// // Then collect the assets +// let assets = AssetManifest::load_from_objects(object_files); + +// let all_assets = assets.assets(); +// println!("{:#?}", all_assets); + +// let locations = all_assets +// .iter() +// .filter_map(|a| match a { +// AssetType::Resource(f) => Some(f.location()), +// _ => None, +// }) +// .collect::>(); + +// // Make sure the right number of assets were collected +// assert_eq!(locations.len(), 16); + +// // Then copy the assets to a temporary directory and run the application +// let assets_dir = PathBuf::from("./assets"); +// assets.copy_static_assets_to(assets_dir).unwrap(); + +// // Then run the application +// let status = Command::new("cargo") +// .arg("run") +// .arg("--release") +// .current_dir(&working_dir) +// .status() +// .unwrap(); + +// // Make sure the application exited successfully +// assert!(status.success()); +// } diff --git a/packages/manganis-common/Cargo.toml b/packages/manganis-common/Cargo.toml new file mode 100644 index 0000000000..58fa5c3d3f --- /dev/null +++ b/packages/manganis-common/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "manganis-common" +version.workspace = true +edition = "2021" +authors = ["Evan Almloff"] +description = "Ergonomic, automatic, cross crate asset collection and optimization" +license = "MIT OR Apache-2.0" +repository = "https://github.com/DioxusLabs/manganis/" +homepage = "https://dioxuslabs.com" +keywords = ["assets"] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +serde = { version = "1.0.183", features = ["derive"] } +anyhow = "1" +base64 = { workspace = true } +infer = { workspace = true } +fluent-uri = { version = "0.2.0", features = ["serde"] } + +# tracing = "0.1.40" + +# Remote assets +# reqwest = { version = "0.12.5", features = ["blocking"] } +# http = "1.1.0" +# http-serde = "2.1.1" + +[features] +default = ["html"] +html = [] + +[build-dependencies] +built = { version = "0.7", features = ["git2"] } +scratch = "1.0.7" diff --git a/packages/manganis-common/build.rs b/packages/manganis-common/build.rs new file mode 100644 index 0000000000..d8f91cb913 --- /dev/null +++ b/packages/manganis-common/build.rs @@ -0,0 +1,3 @@ +fn main() { + built::write_built_file().expect("Failed to acquire build-time information"); +} diff --git a/packages/manganis-common/src/asset.rs b/packages/manganis-common/src/asset.rs new file mode 100644 index 0000000000..6e1bb632cb --- /dev/null +++ b/packages/manganis-common/src/asset.rs @@ -0,0 +1,47 @@ +use std::{ + fmt::Display, + hash::{DefaultHasher, Hash, Hasher}, + path::{Path, PathBuf}, +}; + +use anyhow::Context; +use base64::Engine; +use serde::{Deserialize, Serialize}; + +use crate::{config, FileOptions}; + +// mod file; +// mod folder; +mod error; +mod file; +mod meta; +mod resource; +mod tailwind; + +// pub use folder::*; +pub use error::*; +pub use file::*; +pub use meta::*; +pub use resource::*; +pub use tailwind::*; + +/// The maximum length of a path segment +const MAX_PATH_LENGTH: usize = 128; + +/// The length of the hash in the output path +const HASH_SIZE: usize = 16; + +/// The type of asset +#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] +pub enum AssetType { + /// A resource asset in the form of a URI + /// + /// Typically a file, but could be a folder or a remote URL + Resource(ResourceAsset), + + /// A tailwind class asset + Tailwind(TailwindAsset), + + /// A metadata asset + Metadata(MetadataAsset), +} diff --git a/packages/manganis-common/src/asset/error.rs b/packages/manganis-common/src/asset/error.rs new file mode 100644 index 0000000000..e20e5a32e3 --- /dev/null +++ b/packages/manganis-common/src/asset/error.rs @@ -0,0 +1,62 @@ +use std::{ + fmt::Display, + hash::{DefaultHasher, Hash, Hasher}, + path::{Path, PathBuf}, +}; + +use anyhow::Context; +use base64::Engine; +use serde::{Deserialize, Serialize}; + +use crate::{config, FileOptions}; + +/// Error while checking an asset exists +#[derive(Debug)] +pub enum AssetError { + /// The relative path does not exist + NotFoundRelative(PathBuf, String), + /// The path exist but is not a file + NotFile(PathBuf), + /// The path exist but is not a folder + NotFolder(PathBuf), + /// Unknown IO error + IO(PathBuf, std::io::Error), +} + +impl Display for AssetError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + AssetError::NotFoundRelative(manifest_dir, path) => + write!(f,"cannot find file `{}` in `{}`, please make sure it exists.\nAny relative paths are resolved relative to the manifest directory.", + path, + manifest_dir.display() + ), + AssetError::NotFile(absolute_path) => + write!(f, "`{}` is not a file, please choose a valid asset.\nAny relative paths are resolved relative to the manifest directory.", absolute_path.display()), + AssetError::NotFolder(absolute_path) => + write!(f, "`{}` is not a folder, please choose a valid asset.\nAny relative paths are resolved relative to the manifest directory.", absolute_path.display()), + AssetError::IO(absolute_path, err) => + write!(f, "unknown error when accessing `{}`: \n{}", absolute_path.display(), err) + } + } +} + +/// An error that can occur while collecting assets without CLI support +#[derive(Debug)] +pub enum ManganisSupportError { + /// An error that can occur while collecting assets from other packages without CLI support + ExternalPackageCollection, + /// Manganis failed to find the current package's manifest + FailedToFindCargoManifest, +} + +impl Display for ManganisSupportError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::ExternalPackageCollection => write!(f, "Attempted to collect assets from other packages without a CLI that supports Manganis. Please recompile with a CLI that supports Manganis like the `dioxus-cli`."), + Self::FailedToFindCargoManifest => write!(f, "Manganis failed to find the current package's manifest. Please recompile with a CLI that supports Manganis like the `dioxus-cli`."), + } + } +} + +impl std::error::Error for ManganisSupportError {} diff --git a/packages/manganis-common/src/asset/file.rs b/packages/manganis-common/src/asset/file.rs new file mode 100644 index 0000000000..149bd8a8b7 --- /dev/null +++ b/packages/manganis-common/src/asset/file.rs @@ -0,0 +1,169 @@ +use std::{ + fmt::Display, + hash::{DefaultHasher, Hash, Hasher}, + path::{Path, PathBuf}, +}; + +use anyhow::Context; +use base64::Engine; +use serde::{Deserialize, Serialize}; + +use crate::{config, FileOptions, ResourceAsset as AssetSource}; + +/// A file asset +#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] +pub struct FileAsset { + location: AssetSource, + options: FileOptions, + url_encoded: bool, +} + +/// A folder asset +#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] +pub struct FolderAsset { + location: AssetSource, +} + +impl FolderAsset { + /// + pub fn path(&self) -> &Path { + todo!() + } +} + +impl std::ops::Deref for FolderAsset { + type Target = AssetSource; + + fn deref(&self) -> &Self::Target { + &self.location + } +} + +impl std::ops::Deref for FileAsset { + type Target = AssetSource; + + fn deref(&self) -> &Self::Target { + &self.location + } +} + +impl FileAsset { + /// Creates a new file asset + pub fn new(source: AssetSource) -> Self { + todo!() + // if let Some(path) = source.as_path() { + // assert!(!path.is_dir()); + // } + + // let options = FileOptions::default_for_extension(source.extension().as_deref()); + + // let mut myself = Self { + // location: AssetSource { + // unique_name: Default::default(), + // source, + // }, + // options, + // url_encoded: false, + // }; + + // myself.regenerate_unique_name(); + + // myself + } + + /// Set the file options + pub fn with_options(self, options: FileOptions) -> Self { + let mut myself = Self { + location: self.location, + options, + url_encoded: false, + }; + + myself.regenerate_unique_name(); + + myself + } + + /// Set whether the file asset should be url encoded + pub fn set_url_encoded(&mut self, url_encoded: bool) { + self.url_encoded = url_encoded; + } + + /// Returns whether the file asset should be url encoded + pub fn url_encoded(&self) -> bool { + self.url_encoded + } + + // /// Returns the location where the file asset will be served from or None if the asset cannot be served + // pub fn served_location(&self) -> Result { + // if self.url_encoded { + // let data = self.location.source.read_to_bytes().unwrap(); + // let data = base64::engine::general_purpose::STANDARD_NO_PAD.encode(data); + // let mime = self.location.source.mime_type().unwrap(); + // Ok(format!("data:{mime};base64,{data}")) + // } else { + // resolve_asset_location(&self.location) + // } + // } + + /// Returns the location of the file asset + pub fn location(&self) -> &AssetSource { + &self.location + } + + /// Returns the options for the file asset + pub fn options(&self) -> &FileOptions { + &self.options + } + + /// + pub fn path(&self) -> &Path { + todo!() + } + + /// Returns the options for the file asset mutably + pub fn with_options_mut(&mut self, f: impl FnOnce(&mut FileOptions)) { + f(&mut self.options); + self.regenerate_unique_name(); + } + + /// Hash the file asset source and options + fn hash(&self) -> u64 { + todo!() + // let mut hash = std::collections::hash_map::DefaultHasher::new(); + // hash_file(&self.location.source, &mut hash); + // self.options.hash(&mut hash); + // hash_version(&mut hash); + // hash.finish() + } + + /// Regenerates the unique name of the file asset + fn regenerate_unique_name(&mut self) { + todo!() + // // Generate an unique name for the file based on the options, source, and the current version of manganis + // let uuid = self.hash(); + // let extension = self.options.extension(); + // let file_name = normalized_file_name(&self.location.source, extension); + // let extension = extension.map(|e| format!(".{e}")).unwrap_or_default(); + // self.location.unique_name = format!("{file_name}{uuid:x}{extension}"); + // assert!(self.location.unique_name.len() <= MAX_PATH_LENGTH); + } +} + +// impl Display for FileAsset { +// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +// let url_encoded = if self.url_encoded { +// " [url encoded]" +// } else { +// "" +// }; + +// write!( +// f, +// "{} [{}]{}", +// self.location.source(), +// self.options, +// url_encoded +// ) +// } +// } diff --git a/packages/manganis-common/src/asset/folder.rs b/packages/manganis-common/src/asset/folder.rs new file mode 100644 index 0000000000..c782453da2 --- /dev/null +++ b/packages/manganis-common/src/asset/folder.rs @@ -0,0 +1,114 @@ +use std::{ + fmt::Display, + hash::{DefaultHasher, Hash, Hasher}, + path::{Path, PathBuf}, +}; + +use anyhow::Context; +use base64::Engine; +use serde::{Deserialize, Serialize}; +use url::Url; + +use crate::{config, AssetSource, FileOptions}; + +/// A folder asset +#[derive(Serialize, Deserialize, Debug, PartialEq, PartialOrd, Clone)] +pub struct FolderAsset { + location: AssetSource, +} + +impl Display for FolderAsset { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}/**", self.location.source(),) + } +} + +impl FolderAsset { + /// Creates a new folder asset + pub fn new(source: AssetSource) -> Self { + let AssetSource::Local(source) = source else { + panic!("Folder asset must be a local path"); + }; + assert!(source.canonicalized.is_dir()); + + let mut myself = Self { + location: AssetSource { + unique_name: Default::default(), + source: AssetSource::Local(source), + }, + }; + + myself.regenerate_unique_name(); + + myself + } + + /// Returns the location where the folder asset will be served from or None if the asset cannot be served + pub fn served_location(&self) -> Result { + resolve_asset_location(&self.location) + } + + /// Returns the unique name of the folder asset + pub fn unique_name(&self) -> &str { + &self.location.unique_name + } + + /// Returns the location of the folder asset + pub fn location(&self) -> &AssetSource { + &self.location + } + + /// Create a unique hash for the source folder by recursively hashing the files + fn hash(&self) -> u64 { + let mut hash = std::collections::hash_map::DefaultHasher::new(); + let folder = self + .location + .source + .as_path() + .expect("Folder asset must be a local path"); + + let mut folders_queued = vec![folder.clone()]; + + while let Some(folder) = folders_queued.pop() { + // Add the folder to the hash + for segment in folder.iter() { + segment.hash(&mut hash); + } + + let files = std::fs::read_dir(folder).into_iter().flatten().flatten(); + for file in files { + let path = file.path(); + let metadata = path.metadata().unwrap(); + + // If the file is a folder, add it to the queue otherwise add it to the hash + if metadata.is_dir() { + folders_queued.push(path); + } else { + // todo: these relative/original paths are not correct + let local = self.location.source().local().unwrap(); + hash_file( + &AssetSource::Local(LocalAssetSource { + original: local.original.clone(), + relative: local.relative.clone(), + canonicalized: path, + }), + &mut hash, + ); + } + } + } + + // Add the manganis version to the hash + hash_version(&mut hash); + + hash.finish() + } + + /// Regenerate the unique name of the folder asset + fn regenerate_unique_name(&mut self) { + let uuid = self.hash(); + let file_name = normalized_file_name(&self.location.source, None); + self.location.unique_name = format!("{file_name}{uuid:x}"); + assert!(self.location.unique_name.len() <= MAX_PATH_LENGTH); + } +} diff --git a/packages/manganis-common/src/asset/meta.rs b/packages/manganis-common/src/asset/meta.rs new file mode 100644 index 0000000000..c62248861f --- /dev/null +++ b/packages/manganis-common/src/asset/meta.rs @@ -0,0 +1,38 @@ +use std::{ + fmt::Display, + hash::{DefaultHasher, Hash, Hasher}, + path::{Path, PathBuf}, +}; + +use anyhow::Context; +use base64::Engine; +use serde::{Deserialize, Serialize}; + +use crate::{config, FileOptions}; + +/// A metadata asset +#[derive(Serialize, Deserialize, Debug, PartialEq, PartialOrd, Clone)] +pub struct MetadataAsset { + key: String, + value: String, +} + +impl MetadataAsset { + /// Creates a new metadata asset + pub fn new(key: &str, value: &str) -> Self { + Self { + key: key.to_string(), + value: value.to_string(), + } + } + + /// Returns the key of the metadata asset + pub fn key(&self) -> &str { + &self.key + } + + /// Returns the value of the metadata asset + pub fn value(&self) -> &str { + &self.value + } +} diff --git a/packages/manganis-common/src/asset/resource.rs b/packages/manganis-common/src/asset/resource.rs new file mode 100644 index 0000000000..50e3cb5504 --- /dev/null +++ b/packages/manganis-common/src/asset/resource.rs @@ -0,0 +1,537 @@ +use std::{ + fmt::Display, + hash::{DefaultHasher, Hash, Hasher}, + path::{Path, PathBuf}, + str::FromStr, +}; + +use anyhow::Context; +use base64::Engine; +use fluent_uri::{ + component::{Authority, Scheme}, + encoding::EStr, + UriRef, +}; +use serde::{Deserialize, Serialize}; + +use crate::{config, AssetError, FileOptions}; + +/// An asset identified by a URI +/// +/// This could be a file, a folder, a remote URL, a data-encoded string, etc. +/// +/// We don't want to download or copy the resource itself, just the metadata about it such that +/// we can resolve it later. +#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Hash, Eq)] +pub struct ResourceAsset { + /// The input URI + /// + /// This is basically whatever the user passed in to the macro + pub input: UriRef, + + /// The local URI for fallbacks + /// + /// This generally retains the original URI that was used to resolve the asset, but for files, + /// it's resolved to an absolute path since we transform all schema-less URIs to file:// URIs. + /// + /// If the aset is relative, this will be None since we can't figure it out at compile time. + pub local: Option>, + + /// The output URI that makes it into the final bundle. + /// This explicitly has the `bundle://` scheme to make it clear that it is a bundle URI. + /// + /// The bundler will generate a unique name for the asset and use that as the path to generate a + /// final "flat" architecture. + /// + /// bundle://asset/path/to/file.txt + pub bundled: UriRef, + + /// The options for the resource + pub options: Option, +} + +impl ResourceAsset { + /// + pub fn new(raw: &str) -> Self { + todo!() + } + + /// + pub fn unique_name(&self) -> &str { + todo!() + } + + /// + pub fn original(&self) -> &UriRef { + todo!() + } + + /// Set the file options + pub fn with_options(self, options: FileOptions) -> Self { + todo!() + // let mut myself = Self { + // options, + // url_encoded: false, + // ..self + // }; + + // myself.regenerate_unique_name(); + + // myself + } + + /// + pub fn set_options(&mut self, options: FileOptions) { + self.options = Some(options); + } + + /// Set whether the file asset should be url encoded + pub fn set_url_encoded(&mut self, url_encoded: bool) { + todo!() + // self.url_encoded = url_encoded; + } + + /// Returns whether the file asset should be url encoded + pub fn url_encoded(&self) -> bool { + todo!() + // self.url_encoded + } + + /// Parse a string as a file source + pub fn parse_file(path: &str) -> Result { + // let myself = Self::parse_any(path)?; + // if let Self::Local(path) = &myself { + // if !path.canonicalized.is_file() { + // return Err(AssetError::NotFile(path.canonicalized.to_path_buf())); + // } + // } + // Ok(myself) + todo!() + } + + /// Parse a string as a folder source + pub fn parse_folder(path: &str) -> Result { + // let myself = Self::parse_any(path)?; + // if let Self::Local(path) = &myself { + // if !path.canonicalized.is_dir() { + // return Err(AssetError::NotFolder(path.canonicalized.to_path_buf())); + // } + // } + // Ok(myself) + todo!() + } + + /// + pub fn parse_url(url: &str) -> Result { + todo!() + } + + /// Parse a string as a file or folder source + pub fn parse_any(src: &str) -> Result { + // Process the input as a URI + let input: UriRef = src.parse().unwrap(); + + let local = match input.scheme().map(|x| x.as_str()) { + // For http and https, we just use the input as is + // In fallback mode we end up just passing the URI through + Some("http") | Some("https") => Some(input.clone()), + + // For file, we use the local path + // This will be `file://` in dev + // In release this will be `bundle://` + // Join the URI against the filesystem + None if input.path().is_absolute() => { + let manifest_dir: PathBuf = std::env::var("CARGO_MANIFEST_DIR").unwrap().into(); + let manifest_dir = manifest_dir.canonicalize().unwrap(); + let _local = manifest_dir.join(input.path().as_str()); + Some(UriRef::::parse(format!("file://{}", _local.display())).unwrap()) + } + None => None, + + Some(scheme) => { + panic!("Unsupported scheme: {}", scheme); + } + }; + + // Generate the bundled URI + // + // We: + // - flatten the URI with a hash + // - change the scheme to `bundle` + // - add the authority of pkg-name.bundle + // + // This results in a bundle-per dependency + let pkg_name = std::env::var("CARGO_PKG_NAME").unwrap(); + let bundled = UriRef::builder() + .scheme(Scheme::new_or_panic("bundle")) + .authority_with(|b| b.host(EStr::new_or_panic(&format!("{}.bundle", pkg_name)))) + .path(local.as_ref().map(|x| x.path()).unwrap_or_default()) + .build() + .unwrap(); + + Ok(Self { + input, + local, + bundled, + options: None, + }) + } + + /// + pub fn make_unique_id(uri: &UriRef) -> String { + todo!() + } + + /// + pub fn is_dir(&self) -> bool { + todo!() + } + + /// + pub fn resolve(&self) -> String { + // fn resolve_asset_location(location: &AssetSource) -> Result { + // if !config::is_bundled() { + // return Ok(location.source().raw()); + // } + + // let root = crate::config::base_path(); + // let path = root.join(location.unique_name()); + // Ok(path.display().to_string()) + // } + + todo!() + } + + /// + pub fn normalized(&self, extension: Option<&str>) -> String { + // /// Create a normalized file name from the source + // fn normalized_file_name(location: &AssetSource, extension: Option<&str>) -> String { + // let last_segment = location.last_segment(); + // let mut file_name = to_alphanumeric_string_lossy(last_segment); + + // let extension_len = extension.map(|e| e.len() + 1).unwrap_or_default(); + // let extension_and_hash_size = extension_len + HASH_SIZE; + // // If the file name is too long, we need to truncate it + // if file_name.len() + extension_and_hash_size > MAX_PATH_LENGTH { + // file_name = file_name[..MAX_PATH_LENGTH - extension_and_hash_size].to_string(); + // } + // file_name + // } + + // /// Normalize a string to only contain alphanumeric characters + // fn to_alphanumeric_string_lossy(name: &str) -> String { + // name.chars() + // .filter(|c| c.is_alphanumeric()) + // .collect::() + // } + + // fn hash_file(location: &AssetSource, hash: &mut DefaultHasher) { + // // Hash the last time the file was updated and the file source. If either of these change, we need to regenerate the unique name + // let updated = location.last_updated(); + // updated.hash(hash); + // location.hash(hash); + // } + + todo!() + } + + // /// Covnert the asset source to a string + // pub fn raw(&self) -> String { + // match self { + // Self::Local(path) => path.relative.display().to_string(), + // Self::Remote(url) => url.to_string(), + // } + // } + + // /// Try to convert the asset source to a local asset source + // pub fn local(&self) -> Option<&AssetSource> { + // match self { + // Self::Local(path) => Some(path), + // Self::Remote(_) => None, + // } + // } + + // /// Try to convert the asset source to a path + // pub fn as_path(&self) -> Option<&PathBuf> { + // match self { + // Self::Local(path) => Some(&path.canonicalized), + // Self::Remote(_) => None, + // } + // } + + // /// Try to convert the asset source to a url + // pub fn as_url(&self) -> Option<&Url> { + // match self { + // Self::Local(_) => None, + // Self::Remote(url) => Some(url), + // } + // } + + // /// Returns the last segment of the file source used to generate a unique name + // pub fn last_segment(&self) -> &str { + // match self { + // Self::Local(path) => path.canonicalized.file_name().unwrap().to_str().unwrap(), + // Self::Remote(url) => url.path_segments().unwrap().last().unwrap(), + // } + // } + + /// Returns the extension of the file source + pub fn extension(&self) -> Option { + // match self { + // Self::Local(path) => path + // .canonicalized + // .extension() + // .map(|e| e.to_str().unwrap().to_string()), + // Self::Remote(url) => reqwest::blocking::get(url.as_str()) + // .ok() + // .and_then(|request| { + // request + // .headers() + // .get("content-type") + // .and_then(|content_type| { + // content_type + // .to_str() + // .ok() + // .map(|ty| ext_of_mime(ty).to_string()) + // }) + // }), + // } + todo!() + } + + // /// Attempts to get the mime type of the file source + // pub fn mime_type(&self) -> Option { + // match self { + // Self::Local(path) => get_mime_from_path(&path.canonicalized) + // .ok() + // .map(|mime| mime.to_string()), + // Self::Remote(url) => reqwest::blocking::get(url.as_str()) + // .ok() + // .and_then(|request| { + // request + // .headers() + // .get("content-type") + // .and_then(|content_type| Some(content_type.to_str().ok()?.to_string())) + // }), + // } + // } + + // /// Find when the asset was last updated + // pub fn last_updated(&self) -> Option { + // match self { + // Self::Local(path) => path.canonicalized.metadata().ok().and_then(|metadata| { + // metadata + // .modified() + // .ok() + // .map(|modified| format!("{:?}", modified)) + // .or_else(|| { + // metadata + // .created() + // .ok() + // .map(|created| format!("{:?}", created)) + // }) + // }), + // Self::Remote(url) => reqwest::blocking::get(url.as_str()) + // .ok() + // .and_then(|request| { + // request + // .headers() + // .get("last-modified") + // .and_then(|last_modified| { + // last_modified + // .to_str() + // .ok() + // .map(|last_modified| last_modified.to_string()) + // }) + // }), + // } + // } + + /// Reads the file to a string + pub fn read_to_string(&self) -> anyhow::Result { + // match &self { + // AssetSource::Local(path) => Ok(std::fs::read_to_string(&path.canonicalized) + // .with_context(|| { + // format!( + // "Failed to read file from location: {}", + // path.canonicalized.display() + // ) + // })?), + // AssetSource::Remote(url) => { + // let response = reqwest::blocking::get(url.as_str()) + // .with_context(|| format!("Failed to asset from url: {}", url.as_str()))?; + // Ok(response.text().with_context(|| { + // format!("Failed to read text for asset from url: {}", url.as_str()) + // })?) + // } + // } + todo!() + } + + /// Reads the file to bytes + pub fn read_to_bytes(&self) -> anyhow::Result> { + // match &self { + // AssetSource::Local(path) => { + // Ok(std::fs::read(&path.canonicalized).with_context(|| { + // format!( + // "Failed to read file from location: {}", + // path.canonicalized.display() + // ) + // })?) + // } + // AssetSource::Remote(url) => { + // let response = reqwest::blocking::get(url.as_str()) + // .with_context(|| format!("Failed to asset from url: {}", url.as_str()))?; + // Ok(response.bytes().map(|b| b.to_vec()).with_context(|| { + // format!("Failed to read text for asset from url: {}", url.as_str()) + // })?) + // } + // } + todo!() + } + + /// The location where the asset will be served from post-bundle + /// This is not the "resolved" location at runtime + pub fn served_location(&self) -> Result { + todo!() + } + + // /// Returns the unique name of the file that the asset will be served from + // pub fn unique_name(&self) -> &str { + // &self.unique_name + // } + + // /// Returns the source of the file that the asset will be collected from + // pub fn source(&self) -> &AssetSource { + // &self.source + // } + + /// Returns the location of the file asset + pub fn location(&self) -> &ResourceAsset { + todo!() + // &self.location + } + + /// Returns the options for the file asset + pub fn options(&self) -> &FileOptions { + todo!() + // &self.options + } + + /// Returns the options for the file asset mutably + pub fn with_options_mut(&mut self, f: impl FnOnce(&mut FileOptions)) { + todo!() + // f(&mut self.options); + // self.regenerate_unique_name(); + } + + /// Regenerates the unique name of the file asset + fn regenerate_unique_name(&mut self) { + // // Generate an unique name for the file based on the options, source, and the current version of manganis + // let uuid = self.hash(); + // let extension = self.options.extension(); + // let file_name = normalized_file_name(&self.location.source, extension); + // let extension = extension.map(|e| format!(".{e}")).unwrap_or_default(); + // self.location.unique_name = format!("{file_name}{uuid:x}{extension}"); + // assert!(self.location.unique_name.len() <= MAX_PATH_LENGTH); + } + + // /// Hash the file asset source and options + // fn hash(&self) -> u64 { + // let mut hash = std::collections::hash_map::DefaultHasher::new(); + // hash_file(&self.location.source, &mut hash); + // self.options.hash(&mut hash); + // hash_version(&mut hash); + // hash.finish() + // } +} + +/// Get the mime type from a URI using its extension +fn ext_of_mime(mime: &str) -> &str { + let mime = mime.split(';').next().unwrap_or_default(); + match mime.trim() { + "application/octet-stream" => "bin", + "text/css" => "css", + "text/csv" => "csv", + "text/html" => "html", + "image/vnd.microsoft.icon" => "ico", + "text/javascript" => "js", + "application/json" => "json", + "application/ld+json" => "jsonld", + "application/rtf" => "rtf", + "image/svg+xml" => "svg", + "video/mp4" => "mp4", + "text/plain" => "txt", + "application/xml" => "xml", + "application/zip" => "zip", + "image/png" => "png", + "image/jpeg" => "jpg", + "image/gif" => "gif", + "image/webp" => "webp", + "image/avif" => "avif", + "font/ttf" => "ttf", + "font/woff" => "woff", + "font/woff2" => "woff2", + other => other.split('/').last().unwrap_or_default(), + } +} + +/// Get the mime type from a path-like string +fn get_mime_from_path(trimmed: &Path) -> std::io::Result<&'static str> { + if trimmed.extension().is_some_and(|ext| ext == "svg") { + return Ok("image/svg+xml"); + } + + let res = match infer::get_from_path(trimmed)?.map(|f| f.mime_type()) { + Some(f) => { + if f == "text/plain" { + get_mime_by_ext(trimmed) + } else { + f + } + } + None => get_mime_by_ext(trimmed), + }; + + Ok(res) +} + +/// Get the mime type from a URI using its extension +fn get_mime_by_ext(trimmed: &Path) -> &'static str { + get_mime_from_ext(trimmed.extension().and_then(|e| e.to_str())) +} + +/// Get the mime type from a URI using its extension +pub fn get_mime_from_ext(extension: Option<&str>) -> &'static str { + match extension { + Some("bin") => "application/octet-stream", + Some("css") => "text/css", + Some("csv") => "text/csv", + Some("html") => "text/html", + Some("ico") => "image/vnd.microsoft.icon", + Some("js") => "text/javascript", + Some("json") => "application/json", + Some("jsonld") => "application/ld+json", + Some("mjs") => "text/javascript", + Some("rtf") => "application/rtf", + Some("svg") => "image/svg+xml", + Some("mp4") => "video/mp4", + Some("png") => "image/png", + Some("jpg") => "image/jpeg", + Some("gif") => "image/gif", + Some("webp") => "image/webp", + Some("avif") => "image/avif", + Some("txt") => "text/plain", + // Assume HTML when a TLD is found for eg. `dioxus:://dioxuslabs.app` | `dioxus://hello.com` + Some(_) => "text/html", + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types + // using octet stream according to this: + None => "application/octet-stream", + } +} + +fn hash_version(hash: &mut DefaultHasher) { + // Hash the current version of manganis. If this changes, we need to regenerate the unique name + crate::built::PKG_VERSION.hash(hash); + crate::built::GIT_COMMIT_HASH.hash(hash); +} diff --git a/packages/manganis-common/src/asset/tailwind.rs b/packages/manganis-common/src/asset/tailwind.rs new file mode 100644 index 0000000000..8284a80d2d --- /dev/null +++ b/packages/manganis-common/src/asset/tailwind.rs @@ -0,0 +1,31 @@ +use std::{ + fmt::Display, + hash::{DefaultHasher, Hash, Hasher}, + path::{Path, PathBuf}, +}; + +use anyhow::Context; +use base64::Engine; +use serde::{Deserialize, Serialize}; + +use crate::{config, FileOptions}; + +/// A tailwind class asset +#[derive(Serialize, Deserialize, Debug, PartialEq, PartialOrd, Clone)] +pub struct TailwindAsset { + classes: String, +} + +impl TailwindAsset { + /// Creates a new tailwind class asset + pub fn new(classes: &str) -> Self { + Self { + classes: classes.to_string(), + } + } + + /// Returns the classes of the tailwind class asset + pub fn classes(&self) -> &str { + &self.classes + } +} diff --git a/packages/manganis-common/src/built.rs b/packages/manganis-common/src/built.rs new file mode 100644 index 0000000000..cc9895a6a8 --- /dev/null +++ b/packages/manganis-common/src/built.rs @@ -0,0 +1,2 @@ +// The file `built.rs` was placed there by cargo and `build.rs` +include!(concat!(env!("OUT_DIR"), "/built.rs")); diff --git a/packages/manganis-common/src/config.rs b/packages/manganis-common/src/config.rs new file mode 100644 index 0000000000..590e94f92c --- /dev/null +++ b/packages/manganis-common/src/config.rs @@ -0,0 +1,41 @@ +use std::path::PathBuf; + +/// Get the base path for assets defined by the MG_BASEPATH environment variable +/// +/// The basepath should always start and end with a `/` +/// +/// If no basepath is set, the default is `/` which is the root of the assets folder. +pub fn base_path() -> PathBuf { + "/".into() + // match option_env!("MG_BASEPATH") { + // Some(path) => { + // let path = path.trim_end_matches('/').trim_start_matches('/'); + // PathBuf::from(format!("/{path}/")) + // } + // None => "/".into(), + // } +} + +/// MG_BUNDLED is set to true when the application is bundled. +/// +/// When running under a dev server, this is false to prevent thrashing of the cache since an ordinary +/// `cargo check` will not pass MG_BUNDLED. +pub const fn is_bundled() -> bool { + false + // option_env!("MG_BUNDLED").is_some() +} + +/// The location of the manifest directory used to build this crate +pub fn manifest_dir() -> Option { + std::env::var("CARGO_MANIFEST_DIR").ok().map(PathBuf::from) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn base_path_works() { + assert_eq!(base_path(), PathBuf::from("/")); + } +} diff --git a/packages/manganis-common/src/file.rs b/packages/manganis-common/src/file.rs new file mode 100644 index 0000000000..afd3a08f12 --- /dev/null +++ b/packages/manganis-common/src/file.rs @@ -0,0 +1,574 @@ +use serde::{Deserialize, Serialize}; +use std::{fmt::Display, str::FromStr}; + +/// The options for a file asset +#[derive(Serialize, Deserialize, Debug, PartialEq, PartialOrd, Clone, Hash, Eq)] +pub enum FileOptions { + /// An image asset + Image(ImageOptions), + /// A video asset + Video(VideoOptions), + /// A font asset + Font(FontOptions), + /// A css asset + Css(CssOptions), + /// A JavaScript asset + Js(JsOptions), + /// A Json asset + Json(JsonOptions), + /// Any other asset + Other(UnknownFileOptions), +} + +impl Display for FileOptions { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Image(options) => write!(f, "{}", options), + Self::Video(options) => write!(f, "{}", options), + Self::Font(options) => write!(f, "{}", options), + Self::Css(options) => write!(f, "{}", options), + Self::Js(options) => write!(f, "{}", options), + Self::Json(options) => write!(f, "{}", options), + Self::Other(options) => write!(f, "{}", options), + } + } +} + +impl FileOptions { + /// Returns the default options for a given extension + pub fn default_for_extension(extension: Option<&str>) -> Self { + if let Some(extension) = extension { + if extension == CssOptions::EXTENSION { + return Self::Css(CssOptions::default()); + } else if extension == JsonOptions::EXTENSION { + return Self::Json(JsonOptions::default()); + } else if let Ok(ty) = extension.parse::() { + return Self::Image(ImageOptions::new(ty, None)); + } else if let Ok(ty) = extension.parse::() { + return Self::Video(VideoOptions::new(ty)); + } else if let Ok(ty) = extension.parse::() { + return Self::Font(FontOptions::new(ty)); + } else if let Ok(ty) = extension.parse::() { + return Self::Js(JsOptions::new(ty)); + } + } + Self::Other(UnknownFileOptions { + extension: extension.map(String::from), + }) + } + + /// Returns the extension for this file + pub fn extension(&self) -> Option<&str> { + match self { + Self::Image(options) => Some(options.ty.extension()), + Self::Video(options) => Some(options.ty.extension()), + Self::Font(options) => Some(options.ty.extension()), + Self::Css(_) => Some(CssOptions::EXTENSION), + Self::Js(js) => Some(js.ty.extension()), + Self::Json(_) => Some(JsonOptions::EXTENSION), + Self::Other(extension) => extension.extension.as_deref(), + } + } +} + +impl Default for FileOptions { + fn default() -> Self { + Self::Other(UnknownFileOptions { extension: None }) + } +} + +/// The options for an image asset +#[derive(Serialize, Deserialize, Debug, PartialEq, PartialOrd, Clone, Hash, Eq)] +pub struct ImageOptions { + compress: bool, + size: Option<(u32, u32)>, + preload: bool, + ty: ImageType, +} + +impl Display for ImageOptions { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some((x, y)) = self.size { + write!(f, "{} ({}x{})", self.ty, x, y)?; + } else { + write!(f, "{}", self.ty)?; + } + if self.compress { + write!(f, " (compressed)")?; + } + if self.preload { + write!(f, " (preload)")?; + } + Ok(()) + } +} + +impl ImageOptions { + /// Creates a new image options struct + pub fn new(ty: ImageType, size: Option<(u32, u32)>) -> Self { + Self { + compress: true, + size, + ty, + preload: false, + } + } + + /// Returns whether the image should be preloaded + pub fn preload(&self) -> bool { + self.preload + } + + /// Sets whether the image should be preloaded + pub fn set_preload(&mut self, preload: bool) { + self.preload = preload; + } + + /// Returns the image type + pub fn ty(&self) -> &ImageType { + &self.ty + } + + /// Sets the image type + pub fn set_ty(&mut self, ty: ImageType) { + self.ty = ty; + } + + /// Returns the size of the image + pub fn size(&self) -> Option<(u32, u32)> { + self.size + } + + /// Sets the size of the image + pub fn set_size(&mut self, size: Option<(u32, u32)>) { + self.size = size; + } + + /// Returns whether the image should be compressed + pub fn compress(&self) -> bool { + self.compress + } + + /// Sets whether the image should be compressed + pub fn set_compress(&mut self, compress: bool) { + self.compress = compress; + } +} + +/// The type of an image +#[derive(Serialize, Deserialize, Debug, PartialEq, PartialOrd, Clone, Copy, Hash, Eq)] +pub enum ImageType { + /// A png image + Png, + /// A jpg image + Jpg, + /// An avif image + Avif, + /// A webp image + Webp, +} + +impl ImageType { + /// Returns the extension for this image type + pub fn extension(&self) -> &'static str { + match self { + Self::Png => "png", + Self::Jpg => "jpg", + Self::Avif => "avif", + Self::Webp => "webp", + } + } +} + +impl Display for ImageType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.extension()) + } +} + +impl FromStr for ImageType { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + "png" => Ok(Self::Png), + "jpg" | "jpeg" => Ok(Self::Jpg), + "avif" => Ok(Self::Avif), + "webp" => Ok(Self::Webp), + _ => Err(()), + } + } +} + +/// The options for a video asset +#[derive(Serialize, Deserialize, Debug, PartialEq, PartialOrd, Clone, Hash, Eq)] +pub struct VideoOptions { + /// Whether the video should be compressed + compress: bool, + /// Whether the video should be preloaded + preload: bool, + /// The type of the video + ty: VideoType, +} + +impl Display for VideoOptions { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.ty)?; + if self.compress { + write!(f, " (compressed)")?; + } + if self.preload { + write!(f, " (preload)")?; + } + Ok(()) + } +} + +impl VideoOptions { + /// Creates a new video options struct + pub fn new(ty: VideoType) -> Self { + Self { + compress: true, + ty, + preload: false, + } + } + + /// Returns the type of the video + pub fn ty(&self) -> &VideoType { + &self.ty + } + + /// Sets the type of the video + pub fn set_ty(&mut self, ty: VideoType) { + self.ty = ty; + } + + /// Returns whether the video should be compressed + pub fn compress(&self) -> bool { + self.compress + } + + /// Sets whether the video should be compressed + pub fn set_compress(&mut self, compress: bool) { + self.compress = compress; + } + + /// Returns whether the video should be preloaded + pub fn preload(&self) -> bool { + self.preload + } + + /// Sets whether the video should be preloaded + pub fn set_preload(&mut self, preload: bool) { + self.preload = preload; + } +} + +/// The type of a video +#[derive(Serialize, Deserialize, Debug, PartialEq, PartialOrd, Clone, Hash, Eq)] +pub enum VideoType { + /// An mp4 video + MP4, + /// A webm video + Webm, + /// A gif video + GIF, +} + +impl VideoType { + /// Returns the extension for this video type + pub fn extension(&self) -> &'static str { + match self { + Self::MP4 => "mp4", + Self::Webm => "webm", + Self::GIF => "gif", + } + } +} + +impl Display for VideoType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.extension()) + } +} + +impl FromStr for VideoType { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + "mp4" => Ok(Self::MP4), + "webm" => Ok(Self::Webm), + "gif" => Ok(Self::GIF), + _ => Err(()), + } + } +} + +/// The options for a font asset +#[derive(Serialize, Deserialize, Debug, PartialEq, PartialOrd, Clone, Hash, Eq)] +pub struct FontOptions { + ty: FontType, +} + +impl Display for FontOptions { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.ty) + } +} + +impl FontOptions { + /// Creates a new font options struct + pub fn new(ty: FontType) -> Self { + Self { ty } + } + + /// Returns the type of the font + pub fn ty(&self) -> &FontType { + &self.ty + } +} + +/// The type of a font +#[derive(Serialize, Deserialize, Debug, PartialEq, PartialOrd, Clone, Hash, Eq)] +pub enum FontType { + /// A ttf (TrueType) font + TTF, + /// A woff (Web Open Font Format) font + WOFF, + /// A woff2 (Web Open Font Format 2) font + WOFF2, +} + +impl FontType { + /// Returns the extension for this font type + pub fn extension(&self) -> &'static str { + match self { + Self::TTF => "ttf", + Self::WOFF => "woff", + Self::WOFF2 => "woff2", + } + } +} + +impl FromStr for FontType { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + "ttf" => Ok(Self::TTF), + "woff" => Ok(Self::WOFF), + "woff2" => Ok(Self::WOFF2), + _ => Err(()), + } + } +} + +impl Display for FontType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::TTF => write!(f, "ttf"), + Self::WOFF => write!(f, "woff"), + Self::WOFF2 => write!(f, "woff2"), + } + } +} + +/// The options for a css asset +#[derive(Serialize, Deserialize, Debug, PartialEq, PartialOrd, Clone, Hash, Eq)] +pub struct CssOptions { + minify: bool, + preload: bool, +} + +impl Default for CssOptions { + fn default() -> Self { + Self::new() + } +} + +impl Display for CssOptions { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if self.minify { + write!(f, "minified")?; + } + if self.preload { + write!(f, " (preload)")?; + } + Ok(()) + } +} + +impl CssOptions { + const EXTENSION: &'static str = "css"; + + /// Creates a new css options struct + pub const fn new() -> Self { + Self { + minify: true, + preload: false, + } + } + + /// Returns whether the css should be minified + pub fn minify(&self) -> bool { + self.minify + } + + /// Sets whether the css should be minified + pub fn set_minify(&mut self, minify: bool) { + self.minify = minify; + } + + /// Returns whether the css should be preloaded + pub fn preload(&self) -> bool { + self.preload + } + + /// Sets whether the css should be preloaded + pub fn set_preload(&mut self, preload: bool) { + self.preload = preload; + } +} + +/// The type of a Javascript asset +#[derive(Serialize, Deserialize, Debug, PartialEq, PartialOrd, Clone, Copy, Hash, Default, Eq)] +pub enum JsType { + /// A js asset + #[default] + Js, + // TODO: support ts files +} + +impl JsType { + /// Returns the extension for this js type + pub fn extension(&self) -> &'static str { + match self { + Self::Js => "js", + } + } +} + +impl FromStr for JsType { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + "js" => Ok(Self::Js), + _ => Err(()), + } + } +} + +impl Display for JsType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.extension()) + } +} + +/// The options for a Javascript asset +#[derive(Serialize, Deserialize, Debug, PartialEq, PartialOrd, Clone, Hash, Default, Eq)] +pub struct JsOptions { + ty: JsType, + minify: bool, + preload: bool, +} + +impl Display for JsOptions { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "js")?; + Ok(()) + } +} + +impl JsOptions { + /// Creates a new js options struct + pub fn new(ty: JsType) -> Self { + Self { + ty, + preload: false, + minify: true, + } + } + + /// Returns whether the js should be preloaded + pub fn preload(&self) -> bool { + self.preload + } + + /// Sets whether the js should be preloaded + pub fn set_preload(&mut self, preload: bool) { + self.preload = preload; + } + + /// Returns if the js should be minified + pub fn minify(&self) -> bool { + self.minify + } + + /// Sets if the js should be minified + pub fn set_minify(&mut self, minify: bool) { + self.minify = minify; + } +} + +/// The options for a Json asset +#[derive(Serialize, Deserialize, Debug, PartialEq, PartialOrd, Clone, Hash, Default, Eq)] +pub struct JsonOptions { + preload: bool, +} + +impl Display for JsonOptions { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "json")?; + Ok(()) + } +} + +impl JsonOptions { + /// The extension of the json asset + pub const EXTENSION: &'static str = "json"; + + /// Creates a new json options struct + pub fn new() -> Self { + Self { preload: false } + } + + /// Returns whether the json should be preloaded + pub fn preload(&self) -> bool { + self.preload + } + + /// Sets whether the json should be preloaded + pub fn set_preload(&mut self, preload: bool) { + self.preload = preload; + } +} + +/// The options for an unknown file asset +#[derive(Serialize, Deserialize, Debug, PartialEq, PartialOrd, Clone, Hash, Eq)] +pub struct UnknownFileOptions { + extension: Option, +} + +impl Display for UnknownFileOptions { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(extension) = &self.extension { + write!(f, "{}", extension)?; + } + Ok(()) + } +} + +impl UnknownFileOptions { + /// Creates a new unknown file options struct + pub fn new(extension: Option) -> Self { + Self { extension } + } + + /// Returns the extension of the file + pub fn extension(&self) -> Option<&str> { + self.extension.as_deref() + } +} diff --git a/packages/manganis-common/src/lib.rs b/packages/manganis-common/src/lib.rs new file mode 100644 index 0000000000..e964569ef4 --- /dev/null +++ b/packages/manganis-common/src/lib.rs @@ -0,0 +1,12 @@ +#![deny(missing_docs)] +//! Common types and methods for the manganis asset system + +mod asset; +mod built; +mod config; +mod file; + +pub mod linker; +pub use asset::*; +pub use config::*; +pub use file::*; diff --git a/packages/manganis-common/src/linker.rs b/packages/manganis-common/src/linker.rs new file mode 100644 index 0000000000..b795c8976f --- /dev/null +++ b/packages/manganis-common/src/linker.rs @@ -0,0 +1,75 @@ +//! name conventions used by the linker on different platforms. +//! This is used to make the "link_section" magic working +//! +// code taken from https://github.com/dtolnay/linkme/, +// MIT license + +/// Information about the manganis link section for a given platform +#[derive(Debug, Clone, Copy)] +pub struct LinkSection { + /// The link section we pass to the static + pub link_section: &'static str, + /// The name of the section we find in the binary + pub name: &'static str, +} + +impl LinkSection { + /// The list of link sections for all supported platforms + pub const ALL: &'static [&'static LinkSection] = + &[Self::WASM, Self::MACOS, Self::WINDOWS, Self::ILLUMOS]; + + /// Returns the link section used in linux, android, fuchsia, psp, freebsd, and wasm32 + pub const WASM: &'static LinkSection = &LinkSection { + link_section: "manganis", + name: "manganis", + }; + + /// Returns the link section used in macOS, iOS, tvOS + pub const MACOS: &'static LinkSection = &LinkSection { + link_section: "__DATA,manganis,regular,no_dead_strip", + name: "manganis", + }; + + /// Returns the link section used in windows + pub const WINDOWS: &'static LinkSection = &LinkSection { + link_section: "mg", + name: "mg", + }; + + /// Returns the link section used in illumos + pub const ILLUMOS: &'static LinkSection = &LinkSection { + link_section: "set_manganis", + name: "set_manganis", + }; + + /// The link section used on the current platform + pub const CURRENT: &'static LinkSection = { + #[cfg(any( + target_os = "none", + target_os = "linux", + target_os = "android", + target_os = "fuchsia", + target_os = "psp", + target_os = "freebsd", + target_arch = "wasm32" + ))] + { + Self::WASM + } + + #[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos"))] + { + Self::MACOS + } + + #[cfg(target_os = "windows")] + { + Self::WINDOWS + } + + #[cfg(target_os = "illumos")] + { + Self::ILLUMOS + } + }; +} diff --git a/packages/manganis-macro/Cargo.toml b/packages/manganis-macro/Cargo.toml new file mode 100644 index 0000000000..e2b001dc14 --- /dev/null +++ b/packages/manganis-macro/Cargo.toml @@ -0,0 +1,35 @@ +[package] +name = "manganis-macro" +version.workspace = true +edition = "2021" +authors = ["Evan Almloff"] +description = "Ergonomic, automatic, cross crate asset collection and optimization" +license = "MIT OR Apache-2.0" +repository = "https://github.com/DioxusLabs/manganis/" +homepage = "https://dioxuslabs.com" +keywords = ["assets"] + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = { version = "1.0" } +quote = "1.0" +syn = { version = "2.0", features = ["full", "extra-traits"] } +serde_json = "1.0" +base64 = { workspace = true, optional = true } +serde = { workspace = true, features = ["derive"] } +manganis-common = { workspace = true } + +# manganis-cli-support = { workspace = true, optional = true } + +# manganis-common = { path = "../common", version = "0.3.0-alpha.1" } +# manganis-cli-support = { path = "../cli-support", version = "0.3.0-alpha.1", optional = true } +# base64 = { version = "0.21.5", optional = true } + +[build-dependencies] +manganis-common = { workspace = true } + +[features] +default = ["url-encoding"] +url-encoding = ["base64"] diff --git a/packages/manganis-macro/README.md b/packages/manganis-macro/README.md new file mode 100644 index 0000000000..fde983fef5 --- /dev/null +++ b/packages/manganis-macro/README.md @@ -0,0 +1,3 @@ +# Manganis Macro + +This crate contains the macro used to interact with the Manganis asset system. diff --git a/packages/manganis-macro/src/asset.rs b/packages/manganis-macro/src/asset.rs new file mode 100644 index 0000000000..4db6025a56 --- /dev/null +++ b/packages/manganis-macro/src/asset.rs @@ -0,0 +1,179 @@ +use core::panic; +use manganis_common::{ + CssOptions, FileOptions, FontOptions, ImageOptions, JsOptions, JsonOptions, MetadataAsset, + ResourceAsset, TailwindAsset, UnknownFileOptions, VideoOptions, +}; +use proc_macro::TokenStream; +use proc_macro2::Ident; +use proc_macro2::TokenStream as TokenStream2; +use quote::{quote, quote_spanned, ToTokens}; +use serde::Serialize; +use std::{collections::HashMap, fs::File, sync::atomic::AtomicBool}; +use std::{path::PathBuf, sync::atomic::Ordering}; +use syn::{ + parenthesized, parse::Parse, parse_macro_input, punctuated::Punctuated, token::Token, Expr, + ExprLit, Lit, LitStr, PatLit, Token, +}; + +pub struct AssetParser { + option_source: TokenStream2, + resource: ResourceAsset, + name: Option, + parsed_options: Option, +} + +impl Parse for AssetParser { + // we can take + // + // This gives you the Asset type - it's generic and basically unrefined + // ``` + // asset!("myfile.png") + // ``` + // + // To narrow the type, use a call to get the refined type + // ``` + // asset!( + // image("myfile.png") + // .format(ImageType::Jpg) + // .size(512, 512) + // ) + // ``` + // + // But we need to decide the hint first before parsing the options + fn parse(input: syn::parse::ParseStream) -> syn::Result { + // Get the source of the macro, excluding the first token + let option_source = { + let fork = input.fork(); + fork.parse::()?; + fork.parse::()? + }; + + // And then parse the options + let src = input.parse::()?; + let src = src.value(); + let resource = ResourceAsset::parse_any(&src).unwrap(); + + fn parse_call(input: syn::parse::ParseStream) -> syn::Result { + let ident = input.parse::()?; + let content; + parenthesized!(content in input); + + // Parse as puncutated literals + let lits = Punctuated::::parse_separated_nonempty(&content)?; + + Ok(MethodCallOption { + method: ident, + args: lits, + }) + } + + let mut options = vec![]; + let name = None; + + while !input.is_empty() { + let option = parse_call(input); + if let Ok(option) = option { + options.push(option); + } else { + // todo: make sure we toss a warning in the output + let remaining: TokenStream2 = input.parse()?; + } + } + + let parsed_options = MethodCalls::new(options); + + Ok(Self { + option_source, + resource, + name, + parsed_options, + }) + } +} + +impl ToTokens for AssetParser { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + let option_source = &self.option_source; + let asset = &self.resource; + let link_section = crate::generate_link_section(&asset); + let input = asset.input.to_string(); + let bundled = asset.bundled.to_string(); + + let local = match asset.local.as_ref() { + Some(local) => { + let local = local.to_string(); + quote! { #local } + } + None => { + todo!("relative paths are not supported yet") + // quote! { + // { + // // ensure it exists by throwing away the include_bytes + // static _BLAH: &[u8] = include_bytes!(#input); + + // // But then pass along the path + // concat!(env!("CARGO_MANIFEST_DIR"), "/", file!(), "//", #input) + // } + // } + } + }; + + let manifest_dir: PathBuf = std::env::var("CARGO_MANIFEST_DIR").unwrap().into(); + let displayed_manifest_dir = manifest_dir.display().to_string(); + + tokens.extend(quote! { + Asset::new( + { + #link_section + manganis::AssetSource { + input: #input, + source_file: concat!(#displayed_manifest_dir, "/", file!()), + local: #local, + bundled: #bundled, + } + } + ) #option_source + }) + } +} + +struct MethodCalls { + options: Vec, +} + +/// A builder method in the form of `.method(arg1, arg2)` +struct MethodCallOption { + method: syn::Ident, + args: Punctuated, +} + +impl MethodCalls { + fn new(args: Vec) -> Option { + let asset_type = args.first()?.method.to_string(); + + let stack = args + .into_iter() + .skip(1) + .map(|x| (x.method.to_string(), x.args.into_iter().collect::>())) + .collect::>>(); + + let opts = match asset_type.as_str() { + "image" => { + let mut opts = ImageOptions::new(manganis_common::ImageType::Avif, Some((32, 32))); + // opts.set_preload(preload); + // opts.set_url_encoded(url_encoded); + // opts.set_low_quality_preview(low_quality_preview); + FileOptions::Image(opts) + } + + "video" => FileOptions::Video(VideoOptions::new(todo!())), + "font" => FileOptions::Font(FontOptions::new(todo!())), + "css" => FileOptions::Css(CssOptions::new()), + "js" => FileOptions::Js(JsOptions::new(todo!())), + "json" => FileOptions::Json(JsonOptions::new()), + other => FileOptions::Other(UnknownFileOptions::new(todo!())), + }; + + Some(opts) + } +} diff --git a/packages/manganis-macro/src/css.rs b/packages/manganis-macro/src/css.rs new file mode 100644 index 0000000000..0ad917b992 --- /dev/null +++ b/packages/manganis-macro/src/css.rs @@ -0,0 +1,130 @@ +use manganis_common::{AssetType, CssOptions, FileOptions, ManganisSupportError, ResourceAsset}; +use quote::{quote, ToTokens}; +use syn::{parenthesized, parse::Parse, LitBool}; + +// use crate::{generate_link_section, resource::ResourceAssetParser}; + +struct ParseCssOptions { + options: Vec, +} + +impl ParseCssOptions { + fn apply_to_options(self, file: &mut ResourceAsset) { + for option in self.options { + option.apply_to_options(file); + } + } +} + +impl Parse for ParseCssOptions { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let mut options = Vec::new(); + while !input.is_empty() { + options.push(input.parse::()?); + } + Ok(ParseCssOptions { options }) + } +} + +enum ParseCssOption { + UrlEncoded(bool), + Preload(bool), + Minify(bool), +} + +impl ParseCssOption { + fn apply_to_options(self, file: &mut ResourceAsset) { + match self { + ParseCssOption::Preload(_) | ParseCssOption::Minify(_) => { + file.with_options_mut(|options| { + if let FileOptions::Css(options) = options { + match self { + ParseCssOption::Minify(format) => { + options.set_minify(format); + } + ParseCssOption::Preload(preload) => { + options.set_preload(preload); + } + _ => {} + } + } + }) + } + ParseCssOption::UrlEncoded(url_encoded) => { + file.set_url_encoded(url_encoded); + } + } + } +} + +impl Parse for ParseCssOption { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let _ = input.parse::()?; + let ident = input.parse::()?; + let content; + parenthesized!(content in input); + match ident.to_string().as_str() { + "preload" => { + crate::verify_preload_valid(&ident)?; + Ok(ParseCssOption::Preload(true)) + } + "url_encoded" => { + Ok(ParseCssOption::UrlEncoded(true)) + } + "minify" => { + Ok(ParseCssOption::Minify(content.parse::()?.value())) + } + _ => Err(syn::Error::new( + proc_macro2::Span::call_site(), + format!( + "Unknown Css option: {}. Supported options are preload, url_encoded, and minify", + ident + ), + )), + } + } +} + +pub struct CssAssetParser { + asset: ResourceAsset, +} + +impl Parse for CssAssetParser { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let inside; + parenthesized!(inside in input); + let path = inside.parse::()?; + + let parsed_options = { + if input.is_empty() { + None + } else { + Some(input.parse::()?) + } + }; + + let path_as_str = path.value(); + + let mut asset: ResourceAsset = match ResourceAsset::parse_file(&path_as_str) { + Ok(asset) => asset.with_options(manganis_common::FileOptions::Css(CssOptions::new())), + Err(e) => { + return Err(syn::Error::new( + proc_macro2::Span::call_site(), + format!("{e}"), + )) + } + }; + + if let Some(parsed_options) = parsed_options { + parsed_options.apply_to_options(&mut asset); + } + + Ok(CssAssetParser { asset }) + } +} + +impl ToTokens for CssAssetParser { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + ResourceAssetParser::to_ref_tokens(&self.asset, tokens) + } +} diff --git a/packages/manganis-macro/src/font.rs b/packages/manganis-macro/src/font.rs new file mode 100644 index 0000000000..3bbac2412e --- /dev/null +++ b/packages/manganis-macro/src/font.rs @@ -0,0 +1,173 @@ +use manganis_common::{AssetType, CssOptions, ManganisSupportError, ResourceAsset}; +use quote::{quote, ToTokens}; +use syn::{bracketed, parenthesized, parse::Parse}; + +use crate::{generate_link_section, resource::ResourceAssetParser}; + +#[derive(Default)] +struct FontFamilies { + families: Vec, +} + +impl Parse for FontFamilies { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let inside; + bracketed!(inside in input); + let array = + syn::punctuated::Punctuated::::parse_separated_nonempty( + &inside, + )?; + Ok(FontFamilies { + families: array.into_iter().map(|f| f.value()).collect(), + }) + } +} + +#[derive(Default)] +struct FontWeights { + weights: Vec, +} + +impl Parse for FontWeights { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let inside; + bracketed!(inside in input); + let array = + syn::punctuated::Punctuated::::parse_separated_nonempty( + &inside, + )?; + Ok(FontWeights { + weights: array + .into_iter() + .map(|f| f.base10_parse().unwrap()) + .collect(), + }) + } +} + +struct ParseFontOptions { + families: FontFamilies, + weights: FontWeights, + text: Option, + display: Option, +} + +impl ParseFontOptions { + fn url(&self) -> String { + let mut segments = Vec::new(); + + let families: Vec<_> = self + .families + .families + .iter() + .map(|f| f.replace(' ', "+")) + .collect(); + if !families.is_empty() { + segments.push(format!("family={}", families.join("&"))); + } + + let weights: Vec<_> = self.weights.weights.iter().map(|w| w.to_string()).collect(); + if !weights.is_empty() { + segments.push(format!("weight={}", weights.join(","))); + } + + if let Some(text) = &self.text { + segments.push(format!("text={}", text.replace(' ', "+"))); + } + + if let Some(display) = &self.display { + segments.push(format!("display={}", display.replace(' ', "+"))); + } + + let query = if segments.is_empty() { + String::new() + } else { + format!("?{}", segments.join("&")) + }; + + format!("https://fonts.googleapis.com/css2{}", query) + } +} + +impl Parse for ParseFontOptions { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let mut families = None; + let mut weights = None; + let mut text = None; + let mut display = None; + loop { + if input.is_empty() { + break; + } + let _ = input.parse::()?; + let ident = input.parse::()?; + let inside; + parenthesized!(inside in input); + match ident.to_string().to_lowercase().as_str() { + "families" => { + families = Some(inside.parse::()?); + } + "weights" => { + weights = Some(inside.parse::()?); + } + "text" => { + text = Some(inside.parse::()?.value()); + } + "display" => { + display = Some(inside.parse::()?.value()); + } + _ => { + return Err(syn::Error::new( + proc_macro2::Span::call_site(), + format!("Unknown font option: {ident}. Supported options are families, weights, text, display"), + )) + } + } + } + + Ok(ParseFontOptions { + families: families.unwrap_or_default(), + weights: weights.unwrap_or_default(), + text, + display, + }) + } +} + +pub struct FontAssetParser { + asset: ResourceAsset, +} + +impl Parse for FontAssetParser { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let inside; + parenthesized!(inside in input); + if !inside.is_empty() { + return Err(syn::Error::new( + proc_macro2::Span::call_site(), + "Font assets do not support paths. Please use file() if you want to import a local font file", + )); + } + + let options = input.parse::()?; + + let url = options.url(); + let asset: ResourceAsset = match ResourceAsset::parse_file(&url) { + Ok(url) => url, + Err(e) => { + return Err(syn::Error::new( + proc_macro2::Span::call_site(), + format!("Failed to parse url: {url:?}\n{e}"), + )) + } + }; + + Ok(FontAssetParser { asset }) + } +} + +impl ToTokens for FontAssetParser { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + ResourceAssetParser::to_ref_tokens(&self.asset, tokens) + } +} diff --git a/packages/manganis-macro/src/image.rs b/packages/manganis-macro/src/image.rs new file mode 100644 index 0000000000..769cd57c09 --- /dev/null +++ b/packages/manganis-macro/src/image.rs @@ -0,0 +1,305 @@ +use manganis_common::ManganisSupportError; +use manganis_common::{AssetType, FileOptions, ImageOptions, ResourceAsset}; +use quote::{quote, ToTokens}; +use syn::{parenthesized, parse::Parse, Token}; + +use crate::generate_link_section; + +struct ParseImageOptions { + options: Vec, +} + +impl ParseImageOptions { + fn apply_to_options(self, file: &mut ResourceAsset, low_quality_preview: &mut bool) { + for option in self.options { + option.apply_to_options(file, low_quality_preview); + } + } +} + +impl Parse for ParseImageOptions { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let mut options = Vec::new(); + while !input.is_empty() { + options.push(input.parse::()?); + } + Ok(ParseImageOptions { options }) + } +} + +enum ParseImageOption { + Format(manganis_common::ImageType), + Size((u32, u32)), + Preload(bool), + UrlEncoded(bool), + Lqip(bool), +} + +impl ParseImageOption { + fn apply_to_options(self, file: &mut ResourceAsset, low_quality_preview: &mut bool) { + match self { + ParseImageOption::Format(_) + | ParseImageOption::Size(_) + | ParseImageOption::Preload(_) => file.with_options_mut(|options| { + if let FileOptions::Image(options) = options { + match self { + ParseImageOption::Format(format) => { + options.set_ty(format); + } + ParseImageOption::Size(size) => { + options.set_size(Some(size)); + } + ParseImageOption::Preload(preload) => { + options.set_preload(preload); + } + _ => {} + } + } + }), + ParseImageOption::UrlEncoded(url_encoded) => { + file.set_url_encoded(url_encoded); + } + ParseImageOption::Lqip(lqip) => { + *low_quality_preview = lqip; + } + } + } +} + +impl Parse for ParseImageOption { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let _ = input.parse::()?; + let ident = input.parse::()?; + let content; + parenthesized!(content in input); + match ident.to_string().as_str() { + "format" => { + let format = content.parse::()?; + Ok(ParseImageOption::Format(format.into())) + } + "size" => { + let size = content.parse::()?; + Ok(ParseImageOption::Size((size.width, size.height))) + } + "preload" => { + crate::verify_preload_valid(&ident)?; + Ok(ParseImageOption::Preload(true)) + } + "url_encoded" => { + Ok(ParseImageOption::UrlEncoded(true)) + } + "low_quality_preview" => { + Ok(ParseImageOption::Lqip(true)) + } + _ => Err(syn::Error::new( + proc_macro2::Span::call_site(), + format!( + "Unknown image option: {}. Supported options are format, size, preload, url_encoded, low_quality_preview", + ident + ), + )), + } + } +} + +struct ImageSize { + width: u32, + height: u32, +} + +impl Parse for ImageSize { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let width = input.parse::()?; + let _ = input.parse::()?; + let height = input.parse::()?; + Ok(ImageSize { + width: width.base10_parse()?, + height: height.base10_parse()?, + }) + } +} + +impl From for manganis_common::ImageType { + fn from(val: ImageType) -> Self { + val.0 + } +} + +impl Parse for ImageType { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let _ = input.parse::()?; + let _ = input.parse::()?; + let ident = input.parse::()?; + ident + .to_string() + .to_lowercase() + .as_str() + .parse::() + .map_err(|_| { + syn::Error::new( + proc_macro2::Span::call_site(), + format!( + "Unknown image type: {}. Supported types are png, jpeg, webp, avif", + ident + ), + ) + }) + .map(Self) + } +} + +#[derive(Clone, Copy)] +struct ImageType(manganis_common::ImageType); + +impl Default for ImageType { + fn default() -> Self { + Self(manganis_common::ImageType::Avif) + } +} + +pub struct ImageAssetParser { + asset: ResourceAsset, + low_quality_preview: Option, +} + +impl Parse for ImageAssetParser { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let inside; + parenthesized!(inside in input); + let path = inside.parse::()?; + + let parsed_options = { + if input.is_empty() { + None + } else { + Some(input.parse::()?) + } + }; + + let path_as_str = path.value(); + let mut asset: ResourceAsset = match ResourceAsset::parse_file(&path_as_str) { + Ok(path) => path.with_options(manganis_common::FileOptions::Image(ImageOptions::new( + manganis_common::ImageType::Avif, + None, + ))), + Err(e) => { + return Err(syn::Error::new( + proc_macro2::Span::call_site(), + format!("{e}"), + )) + } + }; + + let mut low_quality_preview = false; + if let Some(parsed_options) = parsed_options { + parsed_options.apply_to_options(&mut asset, &mut low_quality_preview); + } + + // let asset = manganis_common::AssetType::Resource(asset.clone()); + + // let file_name = if asset.url_encoded() { + // #[cfg(not(feature = "url-encoding"))] + // return Err(syn::Error::new( + // proc_macro2::Span::call_site(), + // "URL encoding is not enabled. Enable the url-encoding feature to use this feature", + // )); + // #[cfg(feature = "url-encoding")] + // Ok(crate::url_encoded_asset(&asset).map_err(|e| { + // syn::Error::new( + // proc_macro2::Span::call_site(), + // format!("Failed to encode file: {}", e), + // ) + // })?) + // } else { + // asset.served_location() + // }; + + // let low_quality_preview = if low_quality_preview { + // #[cfg(not(feature = "url-encoding"))] + // return Err(syn::Error::new( + // proc_macro2::Span::call_site(), + // "Low quality previews require URL encoding. Enable the url-encoding feature to use this feature", + // )); + + // #[cfg(feature = "url-encoding")] + // { + // let current_image_size = match asset.options() { + // manganis_common::FileOptions::Image(options) => options.size(), + // _ => None, + // }; + // let low_quality_preview_size = current_image_size + // .map(|(width, height)| { + // let width = width / 10; + // let height = height / 10; + // (width, height) + // }) + // .unwrap_or((32, 32)); + // let lqip = ResourceAsset::new(asset).with_options( + // manganis_common::FileOptions::Image(ImageOptions::new( + // manganis_common::ImageType::Avif, + // Some(low_quality_preview_size), + // )), + // ); + + // Some(crate::url_encoded_asset(&lqip).map_err(|e| { + // syn::Error::new( + // proc_macro2::Span::call_site(), + // format!("Failed to encode file: {}", e), + // ) + // })?) + // } + // } else { + // None + // }; + + let low_quality_preview = None; + + Ok(ImageAssetParser { + low_quality_preview, + asset, + }) + } +} + +impl ToTokens for ImageAssetParser { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + let link_section = generate_link_section(&self.asset); + let input = self.asset.input.to_string(); + + let bundled = self.asset.bundled.to_string(); + + let low_quality_preview = match &self.low_quality_preview { + Some(lqip) => quote! { Some(#lqip) }, + None => quote! { None }, + }; + + // If the asset is relative, we use concat!(env!("CARGO_MANIFEST_DIR"), "/", asset.input.path()) + let local = match self.asset.local.as_ref() { + Some(local) => { + let local = local.to_string(); + quote! { #local } + } + None => { + quote! { + { + static _: &[_] = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/", #input.path())); + concat!(env!("CARGO_MANIFEST_DIR"), "/", #input.path()) + } + } + } + }; + + tokens.extend(quote! { + { + #link_section + manganis::ImageAsset::new( + manganis::Asset { + input: #input, + local: #local, + bundled: #bundled, + } + ).with_preview(#low_quality_preview) + } + }) + } +} diff --git a/packages/manganis-macro/src/js.rs b/packages/manganis-macro/src/js.rs new file mode 100644 index 0000000000..88d4319261 --- /dev/null +++ b/packages/manganis-macro/src/js.rs @@ -0,0 +1,150 @@ +use manganis_common::{ + AssetType, FileOptions, JsOptions, JsType, ManganisSupportError, ResourceAsset, +}; +use quote::{quote, ToTokens}; +use syn::{parenthesized, parse::Parse, LitBool}; + +use crate::{generate_link_section, resource::ResourceAssetParser}; + +struct ParseJsOptions { + options: Vec, +} + +impl ParseJsOptions { + fn apply_to_options(self, file: &mut ResourceAsset) { + for option in self.options { + option.apply_to_options(file); + } + } +} + +impl Parse for ParseJsOptions { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let mut options = Vec::new(); + while !input.is_empty() { + options.push(input.parse::()?); + } + Ok(ParseJsOptions { options }) + } +} + +enum ParseJsOption { + UrlEncoded(bool), + Preload(bool), + Minify(bool), +} + +impl ParseJsOption { + fn apply_to_options(self, file: &mut ResourceAsset) { + match self { + ParseJsOption::Preload(_) | ParseJsOption::Minify(_) => { + file.with_options_mut(|options| { + if let FileOptions::Js(options) = options { + match self { + ParseJsOption::Minify(format) => { + options.set_minify(format); + } + ParseJsOption::Preload(preload) => { + options.set_preload(preload); + } + _ => {} + } + } + }) + } + ParseJsOption::UrlEncoded(url_encoded) => { + file.set_url_encoded(url_encoded); + } + } + } +} + +impl Parse for ParseJsOption { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let _ = input.parse::()?; + let ident = input.parse::()?; + let content; + parenthesized!(content in input); + match ident.to_string().as_str() { + "preload" => { + crate::verify_preload_valid(&ident)?; + Ok(ParseJsOption::Preload(true)) + } + "url_encoded" => Ok(ParseJsOption::UrlEncoded(true)), + "minify" => Ok(ParseJsOption::Minify(content.parse::()?.value())), + _ => Err(syn::Error::new( + proc_macro2::Span::call_site(), + format!( + "Unknown Js option: {}. Supported options are preload, url_encoded, and minify", + ident + ), + )), + } + } +} + +pub struct JsAssetParser { + asset: ResourceAsset, +} + +impl Parse for JsAssetParser { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let inside; + parenthesized!(inside in input); + let path = inside.parse::()?; + + let parsed_options = { + if input.is_empty() { + None + } else { + Some(input.parse::()?) + } + }; + + let path_as_str = path.value(); + let mut asset: ResourceAsset = match ResourceAsset::parse_file(&path_as_str) { + Ok(path) => { + path.with_options(manganis_common::FileOptions::Js(JsOptions::new(JsType::Js))) + } + Err(e) => { + return Err(syn::Error::new( + proc_macro2::Span::call_site(), + format!("{e}"), + )) + } + }; + + if let Some(parsed_options) = parsed_options { + parsed_options.apply_to_options(&mut asset); + } + + // let mut this_file = ResourceAsset::new(path.clone()) + // .with_options(manganis_common::FileOptions::Js(JsOptions::new(JsType::Js))); + // let asset = manganis_common::AssetType::Resource(this_file.clone()); + + // let file_name = if this_file.url_encoded() { + // #[cfg(not(feature = "url-encoding"))] + // return Err(syn::Error::new( + // proc_macro2::Span::call_site(), + // "URL encoding is not enabled. Enable the url-encoding feature to use this feature", + // )); + // #[cfg(feature = "url-encoding")] + // Ok(crate::url_encoded_asset(&this_file).map_err(|e| { + // syn::Error::new( + // proc_macro2::Span::call_site(), + // format!("Failed to encode file: {}", e), + // ) + // })?) + // } else { + // this_file.served_location() + // }; + + Ok(JsAssetParser { asset }) + } +} + +impl ToTokens for JsAssetParser { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + ResourceAssetParser::to_ref_tokens(&self.asset, tokens) + } +} diff --git a/packages/manganis-macro/src/json.rs b/packages/manganis-macro/src/json.rs new file mode 100644 index 0000000000..f3d2dfcb76 --- /dev/null +++ b/packages/manganis-macro/src/json.rs @@ -0,0 +1,70 @@ +use manganis_common::{AssetType, FileOptions, ManganisSupportError, ResourceAsset}; +use quote::{quote, ToTokens}; +use syn::{parenthesized, parse::Parse}; + +use crate::{generate_link_section, resource::ResourceAssetParser}; + +pub enum ParseJsonOption { + UrlEncoded(bool), + Preload(bool), +} + +impl ParseJsonOption { + fn apply(options: Vec, file: &mut ResourceAsset) { + for option in options { + option.apply_to_options(file); + } + } + fn apply_to_options(self, file: &mut ResourceAsset) { + match self { + ParseJsonOption::Preload(preload) => file.with_options_mut(|options| { + if let FileOptions::Json(options) = options { + options.set_preload(preload); + } + }), + ParseJsonOption::UrlEncoded(url_encoded) => { + file.set_url_encoded(url_encoded); + } + } + } +} + +impl Parse for ParseJsonOption { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let _ = input.parse::()?; + let ident = input.parse::()?; + let _content; + parenthesized!(_content in input); + match ident.to_string().as_str() { + "preload" => { + crate::verify_preload_valid(&ident)?; + Ok(ParseJsonOption::Preload(true)) + }, + "url_encoded" => Ok(ParseJsonOption::UrlEncoded(true)), + _ => Err(syn::Error::new( + proc_macro2::Span::call_site(), + format!( + "Unknown Json option: {}. Supported options are preload, url_encoded, and minify", + ident + ), + )), + } + } +} + +// let file_name = if asset.url_encoded() { +// #[cfg(not(feature = "url-encoding"))] +// return Err(syn::Error::new( +// proc_macro2::Span::call_site(), +// "URL encoding is not enabled. Enable the url-encoding feature to use this feature", +// )); +// #[cfg(feature = "url-encoding")] +// Ok(crate::url_encoded_asset(&asset).map_err(|e| { +// syn::Error::new( +// proc_macro2::Span::call_site(), +// format!("Failed to encode file: {}", e), +// ) +// })?) +// } else { +// asset.served_location() +// }; diff --git a/packages/manganis-macro/src/lib.rs b/packages/manganis-macro/src/lib.rs new file mode 100644 index 0000000000..0c265befec --- /dev/null +++ b/packages/manganis-macro/src/lib.rs @@ -0,0 +1,242 @@ +#![doc = include_str!("../README.md")] +#![deny(missing_docs)] + +// use css::CssAssetParser; +// use file::FileAssetParser; +// use folder::FolderAssetParser; +// use font::FontAssetParser; +// use image::ImageAssetParser; +// use js::JsAssetParser; +// use json::JsonAssetParser; +// use manganis_common::cache::macro_log_file; +use manganis_common::{MetadataAsset, ResourceAsset, TailwindAsset}; +use proc_macro::TokenStream; +use proc_macro2::Ident; +use proc_macro2::TokenStream as TokenStream2; +use quote::{quote, quote_spanned, ToTokens}; +// use resource::ResourceAssetParser; +use serde::Serialize; +use std::sync::atomic::AtomicBool; +use std::sync::atomic::Ordering; +use syn::{parse::Parse, parse_macro_input, LitStr}; + +pub(crate) mod asset; +// pub(crate) mod css; +// pub(crate) mod file; +// pub(crate) mod folder; +// pub(crate) mod font; +// pub(crate) mod image; +// pub(crate) mod js; +// pub(crate) mod json; +// pub(crate) mod resource; + +static LOG_FILE_FRESH: AtomicBool = AtomicBool::new(false); + +fn trace_to_file() { + // // If this is the first time the macro is used in the crate, set the subscriber to write to a file + // if !LOG_FILE_FRESH.fetch_or(true, Ordering::Relaxed) { + // let path = macro_log_file(); + // std::fs::create_dir_all(path.parent().unwrap()).unwrap(); + // let file = std::fs::OpenOptions::new() + // .create(true) + // .write(true) + // .truncate(true) + // .open(path) + // .unwrap(); + // tracing_subscriber::fmt::fmt().with_writer(file).init(); + // } +} + +/// this new approach will store the assets descriptions *inside the executable*. +/// The trick is to use the `link_section` attribute. +/// We force rust to store a json representation of the asset description +/// inside a particular region of the binary, with the label "manganis". +/// After linking, the "manganis" sections of the different executables will be merged. +fn generate_link_section(asset: &impl Serialize) -> TokenStream2 { + let position = proc_macro2::Span::call_site(); + + let asset_description = serde_json::to_string(asset).unwrap(); + + let len = asset_description.as_bytes().len(); + + let asset_bytes = syn::LitByteStr::new(asset_description.as_bytes(), position); + + let section_name = syn::LitStr::new( + manganis_common::linker::LinkSection::CURRENT.link_section, + position, + ); + + quote! { + #[link_section = #section_name] + #[used] + static ASSET: [u8; #len] = * #asset_bytes; + } +} + +/// Collects tailwind classes that will be included in the final binary and returns them unmodified +/// +/// ```rust +/// // You can include tailwind classes that will be collected into the final binary +/// const TAILWIND_CLASSES: &str = manganis::classes!("flex flex-col p-5"); +/// assert_eq!(TAILWIND_CLASSES, "flex flex-col p-5"); +/// ``` +#[proc_macro] +pub fn classes(input: TokenStream) -> TokenStream { + trace_to_file(); + + let input_as_str = parse_macro_input!(input as LitStr); + let input_as_str = input_as_str.value(); + + let asset = TailwindAsset::new(&input_as_str); + let link_section = generate_link_section(&asset); + + quote! { + { + #link_section + #input_as_str + } + } + .into_token_stream() + .into() +} + +/// The mg macro collects assets that will be included in the final binary +/// +/// # Files +/// +/// The file builder collects an arbitrary file. Relative paths are resolved relative to the package root +/// ```rust +/// const _: &str = manganis::asset!("src/asset.txt"); +/// ``` +/// Or you can use URLs to read the asset at build time from a remote location +/// ```rust +/// const _: &str = manganis::asset!("https://rustacean.net/assets/rustacean-flat-happy.png"); +/// ``` +/// +/// # Images +/// +/// You can collect images which will be automatically optimized with the image builder: +/// ```rust +/// const _: manganis::ImageAsset = manganis::asset!(image("rustacean-flat-gesture.png")); +/// ``` +/// Resize the image at compile time to make the assets file size smaller: +/// ```rust +/// const _: manganis::ImageAsset = manganis::asset!(image("rustacean-flat-gesture.png").size(52, 52)); +/// ``` +/// Or convert the image at compile time to a web friendly format: +/// ```rust +/// const _: manganis::ImageAsset = manganis::asset!(image("rustacean-flat-gesture.png").format(ImageFormat::Avif).size(52, 52)); +/// ``` +/// You can mark images as preloaded to make them load faster in your app +/// ```rust +/// const _: manganis::ImageAsset = manganis::asset!(image("rustacean-flat-gesture.png").preload()); +/// ``` +/// +/// # Fonts +/// +/// You can use the font builder to collect fonts that will be included in the final binary from google fonts +/// ```rust +/// const _: &str = manganis::asset!(font().families(["Roboto"])); +/// ``` +/// You can specify weights for the fonts +/// ```rust +/// const _: &str = manganis::asset!(font().families(["Roboto"]).weights([200])); +/// ``` +/// Or set the text to only include the characters you need +/// ```rust +/// const _: &str = manganis::asset!(font().families(["Roboto"]).weights([200]).text("Hello, world!")); +/// ``` +#[proc_macro] +pub fn asset(input: TokenStream) -> TokenStream { + trace_to_file(); + let asset = parse_macro_input!(input as asset::AssetParser); + + quote! { + #asset + } + .into_token_stream() + .into() +} + +/// // You can also collect arbitrary key-value pairs. The meaning of these pairs is determined by the CLI that processes your assets +/// ```rust +/// const _: () = manganis::meta!("opt-level": "3"); +/// ``` +#[proc_macro] +pub fn meta(input: TokenStream) -> TokenStream { + struct MetadataValue { + key: String, + value: String, + } + + impl Parse for MetadataValue { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let key = input.parse::()?.to_string(); + input.parse::()?; + let value = input.parse::()?.value(); + Ok(Self { key, value }) + } + } + + trace_to_file(); + + let md = parse_macro_input!(input as MetadataValue); + let asset = MetadataAsset::new(md.key.as_str(), md.value.as_str()); + let link_section = generate_link_section(&asset); + + quote! { + { + #link_section + } + } + .into_token_stream() + .into() +} + +// #[cfg(feature = "url-encoding")] +// pub(crate) fn url_encoded_asset( +// file_asset: &manganis_common::ResourceAsset, +// ) -> Result { +// use base64::Engine; + +// let target_directory = +// std::env::var("CARGO_TARGET_DIR").unwrap_or_else(|_| "target".to_string()); +// let output_folder = std::path::Path::new(&target_directory) +// .join("manganis") +// .join("assets"); +// std::fs::create_dir_all(&output_folder).map_err(|e| { +// syn::Error::new( +// proc_macro2::Span::call_site(), +// format!("Failed to create output folder: {}", e), +// ) +// })?; +// manganis_cli_support::process_file(file_asset, &output_folder).map_err(|e| { +// syn::Error::new( +// proc_macro2::Span::call_site(), +// format!("Failed to process file: {}", e), +// ) +// })?; +// let file = output_folder.join(file_asset.location().unique_name()); +// let data = std::fs::read(file).map_err(|e| { +// syn::Error::new( +// proc_macro2::Span::call_site(), +// format!("Failed to read file: {}", e), +// ) +// })?; +// let data = base64::engine::general_purpose::STANDARD_NO_PAD.encode(data); +// let mime = manganis_common::get_mime_from_ext(file_asset.options().extension()); +// Ok(format!("data:{mime};base64,{data}")) +// } + +pub(crate) fn verify_preload_valid(ident: &Ident) -> Result<(), syn::Error> { + // Compile time preload is only supported for the primary package + if std::env::var("CARGO_PRIMARY_PACKAGE").is_err() { + return Err(syn::Error::new( + ident.span(), + "The `preload` option is only supported for the primary package. Libraries should not preload assets or should preload assets\ + at runtime with utilities your framework provides", + )); + } + + Ok(()) +} diff --git a/packages/manganis-resolver/Cargo.toml b/packages/manganis-resolver/Cargo.toml new file mode 100644 index 0000000000..7ea1df5679 --- /dev/null +++ b/packages/manganis-resolver/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "manganis-resolver" +edition = "2021" +version.workspace = true + +[dependencies] +infer = { workspace = true } +manganis-common = { workspace = true } +manganis = { workspace = true } +dunce = "1.0.2" + +[target.'cfg(target_os = "macos")'.dependencies] +core-foundation = "0.9.3" + +[target.'cfg(target_os ="macos")'.dependencies] diff --git a/packages/manganis-resolver/src/lib.rs b/packages/manganis-resolver/src/lib.rs new file mode 100644 index 0000000000..f3bd86cb4e --- /dev/null +++ b/packages/manganis-resolver/src/lib.rs @@ -0,0 +1,19 @@ +// use manganis::Asset; +// use std::path::PathBuf; + +// pub struct VirtualFile {} + +// impl VirtualFile { +// /// Return an absolute, canonicalized path to the file +// pub fn path(&self) -> PathBuf { +// todo!() +// } +// } + +// pub fn resolve(asset: Asset) -> Result { +// todo!() +// } + +// pub fn resolve_macos(mg_path: &str) -> Result { +// todo!() +// } diff --git a/packages/manganis-test-package/Cargo.toml b/packages/manganis-test-package/Cargo.toml new file mode 100644 index 0000000000..60b2383110 --- /dev/null +++ b/packages/manganis-test-package/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "test-package" +version = "0.2.1" +edition = "2021" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +manganis = { workspace = true } +test-package-dependency = { path = "./test-package-dependency", version = "0.2.1" } +tracing-subscriber = "0.3.17" diff --git a/packages/manganis-test-package/assets/asset.txt b/packages/manganis-test-package/assets/asset.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/manganis-test-package/assets/data.json b/packages/manganis-test-package/assets/data.json new file mode 100644 index 0000000000..0a9f9ef376 --- /dev/null +++ b/packages/manganis-test-package/assets/data.json @@ -0,0 +1,6 @@ +{ + "hello": "world", + "foo": { + "bar": "baz" + } +} \ No newline at end of file diff --git a/packages/manganis-test-package/assets/script.js b/packages/manganis-test-package/assets/script.js new file mode 100644 index 0000000000..2c9e898449 --- /dev/null +++ b/packages/manganis-test-package/assets/script.js @@ -0,0 +1,3 @@ +export function hello() { + console.log("Hello world!"); +} \ No newline at end of file diff --git a/packages/manganis-test-package/assets/test.mp4 b/packages/manganis-test-package/assets/test.mp4 new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/manganis-test-package/src/main.rs b/packages/manganis-test-package/src/main.rs new file mode 100644 index 0000000000..fd65c5b21e --- /dev/null +++ b/packages/manganis-test-package/src/main.rs @@ -0,0 +1,43 @@ +// The assets must be configured with the [CLI](cli-support/examples/cli.rs) before this example can be run. + +use std::path::PathBuf; + +use test_package_dependency::*; + +// const TEXT_FILE: &str = manganis::asset!("/test-package-dependency/src/asset.txt"); +// const VIDEO_FILE: &str = manganis::mg!("/assets/test.mp4"); + +// const ALL_ASSETS: &[&str] = &[ +// VIDEO_FILE, +// TEXT_FILE, +// TEXT_ASSET, +// IMAGE_ASSET, +// HTML_ASSET, +// CSS_ASSET, +// PNG_ASSET, +// RESIZED_PNG_ASSET.path(), +// JPEG_ASSET.path(), +// RESIZED_JPEG_ASSET.path(), +// AVIF_ASSET.path(), +// RESIZED_AVIF_ASSET.path(), +// WEBP_ASSET.path(), +// RESIZED_WEBP_ASSET.path(), +// ROBOTO_FONT, +// COMFORTAA_FONT, +// ROBOTO_FONT_LIGHT_FONT, +// SCRIPT, +// DATA, +// FOLDER, +// ]; + +fn main() { + tracing_subscriber::fmt::init(); + + todo!() + // // Make sure the macro paths match with the paths that actually exist + // for path in ALL_ASSETS { + // let path = PathBuf::from(path); + // println!("{:?}", path); + // assert!(!external_paths_should_exist || path.exists()); + // } +} diff --git a/packages/manganis-test-package/test-package-dependency/Cargo.toml b/packages/manganis-test-package/test-package-dependency/Cargo.toml new file mode 100644 index 0000000000..3114a601a1 --- /dev/null +++ b/packages/manganis-test-package/test-package-dependency/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "test-package-dependency" +version = "0.2.1" +edition = "2021" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +manganis = { workspace = true } +test-package-nested-dependency = { path = "../test-package-nested-dependency" } diff --git a/packages/manganis-test-package/test-package-dependency/src/asset.txt b/packages/manganis-test-package/test-package-dependency/src/asset.txt new file mode 100644 index 0000000000..793aa682b0 --- /dev/null +++ b/packages/manganis-test-package/test-package-dependency/src/asset.txt @@ -0,0 +1 @@ +This is a test \ No newline at end of file diff --git a/packages/manganis-test-package/test-package-dependency/src/file.rs b/packages/manganis-test-package/test-package-dependency/src/file.rs new file mode 100644 index 0000000000..8a4ca522a7 --- /dev/null +++ b/packages/manganis-test-package/test-package-dependency/src/file.rs @@ -0,0 +1,7 @@ +pub static FORCE_IMPORT: u32 = 0; + +// const _: &str = manganis::classes!("flex flex-col p-5"); +// pub const TEXT_ASSET: &str = manganis::asset!("./src/asset.txt"); +// pub const IMAGE_ASSET: &str = +// manganis::asset!("https://rustacean.net/assets/rustacean-flat-happy.png"); +// pub const HTML_ASSET: &str = manganis::asset!("https://github.com/DioxusLabs/dioxus"); diff --git a/packages/manganis-test-package/test-package-dependency/src/lib.rs b/packages/manganis-test-package/test-package-dependency/src/lib.rs new file mode 100644 index 0000000000..5eb015a968 --- /dev/null +++ b/packages/manganis-test-package/test-package-dependency/src/lib.rs @@ -0,0 +1,8 @@ +mod file; +pub use file::*; +pub use test_package_nested_dependency::*; + +const _: &str = manganis::classes!("flex flex-col p-1"); +const _: &str = manganis::classes!("flex flex-col p-2"); +const _: &str = manganis::classes!("flex flex-col p-3"); +const _: &str = manganis::classes!("flex flex-col p-4"); diff --git a/packages/manganis-test-package/test-package-nested-dependency/Cargo.toml b/packages/manganis-test-package/test-package-nested-dependency/Cargo.toml new file mode 100644 index 0000000000..8fe24b81a6 --- /dev/null +++ b/packages/manganis-test-package/test-package-nested-dependency/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "test-package-nested-dependency" +version = "0.2.1" +edition = "2021" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +manganis = { workspace = true } + diff --git a/packages/manganis-test-package/test-package-nested-dependency/all_the_assets/data.json b/packages/manganis-test-package/test-package-nested-dependency/all_the_assets/data.json new file mode 100644 index 0000000000..0a9f9ef376 --- /dev/null +++ b/packages/manganis-test-package/test-package-nested-dependency/all_the_assets/data.json @@ -0,0 +1,6 @@ +{ + "hello": "world", + "foo": { + "bar": "baz" + } +} \ No newline at end of file diff --git a/packages/manganis-test-package/test-package-nested-dependency/all_the_assets/rustacean-flat-gesture.png b/packages/manganis-test-package/test-package-nested-dependency/all_the_assets/rustacean-flat-gesture.png new file mode 100644 index 0000000000000000000000000000000000000000..866412dc60dda68a204978cb15726b22c28567d7 GIT binary patch literal 49537 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 F{{ZTl;J^R? literal 0 HcmV?d00001 diff --git a/packages/manganis-test-package/test-package-nested-dependency/all_the_assets/script.js b/packages/manganis-test-package/test-package-nested-dependency/all_the_assets/script.js new file mode 100644 index 0000000000..dfb90b8d27 --- /dev/null +++ b/packages/manganis-test-package/test-package-nested-dependency/all_the_assets/script.js @@ -0,0 +1,7 @@ +export function hello() { + console.log("Hello world!"); + local(); +} +function local() { + console.log("minify this") +} \ No newline at end of file diff --git a/packages/manganis-test-package/test-package-nested-dependency/all_the_assets/style.css b/packages/manganis-test-package/test-package-nested-dependency/all_the_assets/style.css new file mode 100644 index 0000000000..fbcfd084c1 --- /dev/null +++ b/packages/manganis-test-package/test-package-nested-dependency/all_the_assets/style.css @@ -0,0 +1,8 @@ +.foo { + color: red; +} + +.bar { + color: red; + width: calc(1px + 1px); +} \ No newline at end of file diff --git a/packages/manganis-test-package/test-package-nested-dependency/src/file.rs b/packages/manganis-test-package/test-package-nested-dependency/src/file.rs new file mode 100644 index 0000000000..2e560757a5 --- /dev/null +++ b/packages/manganis-test-package/test-package-nested-dependency/src/file.rs @@ -0,0 +1,31 @@ +use manganis::{asset, classes, Asset, ImageAsset, ImageType}; + +// const _: &str = manganis::classes!("flex flex-row p-4"); +// pub const CSS_ASSET: &str = asset!(file("/all_the_assets/style.css")); +// pub const PNG_ASSET: &str = asset!(file("/all_the_assets/rustacean-flat-gesture.png")); +// pub const RESIZED_PNG_ASSET: ImageAsset = +// asset!(image("/all_the_assets/rustacean-flat-gesture.png").size(52, 52)); + +// pub const JPEG_ASSET: ImageAsset = +// asset!(image("/all_the_assets/rustacean-flat-gesture.png").format(ImageType::Jpg)); + +// pub const RESIZED_JPEG_ASSET: ImageAsset = +// asset!(image("/all_the_assets/rustacean-flat-gesture.png") +// .format(ImageType::Jpg) +// .size(52, 52)); + +// pub const AVIF_ASSET: ImageAsset = +// asset!(image("/all_the_assets/rustacean-flat-gesture.png").format(ImageType::Avif)); + +// pub const RESIZED_AVIF_ASSET: ImageAsset = +// asset!(image("/all_the_assets/rustacean-flat-gesture.png") +// .format(ImageType::Avif) +// .size(52, 52)); + +// pub const WEBP_ASSET: ImageAsset = +// asset!(image("/all_the_assets/rustacean-flat-gesture.png").format(ImageType::Webp)); + +// pub const RESIZED_WEBP_ASSET: ImageAsset = +// asset!(image("/all_the_assets/rustacean-flat-gesture.png") +// .format(ImageType::Webp) +// .size(52, 52)); diff --git a/packages/manganis-test-package/test-package-nested-dependency/src/folder.rs b/packages/manganis-test-package/test-package-nested-dependency/src/folder.rs new file mode 100644 index 0000000000..d6c1a716d1 --- /dev/null +++ b/packages/manganis-test-package/test-package-nested-dependency/src/folder.rs @@ -0,0 +1,3 @@ +use manganis::Asset; + +pub const FOLDER: Asset = manganis::asset!("/all_the_assets"); diff --git a/packages/manganis-test-package/test-package-nested-dependency/src/font.rs b/packages/manganis-test-package/test-package-nested-dependency/src/font.rs new file mode 100644 index 0000000000..bc28c77dc4 --- /dev/null +++ b/packages/manganis-test-package/test-package-nested-dependency/src/font.rs @@ -0,0 +1,16 @@ +use manganis::Asset; + +// pub const ROBOTO_FONT: Asset = manganis::asset!(font() +// .families(["Roboto"]) +// .weights([400]) +// .text("hello world")); + +// pub const COMFORTAA_FONT: Asset = manganis::asset!(font() +// .families(["Comfortaa"]) +// .weights([400]) +// .text("hello world")); + +// pub const ROBOTO_FONT_LIGHT_FONT: Asset = manganis::asset!(font() +// .families(["Roboto"]) +// .weights([200]) +// .text("hello world")); diff --git a/packages/manganis-test-package/test-package-nested-dependency/src/js.rs b/packages/manganis-test-package/test-package-nested-dependency/src/js.rs new file mode 100644 index 0000000000..f6f4c9779d --- /dev/null +++ b/packages/manganis-test-package/test-package-nested-dependency/src/js.rs @@ -0,0 +1,4 @@ +use manganis::Asset; + +pub const SCRIPT: Asset = manganis::asset!("/all_the_assets/script.js"); +pub const DATA: Asset = manganis::asset!("/all_the_assets/data.json"); diff --git a/packages/manganis-test-package/test-package-nested-dependency/src/lib.rs b/packages/manganis-test-package/test-package-nested-dependency/src/lib.rs new file mode 100644 index 0000000000..f22f81ddba --- /dev/null +++ b/packages/manganis-test-package/test-package-nested-dependency/src/lib.rs @@ -0,0 +1,13 @@ +mod file; +pub use file::*; +mod font; +pub use font::*; +mod js; +pub use js::*; +mod folder; +pub use folder::*; + +const _: &str = manganis::classes!("flex flex-row p-1"); +const _: &str = manganis::classes!("flex flex-row p-2"); +const _: &str = manganis::classes!("flex flex-row p-3"); +const _: &str = manganis::classes!("flex flex-row p-4"); diff --git a/packages/manganis/Cargo.toml b/packages/manganis/Cargo.toml new file mode 100644 index 0000000000..a80a158aec --- /dev/null +++ b/packages/manganis/Cargo.toml @@ -0,0 +1,33 @@ +[package] +# Manganese is a rusting catalyst. Manganis makes it faster to collect rust assets (and has almost no google search results) +name = "manganis" +version.workspace = true +authors = ["Evan Almloff"] +edition = "2021" +description = "Ergonomic, automatic, cross crate asset collection and optimization" +license = "MIT OR Apache-2.0" +repository = "https://github.com/DioxusLabs/manganis/" +homepage = "https://dioxuslabs.com" +keywords = ["assets"] + +[lib] + +[dependencies] +manganis-macro = { workspace = true, optional = true } +once_cell = "1.19.0" +dirs = "5.0.1" +infer = { workspace = true } +manganis-common = { workspace = true } +dunce = "1.0.2" + +[target.'cfg(target_os = "macos")'.dependencies] +core-foundation = "0.9.3" + +[target.'cfg(target_os ="macos")'.dependencies] + + +[features] +default = ["macro"] +html = [] +url-encoding = ["manganis-macro/url-encoding"] +macro = ["dep:manganis-macro"] diff --git a/packages/manganis/src/asset.rs b/packages/manganis/src/asset.rs new file mode 100644 index 0000000000..0f2993a3ef --- /dev/null +++ b/packages/manganis/src/asset.rs @@ -0,0 +1,132 @@ +/// This is basically a compile-time version of ResourceAsset +/// A struct that contains the relative and absolute paths of an asset +#[derive(Debug, PartialEq, PartialOrd, Clone, Hash)] +pub struct Asset { + /// The input URI given to the macro + pub input: &'static str, + + /// The sourcefile of the asset + pub source_file: &'static str, + + /// + pub local: &'static str, + + /// + pub bundled: &'static str, +} + +impl std::fmt::Display for Asset { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.resolve().fmt(f) + } +} + +impl From for String { + fn from(asset: Asset) -> Self { + asset.resolve() + } +} + +impl From for Option { + fn from(asset: Asset) -> Self { + Some(asset.resolve()) + } +} + +impl Asset { + /// Resolve the asset against the bundle + pub fn resolve(&self) -> String { + // A fallback for non-bundled apps with no support for manganis + // + // Necessary to get `cargo run` to work when folks use `cargo run --example demo` on the main + // dioxus repo. + // + // We could also say, suggest that they install `dioxus-cli` and use that instead. + if local_fallback() { + return self.bundled.to_string(); + } + + // the rest of the platforms are bundled, so we need to resolve the asset against the bundle + + // for web, we just do the basepath thing + #[cfg(target_arch = "wasm32")] + { + return format!("/{}", self.bundled); + } + + // On mac do a bundle lookup + #[cfg(target_os = "macos")] + { + let bundle = core_foundation::bundle::CFBundle::main_bundle(); + let bundle_path = bundle.path().unwrap(); + let resources_path = bundle.resources_path().unwrap(); + let absolute_resources_root = bundle_path.join(resources_path); + return dunce::canonicalize(absolute_resources_root) + .ok() + .unwrap() + .display() + .to_string(); + } + + // // on ios do a bundle lookup + // #[cfg(target_os = "ios")] + // { + // let bundle = core_foundation::bundle::CFBundle::main_bundle(); + // let bundle_path = bundle.path().unwrap(); + // let resources_path = bundle.resources_path().unwrap(); + // let absolute_resources_root = bundle_path.join(resources_path); + // return dunce::canonicalize(absolute_resources_root) + // .ok() + // .unwrap() + // .display() + // .to_string(); + // } + + // on android do a bundle lookup + + // on windows, + + todo!() + } + + fn name(&self) -> String { + if BUNDLED { + self.input.to_string() + } else { + self.local.to_string() + } + } +} + +static BUNDLED: bool = false; +// static BUNDLED: bool = option_env!("MG_BUNDLED").is_some(); + +/// Returns whether the app should use the local fallback or not +/// +/// A `cargo run` will not be bundled but the asset will be resolved against the filesystem through +/// dependencies. +pub fn local_fallback() -> bool { + // If we're bundled, manganis is active + if BUNDLED { + return false; + } + + // Otherwise, check if the MG_RUNTIME env var is set + // this prevents us from thrashing the cache when running `cargo run` + static USE_FALLBACK: once_cell::sync::OnceCell = once_cell::sync::OnceCell::new(); + *USE_FALLBACK.get_or_init(|| { + // If the env var is present, we use the bundled path + if std::env::var("MG_RUNTIME").is_ok() { + return false; + } + + // on wasm, there's no env vars... but the app is not bundled + // for now we just assume you're using manganis in a wasm app + if cfg!(target_arch = "wasm32") { + return false; + } + + // No env var, not wasm, not bundled, so we're not using manganis + true + }) +} diff --git a/packages/manganis/src/builder.rs b/packages/manganis/src/builder.rs new file mode 100644 index 0000000000..e460cc88e6 --- /dev/null +++ b/packages/manganis/src/builder.rs @@ -0,0 +1,298 @@ +/// Asset +#[derive(Debug, PartialEq, Clone, Copy, Hash)] +pub struct Asset { + src: AssetSource, +} + +impl From for String { + fn from(value: Asset) -> Self { + value.to_string() + } +} +impl From for Option { + fn from(value: Asset) -> Self { + Some(value.to_string()) + } +} + +impl Asset { + /// + pub const fn new(src: AssetSource) -> Self { + Self { src } + } + + /// + pub const fn folder(self) -> FolderAsset { + FolderAsset::new(self.src) + } + + /// Convert this asset into an image asset + pub const fn image(self) -> ImageAsset { + ImageAsset::new(self.src) + } + + /// + pub const fn video(self) -> VideoAsset { + VideoAsset::new(self.src) + } + + /// + pub const fn json(self) -> JsonAsset { + JsonAsset::new(self.src) + } + + /// + pub const fn css(self) -> CssAsset { + CssAsset::new(self.src) + } + + /// + pub const fn javascript(self) -> JavaScriptAsset { + JavaScriptAsset::new(self.src) + } + + /// + pub const fn typescript(self) -> TypeScriptAsset { + TypeScriptAsset::new(self.src) + } +} + +impl std::fmt::Display for Asset { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.src.input) + } +} + +/// +#[derive(Debug, PartialEq, PartialOrd, Clone, Copy, Hash)] +pub struct AssetSource { + /// The input URI given to the macro + pub input: &'static str, + + /// The sourcefile of the asset + pub source_file: &'static str, + + /// + pub local: &'static str, + + /// + pub bundled: &'static str, +} + +/// +pub struct CssAsset { + src: AssetSource, +} + +impl CssAsset { + /// + pub const fn new(src: AssetSource) -> Self { + Self { src } + } + + /// + pub const fn minify(self, minify: bool) -> Self { + todo!() + } +} + +/// +pub struct VideoAsset { + src: AssetSource, +} + +impl VideoAsset { + /// + pub const fn new(src: AssetSource) -> Self { + Self { src } + } +} + +/// +/// +pub struct JsonAsset { + src: AssetSource, +} +impl JsonAsset { + /// + pub const fn new(src: AssetSource) -> Self { + Self { src } + } + + /// Make the json preloaded + /// + /// > **Note**: This will do nothing outside of the `asset!` macro + /// + /// Preloading json will make the json start to load as soon as possible. This is useful for json that will be run soon after the page loads or json that may not be used immediately, but should start loading sooner + /// + /// ```rust + /// const _: &str = manganis::asset!(json("assets/data.json").preload()); + /// ``` + #[allow(unused)] + pub const fn preload(self) -> Self { + self + } + + /// Make the json URL encoded + /// + /// > **Note**: This will do nothing outside of the `asset!` macro + /// + /// URL encoding an image inlines the data of the json into the URL. This is useful for small json files that should load as soon as the html is parsed + /// + /// ```rust + /// const _: &str = manganis::asset!(json("assets/data.json").url_encoded()); + /// ``` + #[allow(unused)] + pub const fn url_encoded(self) -> Self { + self + } +} + +/// +/// +/// +pub struct JavaScriptAsset { + src: AssetSource, +} +impl JavaScriptAsset { + /// + pub const fn new(src: AssetSource) -> Self { + Self { src } + } +} + +/// +/// +pub struct TypeScriptAsset { + src: AssetSource, +} + +impl TypeScriptAsset { + /// + pub const fn new(src: AssetSource) -> Self { + Self { src } + } +} +/// +pub struct FolderAsset { + src: AssetSource, +} + +impl FolderAsset { + /// + pub const fn new(src: AssetSource) -> Self { + Self { src } + } + + /// + pub const fn build(self) -> FolderAsset { + FolderAsset { src: self.src } + } +} + +/// Asset +#[derive(Debug, PartialEq, Clone, Copy, Hash)] +pub struct ImageAsset { + src: AssetSource, +} + +impl std::fmt::Display for ImageAsset { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + todo!() + } +} + +/// The type of an image. You can read more about the tradeoffs between image formats [here](https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types) +#[derive(Debug, PartialEq, PartialOrd, Clone, Copy, Hash)] +pub enum ImageType { + /// A png image. Png images cannot contain transparency and tend to compress worse than other formats + Png, + /// A jpg image. Jpg images can contain transparency and tend to compress better than png images + Jpg, + /// A webp image. Webp images can contain transparency and tend to compress better than jpg images + Webp, + /// An avif image. Avif images can compress slightly better than webp images but are not supported by all browsers + Avif, +} + +impl ImageAsset { + /// + pub const fn new(src: AssetSource) -> Self { + Self { src } + } + + /// + pub const fn build(self) -> ImageAsset { + ImageAsset { src: self.src } + } + + /// Sets the format of the image + /// + /// > **Note**: This will do nothing outside of the `asset!` macro + /// + /// Choosing the right format can make your site load much faster. Webp and avif images tend to be a good default for most images + /// + /// ```rust + /// const _: manganis::ImageAsset = manganis::asset!(image("https://avatars.githubusercontent.com/u/79236386?s=48&v=4").format(ImageType::Webp)); + /// ``` + #[allow(unused)] + pub const fn format(self, format: ImageType) -> Self { + self + } + + /// Sets the size of the image + /// + /// > **Note**: This will do nothing outside of the `asset!` macro + /// + /// If you only use the image in one place, you can set the size of the image to the size it will be displayed at. This will make the image load faster + /// + /// ```rust + /// const _: manganis::ImageAsset = manganis::asset!(image("https://avatars.githubusercontent.com/u/79236386?s=48&v=4").size(512, 512)); + /// ``` + #[allow(unused)] + pub const fn size(self, x: u32, y: u32) -> Self { + self + } + + /// Make the image use a low quality preview + /// + /// > **Note**: This will do nothing outside of the `asset!` macro + /// + /// A low quality preview is a small version of the image that will load faster. This is useful for large images on mobile devices that may take longer to load + /// + /// ```rust + /// const _: manganis::ImageAsset = manganis::asset!(image("https://avatars.githubusercontent.com/u/79236386?s=48&v=4").low_quality_preview()); + /// ``` + #[allow(unused)] + pub const fn low_quality_preview(self) -> Self { + self + } + + /// Make the image preloaded + /// + /// > **Note**: This will do nothing outside of the `asset!` macro + /// + /// Preloading an image will make the image start to load as soon as possible. This is useful for images that will be displayed soon after the page loads or images that may not be visible immediately, but should start loading sooner + /// + /// ```rust + /// const _: manganis::ImageAsset = manganis::asset!(image("https://avatars.githubusercontent.com/u/79236386?s=48&v=4").preload()); + /// ``` + #[allow(unused)] + pub const fn preload(self) -> Self { + self + } + + /// Make the image URL encoded + /// + /// > **Note**: This will do nothing outside of the `asset!` macro + /// + /// URL encoding an image inlines the data of the image into the URL. This is useful for small images that should load as soon as the html is parsed + /// + /// ```rust + /// const _: manganis::ImageAsset = manganis::asset!(image("https://avatars.githubusercontent.com/u/79236386?s=48&v=4").url_encoded()); + /// ``` + #[allow(unused)] + pub const fn url_encoded(self) -> Self { + self + } +} diff --git a/packages/manganis/src/csss.rs b/packages/manganis/src/csss.rs new file mode 100644 index 0000000000..670a54ffff --- /dev/null +++ b/packages/manganis/src/csss.rs @@ -0,0 +1,65 @@ +/// A builder for a css asset. This must be used in the [`asset!`] macro. +/// +/// > **Note**: This will do nothing outside of the `asset!` macro +pub struct CssAssetBuilder; + +impl CssAssetBuilder { + /// Sets whether the css should be minified (default: true) + /// + /// > **Note**: This will do nothing outside of the `asset!` macro + /// + /// Minifying the css can make your site load faster by loading less data + /// + /// ```rust + /// const _: &str = manganis::asset!(css("https://sindresorhus.com/github-markdown-css/github-markdown.css").minify(false)); + /// ``` + #[allow(unused)] + pub const fn minify(self, minify: bool) -> Self { + Self + } + + /// Make the css preloaded + /// + /// > **Note**: This will do nothing outside of the `asset!` macro + /// + /// Preloading css will make the css start to load as soon as possible. This is useful for css that will be displayed soon after the page loads or css that may not be visible immediately, but should start loading sooner + /// + /// ```rust + /// const _: &str = manganis::asset!(css("https://sindresorhus.com/github-markdown-css/github-markdown.css").preload()); + /// ``` + #[allow(unused)] + pub const fn preload(self) -> Self { + Self + } + + /// Make the css URL encoded + /// + /// > **Note**: This will do nothing outside of the `asset!` macro + /// + /// URL encoding an image inlines the data of the css into the URL. This is useful for small css files that should load as soon as the html is parsed + /// + /// ```rust + /// const _: &str = manganis::asset!(css("https://sindresorhus.com/github-markdown-css/github-markdown.css").url_encoded()); + /// ``` + #[allow(unused)] + pub const fn url_encoded(self) -> Self { + Self + } +} + +/// Create an css asset from the local path or url to the css +/// +/// > **Note**: This will do nothing outside of the `asset!` macro +/// +/// You can collect css which will be automatically minified with the css builder: +/// ```rust +/// const _: &str = manganis::asset!(css("https://sindresorhus.com/github-markdown-css/github-markdown.css")); +/// ``` +/// You can mark css as preloaded to make them load faster in your app: +/// ```rust +/// const _: &str = manganis::asset!(css("https://sindresorhus.com/github-markdown-css/github-markdown.css").preload()); +/// ``` +#[allow(unused)] +pub const fn css(path: &'static str) -> CssAssetBuilder { + CssAssetBuilder +} diff --git a/packages/manganis/src/files.rs b/packages/manganis/src/files.rs new file mode 100644 index 0000000000..0501e984d2 --- /dev/null +++ b/packages/manganis/src/files.rs @@ -0,0 +1,16 @@ +/// Create an file asset from the local path or url to the file +/// +/// > **Note**: This will do nothing outside of the `asset!` macro +/// +/// The file builder collects an arbitrary file. Relative paths are resolved relative to the package root +/// ```rust +/// const _: &str = manganis::asset!("/assets/asset.txt"); +/// ``` +/// Or you can use URLs to read the asset at build time from a remote location +/// ```rust +/// const _: &str = manganis::asset!("https://rustacean.net/assets/rustacean-flat-happy.png"); +/// ``` +#[allow(unused)] +pub const fn file(path: &'static str) -> &'static str { + path +} diff --git a/packages/manganis/src/folder.rs b/packages/manganis/src/folder.rs new file mode 100644 index 0000000000..a5c1e2697f --- /dev/null +++ b/packages/manganis/src/folder.rs @@ -0,0 +1,24 @@ +use crate::Asset; + +/// This is basically a compile-time version of ResourceAsset +/// A struct that contains the relative and absolute paths of an asset +#[derive(Debug, PartialEq, PartialOrd, Clone, Hash)] +pub struct FolderAsset { + src: Asset, +} + +/// +pub struct FolderAssetBuilder; + +/// Create an folder asset from the local path +/// +/// > **Note**: This will do nothing outside of the `asset!` macro +/// +/// The folder builder collects an arbitrary local folder. Relative paths are resolved relative to the package root +/// ```rust +/// const _: &str = manganis::asset!("/assets"); +/// ``` +#[allow(unused)] +pub const fn folder(src: &'static str) -> FolderAssetBuilder { + FolderAssetBuilder {} +} diff --git a/packages/manganis/src/fonts.rs b/packages/manganis/src/fonts.rs new file mode 100644 index 0000000000..1f69e56010 --- /dev/null +++ b/packages/manganis/src/fonts.rs @@ -0,0 +1,75 @@ +/// A builder for a font asset. This must be used in the `asset!` macro. +/// +/// > **Note**: This will do nothing outside of the `asset!` macro +pub struct FontAssetBuilder; + +impl FontAssetBuilder { + /// Sets the font family of the font + /// + /// > **Note**: This will do nothing outside of the `asset!` macro + /// + /// ```rust + /// const _: &str = manganis::asset!(font().families(["Roboto"])); + /// ``` + #[allow(unused)] + pub const fn families(self, families: [&'static str; N]) -> Self { + Self + } + + /// Sets the font weight of the font + /// + /// > **Note**: This will do nothing outside of the `asset!` macro + /// + /// ```rust + /// const _: &str = manganis::asset!(font().families(["Roboto"]).weights([200])); + /// ``` + #[allow(unused)] + pub const fn weights(self, weights: [u32; N]) -> Self { + Self + } + + /// Sets the subset of text that the font needs to support. The font will only include the characters in the text which can make the font file size significantly smaller + /// + /// > **Note**: This will do nothing outside of the `asset!` macro + /// + /// ```rust + /// const _: &str = manganis::asset!(font().families(["Roboto"]).weights([200]).text("Hello, world!")); + /// ``` + #[allow(unused)] + pub const fn text(self, text: &'static str) -> Self { + Self + } + + /// Sets the [display](https://www.w3.org/TR/css-fonts-4/#font-display-desc) of the font. The display control what happens when the font is unavailable + /// + /// > **Note**: This will do nothing outside of the `asset!` macro + /// + /// ```rust + /// const _: &str = manganis::asset!(font().families(["Roboto"]).weights([200]).text("Hello, world!").display("swap")); + /// ``` + #[allow(unused)] + pub const fn display(self, display: &'static str) -> Self { + Self + } +} + +/// Create a font asset +/// +/// > **Note**: This will do nothing outside of the `asset!` macro +/// +/// You can use the font builder to collect fonts that will be included in the final binary from google fonts +/// ```rust +/// const _: &str = manganis::asset!(font().families(["Roboto"])); +/// ``` +/// You can specify weights for the fonts +/// ```rust +/// const _: &str = manganis::asset!(font().families(["Roboto"]).weights([200])); +/// ``` +/// Or set the text to only include the characters you need +/// ```rust +/// const _: &str = manganis::asset!(font().families(["Roboto"]).weights([200]).text("Hello, world!")); +/// ``` +#[allow(unused)] +pub const fn font() -> FontAssetBuilder { + FontAssetBuilder +} diff --git a/packages/manganis/src/images.rs b/packages/manganis/src/images.rs new file mode 100644 index 0000000000..e05626ed07 --- /dev/null +++ b/packages/manganis/src/images.rs @@ -0,0 +1,177 @@ +use crate::Asset; + +/// An image asset that is built by the [`asset!`] macro +#[derive(Debug, PartialEq, PartialOrd, Clone, Hash)] +pub struct ImageAsset { + /// The path to the image + asset: Asset, + /// A low quality preview of the image that is URL encoded + preview: Option<&'static str>, + /// A caption for the image + caption: Option<&'static str>, +} + +impl ImageAsset { + /// Creates a new image asset + pub const fn new(path: Asset) -> Self { + Self { + asset: path, + preview: None, + caption: None, + } + } + + /// Returns the path to the image + pub const fn path(&self) -> &'static str { + self.asset.bundled + } + + /// Returns the preview of the image + pub const fn preview(&self) -> Option<&'static str> { + self.preview + } + + /// Sets the preview of the image + pub const fn with_preview(self, preview: Option<&'static str>) -> Self { + Self { preview, ..self } + } + + /// Returns the caption of the image + pub const fn caption(&self) -> Option<&'static str> { + self.caption + } + + /// Sets the caption of the image + pub const fn with_caption(self, caption: Option<&'static str>) -> Self { + Self { caption, ..self } + } +} + +impl std::ops::Deref for ImageAsset { + type Target = Asset; + + fn deref(&self) -> &Self::Target { + &self.asset + } +} + +impl std::fmt::Display for ImageAsset { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.asset.fmt(f) + } +} + +/// The type of an image. You can read more about the tradeoffs between image formats [here](https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types) +#[derive(Debug, PartialEq, PartialOrd, Clone, Copy, Hash)] +pub enum ImageType { + /// A png image. Png images cannot contain transparency and tend to compress worse than other formats + Png, + /// A jpg image. Jpg images can contain transparency and tend to compress better than png images + Jpg, + /// A webp image. Webp images can contain transparency and tend to compress better than jpg images + Webp, + /// An avif image. Avif images can compress slightly better than webp images but are not supported by all browsers + Avif, +} + +/// A builder for an image asset. This must be used in the [`asset!`] macro. +/// +/// > **Note**: This will do nothing outside of the `asset!` macro +pub struct ImageAssetBuilder; + +impl ImageAssetBuilder { + /// Sets the format of the image + /// + /// > **Note**: This will do nothing outside of the `asset!` macro + /// + /// Choosing the right format can make your site load much faster. Webp and avif images tend to be a good default for most images + /// + /// ```rust + /// const _: manganis::ImageAsset = manganis::asset!(image("https://avatars.githubusercontent.com/u/79236386?s=48&v=4").format(ImageType::Webp)); + /// ``` + #[allow(unused)] + pub const fn format(self, format: ImageType) -> Self { + Self + } + + /// Sets the size of the image + /// + /// > **Note**: This will do nothing outside of the `asset!` macro + /// + /// If you only use the image in one place, you can set the size of the image to the size it will be displayed at. This will make the image load faster + /// + /// ```rust + /// const _: manganis::ImageAsset = manganis::asset!(image("https://avatars.githubusercontent.com/u/79236386?s=48&v=4").size(512, 512)); + /// ``` + #[allow(unused)] + pub const fn size(self, x: u32, y: u32) -> Self { + Self + } + + /// Make the image use a low quality preview + /// + /// > **Note**: This will do nothing outside of the `asset!` macro + /// + /// A low quality preview is a small version of the image that will load faster. This is useful for large images on mobile devices that may take longer to load + /// + /// ```rust + /// const _: manganis::ImageAsset = manganis::asset!(image("https://avatars.githubusercontent.com/u/79236386?s=48&v=4").low_quality_preview()); + /// ``` + #[allow(unused)] + pub const fn low_quality_preview(self) -> Self { + Self + } + + /// Make the image preloaded + /// + /// > **Note**: This will do nothing outside of the `asset!` macro + /// + /// Preloading an image will make the image start to load as soon as possible. This is useful for images that will be displayed soon after the page loads or images that may not be visible immediately, but should start loading sooner + /// + /// ```rust + /// const _: manganis::ImageAsset = manganis::asset!(image("https://avatars.githubusercontent.com/u/79236386?s=48&v=4").preload()); + /// ``` + #[allow(unused)] + pub const fn preload(self) -> Self { + Self + } + + /// Make the image URL encoded + /// + /// > **Note**: This will do nothing outside of the `asset!` macro + /// + /// URL encoding an image inlines the data of the image into the URL. This is useful for small images that should load as soon as the html is parsed + /// + /// ```rust + /// const _: manganis::ImageAsset = manganis::asset!(image("https://avatars.githubusercontent.com/u/79236386?s=48&v=4").url_encoded()); + /// ``` + #[allow(unused)] + pub const fn url_encoded(self) -> Self { + Self + } +} + +/// Create an image asset from the local path or url to the image +/// +/// > **Note**: This will do nothing outside of the `asset!` macro +/// +/// You can collect images which will be automatically optimized with the image builder: +/// ```rust +/// const _: manganis::ImageAsset = manganis::asset!(image("rustacean-flat-gesture.png")); +/// ``` +/// Resize the image at compile time to make the assets file size smaller: +/// ```rust +/// const _: manganis::ImageAsset = manganis::asset!(image("rustacean-flat-gesture.png").size(52, 52)); +/// ``` +/// Or convert the image at compile time to a web friendly format: +/// ```rust +/// const _: manganis::ImageAsset = manganis::asset!(image("rustacean-flat-gesture.png").format(ImageType::Avif).size(52, 52)); +/// ``` +/// You can mark images as preloaded to make them load faster in your app +/// ```rust +/// const _: manganis::ImageAsset = manganis::asset!(image("rustacean-flat-gesture.png").preload()); +/// ``` +#[allow(unused)] +pub const fn image(path: &'static str) -> ImageAssetBuilder { + ImageAssetBuilder +} diff --git a/packages/manganis/src/jsons.rs b/packages/manganis/src/jsons.rs new file mode 100644 index 0000000000..429a0ab3f3 --- /dev/null +++ b/packages/manganis/src/jsons.rs @@ -0,0 +1,36 @@ +use crate::Asset; + +/// A builder for a json asset. This must be used in the [`asset!`] macro. +/// +/// > **Note**: This will do nothing outside of the `asset!` macro +pub struct JsonAssetBuilder; + +impl JsonAssetBuilder { + /// Make the json preloaded + /// + /// > **Note**: This will do nothing outside of the `asset!` macro + /// + /// Preloading json will make the json start to load as soon as possible. This is useful for json that will be run soon after the page loads or json that may not be used immediately, but should start loading sooner + /// + /// ```rust + /// const _: &str = manganis::asset!(json("assets/data.json").preload()); + /// ``` + #[allow(unused)] + pub const fn preload(self) -> Self { + Self + } + + /// Make the json URL encoded + /// + /// > **Note**: This will do nothing outside of the `asset!` macro + /// + /// URL encoding an image inlines the data of the json into the URL. This is useful for small json files that should load as soon as the html is parsed + /// + /// ```rust + /// const _: &str = manganis::asset!(json("assets/data.json").url_encoded()); + /// ``` + #[allow(unused)] + pub const fn url_encoded(self) -> Self { + Self + } +} diff --git a/packages/manganis/src/jss.rs b/packages/manganis/src/jss.rs new file mode 100644 index 0000000000..6b57483a56 --- /dev/null +++ b/packages/manganis/src/jss.rs @@ -0,0 +1,48 @@ +/// A builder for a javascript asset. This must be used in the [`asset!`] macro. +/// +/// > **Note**: This will do nothing outside of the `asset!` macro +pub struct JsAssetBuilder; + +impl JsAssetBuilder { + /// Sets whether the js should be minified (default: true) + /// + /// > **Note**: This will do nothing outside of the `asset!` macro + /// + /// Minifying the js can make your site load faster by loading less data + /// + /// ```rust + /// const _: &str = manganis::asset!(js("assets/script.js").minify(false)); + /// ``` + #[allow(unused)] + pub const fn minify(self, minify: bool) -> Self { + Self + } + + /// Make the js preloaded + /// + /// > **Note**: This will do nothing outside of the `asset!` macro + /// + /// Preloading js will make the js start to load as soon as possible. This is useful for js that will be run soon after the page loads or js that may not be used immediately, but should start loading sooner + /// + /// ```rust + /// const _: &str = manganis::asset!(js("assets/script.js").preload()); + /// ``` + #[allow(unused)] + pub const fn preload(self) -> Self { + Self + } + + /// Make the js URL encoded + /// + /// > **Note**: This will do nothing outside of the `asset!` macro + /// + /// URL encoding an image inlines the data of the js into the URL. This is useful for small js files that should load as soon as the html is parsed + /// + /// ```rust + /// const _: &str = manganis::asset!(js("assets/script.js").url_encoded()); + /// ``` + #[allow(unused)] + pub const fn url_encoded(self) -> Self { + Self + } +} diff --git a/packages/manganis/src/lib.rs b/packages/manganis/src/lib.rs new file mode 100644 index 0000000000..f0c056104c --- /dev/null +++ b/packages/manganis/src/lib.rs @@ -0,0 +1,56 @@ +#![doc = include_str!("../../../README.md")] +#![deny(missing_docs)] + +#[cfg(feature = "macro")] +pub use manganis_macro::*; + +// mod asset; +// pub use asset::*; + +// mod csss; +// pub use csss::*; + +// mod files; +// pub use files::*; + +// mod folder; +// pub use folder::*; + +// mod fonts; +// pub use fonts::*; + +// mod images; +// pub use images::*; + +// mod jsons; +// pub use jsons::*; + +// mod jss; +// pub use jss::*; + +mod builder; +pub use builder::*; + +/// A trait for something that can be used in the `asset!` macro +/// +/// > **Note**: These types will do nothing outside of the `asset!` macro +pub trait ForMgMacro: __private::Sealed + Sync + Send {} + +mod __private { + use super::*; + + pub trait Sealed {} + + // impl Sealed for FolderAssetBuilder {} + // impl Sealed for FontAssetBuilder {} + // impl Sealed for ImageAssetBuilder {} + // impl Sealed for JsAssetBuilder {} + // impl Sealed for JsonAssetBuilder {} + // impl Sealed for CssAssetBuilder {} + impl Sealed for &'static str {} +} + +// impl ForMgMacro for FolderAssetBuilder {} +// impl ForMgMacro for ImageAssetBuilder {} +// impl ForMgMacro for FontAssetBuilder {} +// impl ForMgMacro for &'static str {} diff --git a/packages/manganis/src/video.rs b/packages/manganis/src/video.rs new file mode 100644 index 0000000000..0f7bbcf538 --- /dev/null +++ b/packages/manganis/src/video.rs @@ -0,0 +1,16 @@ +/// Create a video asset from the local path or url to the video +/// +/// > **Note**: This will do nothing outside of the `mg!` macro +/// +/// The video builder collects an arbitrary file. Relative paths are resolved relative to the package root +/// ```rust +/// const _: &str = manganis::mg!(video("/assets/video.mp4")); +/// ``` +/// Or you can use URLs to read the asset at build time from a remote location +/// ```rust +/// const _: &str = manganis::mg!(video("https://private-user-images.githubusercontent.com/66571940/355646745-10781eef-de07-491d-aaa3-f75949b32190.mov?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjMxMzI5NTcsIm5iZiI6MTcyMzEzMjY1NywicGF0aCI6Ii82NjU3MTk0MC8zNTU2NDY3NDUtMTA3ODFlZWYtZGUwNy00OTFkLWFhYTMtZjc1OTQ5YjMyMTkwLm1vdj9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA4MDglMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwODA4VDE1NTczN1omWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTVkODEwZjI4ODE2ZmM4MjE3MWQ2ZDk3MjQ0NjQxYmZlMDI2OTAyMzhjNGU4MzlkYTdmZWM1MjI4ZWQ5NDg3M2QmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.jlX5E6WGjZeqZind6UCRLFrJ9NHcsV8xXy-Ls30tKPQ")); +/// ``` +#[allow(unused)] +pub const fn video(path: &'static str) -> &'static str { + path +} From 547c860746db263780ef2fda834e07372afdcad7 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Sat, 17 Aug 2024 00:44:59 -0700 Subject: [PATCH 016/139] temp no basepath --- packages/cli-config/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/cli-config/src/lib.rs b/packages/cli-config/src/lib.rs index 1578b88e89..52dda9e531 100644 --- a/packages/cli-config/src/lib.rs +++ b/packages/cli-config/src/lib.rs @@ -75,5 +75,6 @@ pub static CURRENT_CONFIG: once_cell::sync::Lazy< /// /// This is typically the offset of the asset from its domain pub fn base_path() -> Option<&'static str> { - todo!() + // todo!() + None } From e664a38cef2f22f48126520c5aa879bb9a53c94c Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Sat, 17 Aug 2024 00:50:30 -0700 Subject: [PATCH 017/139] display impl for attrvalue --- packages/core/src/nodes.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index dadc10105e..ee809ee4e2 100644 --- a/packages/core/src/nodes.rs +++ b/packages/core/src/nodes.rs @@ -1081,16 +1081,14 @@ impl IntoAttributeValue for Option { #[cfg(feature = "manganis")] impl IntoAttributeValue for manganis::ImageAsset { fn into_value(self) -> AttributeValue { - todo!() - // AttributeValue::Text(self.path().to_string()) + AttributeValue::Text(self.to_string()) } } #[cfg(feature = "manganis")] impl IntoAttributeValue for manganis::Asset { fn into_value(self) -> AttributeValue { - todo!() - // AttributeValue::Text(self.path().to_string()) + AttributeValue::Text(self.to_string()) } } From d2ea92d08d80bd74cb6197e6fe5c9a56f78fb4bc Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Sat, 17 Aug 2024 01:01:57 -0700 Subject: [PATCH 018/139] dont panic on collect failure --- packages/cli/src/assets.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/assets.rs b/packages/cli/src/assets.rs index 39555bb3b5..076c0443cb 100644 --- a/packages/cli/src/assets.rs +++ b/packages/cli/src/assets.rs @@ -21,9 +21,9 @@ pub const MG_JSON_OUT: &str = "mg-out"; pub fn asset_manifest(build: &BuildRequest) -> AssetManifest { let file_path = build.target_out_dir().join(MG_JSON_OUT); - let read = fs::read_to_string(&file_path).unwrap(); + let read = fs::read_to_string(&file_path).unwrap_or_default(); _ = fs::remove_file(file_path); - let json: Vec = serde_json::from_str(&read).unwrap(); + let json: Vec = serde_json::from_str(&read).unwrap_or_default(); AssetManifest::load(json) } From b6a15274bd36cea01e70db52bee2b3e913471a5d Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Sat, 17 Aug 2024 12:44:53 -0700 Subject: [PATCH 019/139] cut down web deps to 150 --- Cargo.lock | 6 -- packages/cli/Cargo.toml | 2 +- packages/manganis-macro/Cargo.toml | 8 +- packages/manganis-macro/src/asset.rs | 108 ++++++++++++++--------- packages/manganis-macro/src/lib.rs | 126 +++++++++++++++++++++------ packages/manganis/Cargo.toml | 14 +-- packages/rsx/Cargo.toml | 2 +- packages/web/src/hot_reload.rs | 2 +- 8 files changed, 180 insertions(+), 88 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5ef4594401..5e99718716 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6342,11 +6342,6 @@ dependencies = [ name = "manganis" version = "0.6.0-alpha.2" dependencies = [ - "core-foundation 0.9.4", - "dirs", - "dunce", - "infer 0.16.0", - "manganis-common", "manganis-macro", "once_cell", ] @@ -6395,7 +6390,6 @@ name = "manganis-macro" version = "0.6.0-alpha.2" dependencies = [ "base64 0.22.1", - "manganis-common", "proc-macro2", "quote", "serde", diff --git a/packages/cli/Cargo.toml b/packages/cli/Cargo.toml index 11df6d7823..666633bace 100644 --- a/packages/cli/Cargo.toml +++ b/packages/cli/Cargo.toml @@ -90,7 +90,7 @@ brotli = "6.0.0" dioxus-autofmt = { workspace = true } dioxus-check = { workspace = true } rsx-rosetta = { workspace = true } -dioxus-rsx = { workspace = true, features = ["serde"]} +dioxus-rsx = { workspace = true, features = ["serde", "hot-reload"] } dioxus-html = { workspace = true, features = ["hot-reload-context"] } dioxus-core = { workspace = true, features = ["serialize"] } dioxus-hot-reload = { workspace = true, features = ["serve"] } diff --git a/packages/manganis-macro/Cargo.toml b/packages/manganis-macro/Cargo.toml index e2b001dc14..ba5e64c14a 100644 --- a/packages/manganis-macro/Cargo.toml +++ b/packages/manganis-macro/Cargo.toml @@ -19,16 +19,14 @@ syn = { version = "2.0", features = ["full", "extra-traits"] } serde_json = "1.0" base64 = { workspace = true, optional = true } serde = { workspace = true, features = ["derive"] } -manganis-common = { workspace = true } +# manganis-common = { workspace = true } # manganis-cli-support = { workspace = true, optional = true } - # manganis-common = { path = "../common", version = "0.3.0-alpha.1" } # manganis-cli-support = { path = "../cli-support", version = "0.3.0-alpha.1", optional = true } # base64 = { version = "0.21.5", optional = true } - -[build-dependencies] -manganis-common = { workspace = true } +# [build-dependencies] +# manganis-common = { workspace = true } [features] default = ["url-encoding"] diff --git a/packages/manganis-macro/src/asset.rs b/packages/manganis-macro/src/asset.rs index 4db6025a56..704a3e6758 100644 --- a/packages/manganis-macro/src/asset.rs +++ b/packages/manganis-macro/src/asset.rs @@ -1,13 +1,13 @@ use core::panic; -use manganis_common::{ - CssOptions, FileOptions, FontOptions, ImageOptions, JsOptions, JsonOptions, MetadataAsset, - ResourceAsset, TailwindAsset, UnknownFileOptions, VideoOptions, -}; +// use manganis_common::{ +// CssOptions, FileOptions, FontOptions, ImageOptions, JsOptions, JsonOptions, MetadataAsset, +// ResourceAsset, TailwindAsset, UnknownFileOptions, VideoOptions, +// }; use proc_macro::TokenStream; use proc_macro2::Ident; use proc_macro2::TokenStream as TokenStream2; use quote::{quote, quote_spanned, ToTokens}; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use std::{collections::HashMap, fs::File, sync::atomic::AtomicBool}; use std::{path::PathBuf, sync::atomic::Ordering}; use syn::{ @@ -19,7 +19,36 @@ pub struct AssetParser { option_source: TokenStream2, resource: ResourceAsset, name: Option, - parsed_options: Option, + // parsed_options: Option, +} + +#[derive(Serialize, Deserialize)] +struct ResourceAsset { + pub input: PathBuf, + pub local: Option, + pub bundled: PathBuf, +} +#[derive(Debug)] +struct AssetError {} +impl ResourceAsset { + fn parse_any(input: &str) -> Result { + let input = PathBuf::from(input); + let local = Some(input.clone()); + let bundled = input.clone(); + // let local = match input.canonicalized().is_file() { + // true => Some(input.canonicalized()), + // false => None, + // }; + // let bundled = match input.canonicalized().is_file() { + // true => input.canonicalized(), + // false => input, + // }; + Ok(Self { + input, + local, + bundled, + }) + } } impl Parse for AssetParser { @@ -80,13 +109,13 @@ impl Parse for AssetParser { } } - let parsed_options = MethodCalls::new(options); + // let parsed_options = MethodCalls::new(options); Ok(Self { option_source, resource, name, - parsed_options, + // parsed_options,a }) } } @@ -96,12 +125,12 @@ impl ToTokens for AssetParser { let option_source = &self.option_source; let asset = &self.resource; let link_section = crate::generate_link_section(&asset); - let input = asset.input.to_string(); - let bundled = asset.bundled.to_string(); + let input = asset.input.display().to_string(); + let bundled = asset.bundled.display().to_string(); let local = match asset.local.as_ref() { Some(local) => { - let local = local.to_string(); + let local = local.display().to_string(); quote! { #local } } None => { @@ -148,32 +177,33 @@ struct MethodCallOption { } impl MethodCalls { - fn new(args: Vec) -> Option { - let asset_type = args.first()?.method.to_string(); - - let stack = args - .into_iter() - .skip(1) - .map(|x| (x.method.to_string(), x.args.into_iter().collect::>())) - .collect::>>(); - - let opts = match asset_type.as_str() { - "image" => { - let mut opts = ImageOptions::new(manganis_common::ImageType::Avif, Some((32, 32))); - // opts.set_preload(preload); - // opts.set_url_encoded(url_encoded); - // opts.set_low_quality_preview(low_quality_preview); - FileOptions::Image(opts) - } - - "video" => FileOptions::Video(VideoOptions::new(todo!())), - "font" => FileOptions::Font(FontOptions::new(todo!())), - "css" => FileOptions::Css(CssOptions::new()), - "js" => FileOptions::Js(JsOptions::new(todo!())), - "json" => FileOptions::Json(JsonOptions::new()), - other => FileOptions::Other(UnknownFileOptions::new(todo!())), - }; - - Some(opts) - } + // fn new(args: Vec) -> Option { + // let asset_type = args.first()?.method.to_string(); + + // let stack = args + // .into_iter() + // .skip(1) + // .map(|x| (x.method.to_string(), x.args.into_iter().collect::>())) + // .collect::>>(); + + // let opts = match asset_type.as_str() { + // "image" => { + // let mut opts = ImageOptions::new(manganis_common::ImageType::Avif, Some((32, 32))); + // // opts.set_preload(preload); + // // opts.set_url_encoded(url_encoded); + // // opts.set_low_quality_preview(low_quality_preview); + // FileOptions::Image(opts) + // } + + // "video" => FileOptions::Video(VideoOptions::new(todo!())), + // "font" => FileOptions::Font(FontOptions::new(todo!())), + // "css" => FileOptions::Css(CssOptions::new()), + // "js" => FileOptions::Js(JsOptions::new(todo!())), + // "json" => FileOptions::Json(JsonOptions::new()), + // other => FileOptions::Other(UnknownFileOptions::new(todo!())), + // }; + + // Some(opts) + // None + // } } diff --git a/packages/manganis-macro/src/lib.rs b/packages/manganis-macro/src/lib.rs index 0c265befec..cb442c41ae 100644 --- a/packages/manganis-macro/src/lib.rs +++ b/packages/manganis-macro/src/lib.rs @@ -9,7 +9,7 @@ // use js::JsAssetParser; // use json::JsonAssetParser; // use manganis_common::cache::macro_log_file; -use manganis_common::{MetadataAsset, ResourceAsset, TailwindAsset}; +// use manganis_common::{MetadataAsset, ResourceAsset, TailwindAsset}; use proc_macro::TokenStream; use proc_macro2::Ident; use proc_macro2::TokenStream as TokenStream2; @@ -61,10 +61,7 @@ fn generate_link_section(asset: &impl Serialize) -> TokenStream2 { let asset_bytes = syn::LitByteStr::new(asset_description.as_bytes(), position); - let section_name = syn::LitStr::new( - manganis_common::linker::LinkSection::CURRENT.link_section, - position, - ); + let section_name = syn::LitStr::new(LinkSection::CURRENT.link_section, position); quote! { #[link_section = #section_name] @@ -82,22 +79,23 @@ fn generate_link_section(asset: &impl Serialize) -> TokenStream2 { /// ``` #[proc_macro] pub fn classes(input: TokenStream) -> TokenStream { - trace_to_file(); + todo!() + // trace_to_file(); - let input_as_str = parse_macro_input!(input as LitStr); - let input_as_str = input_as_str.value(); + // let input_as_str = parse_macro_input!(input as LitStr); + // let input_as_str = input_as_str.value(); - let asset = TailwindAsset::new(&input_as_str); - let link_section = generate_link_section(&asset); + // let asset = TailwindAsset::new(&input_as_str); + // let link_section = generate_link_section(&asset); - quote! { - { - #link_section - #input_as_str - } - } - .into_token_stream() - .into() + // quote! { + // { + // #link_section + // #input_as_str + // } + // } + // .into_token_stream() + // .into() } /// The mg macro collects assets that will be included in the final binary @@ -180,17 +178,19 @@ pub fn meta(input: TokenStream) -> TokenStream { trace_to_file(); - let md = parse_macro_input!(input as MetadataValue); - let asset = MetadataAsset::new(md.key.as_str(), md.value.as_str()); - let link_section = generate_link_section(&asset); + todo!() - quote! { - { - #link_section - } - } - .into_token_stream() - .into() + // let md = parse_macro_input!(input as MetadataValue); + // let asset = MetadataAsset::new(md.key.as_str(), md.value.as_str()); + // let link_section = generate_link_section(&asset); + + // quote! { + // { + // #link_section + // } + // } + // .into_token_stream() + // .into() } // #[cfg(feature = "url-encoding")] @@ -240,3 +240,73 @@ pub(crate) fn verify_preload_valid(ident: &Ident) -> Result<(), syn::Error> { Ok(()) } + +/// Information about the manganis link section for a given platform +#[derive(Debug, Clone, Copy)] +struct LinkSection { + /// The link section we pass to the static + pub link_section: &'static str, + /// The name of the section we find in the binary + pub name: &'static str, +} + +impl LinkSection { + /// The list of link sections for all supported platforms + pub const ALL: &'static [&'static LinkSection] = + &[Self::WASM, Self::MACOS, Self::WINDOWS, Self::ILLUMOS]; + + /// Returns the link section used in linux, android, fuchsia, psp, freebsd, and wasm32 + pub const WASM: &'static LinkSection = &LinkSection { + link_section: "manganis", + name: "manganis", + }; + + /// Returns the link section used in macOS, iOS, tvOS + pub const MACOS: &'static LinkSection = &LinkSection { + link_section: "__DATA,manganis,regular,no_dead_strip", + name: "manganis", + }; + + /// Returns the link section used in windows + pub const WINDOWS: &'static LinkSection = &LinkSection { + link_section: "mg", + name: "mg", + }; + + /// Returns the link section used in illumos + pub const ILLUMOS: &'static LinkSection = &LinkSection { + link_section: "set_manganis", + name: "set_manganis", + }; + + /// The link section used on the current platform + pub const CURRENT: &'static LinkSection = { + #[cfg(any( + target_os = "none", + target_os = "linux", + target_os = "android", + target_os = "fuchsia", + target_os = "psp", + target_os = "freebsd", + target_arch = "wasm32" + ))] + { + Self::WASM + } + + #[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos"))] + { + Self::MACOS + } + + #[cfg(target_os = "windows")] + { + Self::WINDOWS + } + + #[cfg(target_os = "illumos")] + { + Self::ILLUMOS + } + }; +} diff --git a/packages/manganis/Cargo.toml b/packages/manganis/Cargo.toml index a80a158aec..02d85c11e3 100644 --- a/packages/manganis/Cargo.toml +++ b/packages/manganis/Cargo.toml @@ -15,15 +15,15 @@ keywords = ["assets"] [dependencies] manganis-macro = { workspace = true, optional = true } once_cell = "1.19.0" -dirs = "5.0.1" -infer = { workspace = true } -manganis-common = { workspace = true } -dunce = "1.0.2" +# dirs = "5.0.1" +# infer = { workspace = true } +# manganis-common = { workspace = true } +# dunce = "1.0.2" -[target.'cfg(target_os = "macos")'.dependencies] -core-foundation = "0.9.3" +# [target.'cfg(target_os = "macos")'.dependencies] +# core-foundation = "0.9.3" -[target.'cfg(target_os ="macos")'.dependencies] +# [target.'cfg(target_os ="macos")'.dependencies] [features] diff --git a/packages/rsx/Cargo.toml b/packages/rsx/Cargo.toml index 6d7430aed4..ec9a85b54f 100644 --- a/packages/rsx/Cargo.toml +++ b/packages/rsx/Cargo.toml @@ -23,7 +23,7 @@ tracing = { workspace = true } proc-macro2-diagnostics = { version = "0.10", default-features = false } [features] -default = ["hot_reload"] +default = [] hot_reload_traits = [] hot_reload = ["dep:internment", "dep:dioxus-core", "hot_reload_traits", "serde"] serde = ["dep:serde", "dioxus-core/serialize"] diff --git a/packages/web/src/hot_reload.rs b/packages/web/src/hot_reload.rs index a437d336dc..b099c72e38 100644 --- a/packages/web/src/hot_reload.rs +++ b/packages/web/src/hot_reload.rs @@ -75,7 +75,7 @@ fn make_ws(tx: UnboundedSender, poll_interval: i32, reload: bool) "Your app is being rebuilt.", "A non-hot-reloadable change occurred and we must rebuild.", ToastLevel::Info, - TOAST_TIMEOUT, + Duration::from_secs(600), false, ), // The devserver is telling us that the full rebuild failed. From 34082dc5e84df572402f44a848fbb3f26e51a718 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Sun, 18 Aug 2024 15:03:40 -0700 Subject: [PATCH 020/139] clean up deps in a lot of places, simplify build scripts --- Cargo.lock | 57 +-- Cargo.toml | 3 + packages/cli-config/Cargo.toml | 12 +- packages/cli-config/build.rs | 4 - packages/cli-config/src/build_info.rs | 2 - packages/cli-config/src/lib.rs | 3 - packages/cli/Cargo.toml | 2 +- packages/core-macro/Cargo.toml | 1 - packages/core-macro/src/utils.rs | 18 +- packages/core-types/Cargo.toml | 6 + .../script.js => core-types/src/core.rs} | 0 packages/core-types/src/hotreload.rs | 480 ++++++++++++++++++ packages/core-types/src/lib.rs | 2 + packages/desktop/Cargo.toml | 17 +- packages/desktop/src/app.rs | 20 +- packages/desktop/src/bindings.rs | 1 + packages/desktop/src/config.rs | 17 +- packages/desktop/src/file_upload.rs | 56 +- packages/desktop/src/lib.rs | 1 + packages/desktop/src/protocol.rs | 10 +- packages/desktop/src/webview.rs | 2 +- packages/hot-reload/Cargo.toml | 32 +- packages/hot-reload/src/lib.rs | 14 - packages/hot-reload/src/ws_receiver.rs | 116 ++--- packages/html/Cargo.toml | 57 +-- packages/html/assets/style.css | 0 packages/html/src/document/bindings.rs | 63 +-- packages/html/src/document/mod.rs | 84 +-- packages/html/src/lib.rs | 11 +- packages/html/src/native_bind/mod.rs | 3 - .../src/native_bind/native_file_engine.rs | 56 -- packages/rsx/Cargo.toml | 11 +- packages/server-macro/Cargo.toml | 1 - packages/web/Cargo.toml | 38 +- packages/web/src/bindings.rs | 12 + packages/web/src/document.rs | 6 +- packages/web/src/lib.rs | 1 + .../{html => web}/src/web_sys_bind/events.rs | 0 .../src/web_sys_bind/file_engine.rs | 0 .../{html => web}/src/web_sys_bind/mod.rs | 0 40 files changed, 818 insertions(+), 401 deletions(-) delete mode 100644 packages/cli-config/build.rs delete mode 100644 packages/cli-config/src/build_info.rs create mode 100644 packages/core-types/Cargo.toml rename packages/{html/assets/script.js => core-types/src/core.rs} (100%) create mode 100644 packages/core-types/src/hotreload.rs create mode 100644 packages/core-types/src/lib.rs create mode 100644 packages/desktop/src/bindings.rs delete mode 100644 packages/html/assets/style.css delete mode 100644 packages/html/src/native_bind/mod.rs delete mode 100644 packages/html/src/native_bind/native_file_engine.rs create mode 100644 packages/web/src/bindings.rs rename packages/{html => web}/src/web_sys_bind/events.rs (100%) rename packages/{html => web}/src/web_sys_bind/file_engine.rs (100%) rename packages/{html => web}/src/web_sys_bind/mod.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 5e99718716..6bd99d9394 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -694,7 +694,7 @@ dependencies = [ "sha1", "sync_wrapper 1.0.1", "tokio", - "tokio-tungstenite 0.21.0", + "tokio-tungstenite", "tower", "tower-layer", "tower-service", @@ -2623,7 +2623,6 @@ dependencies = [ name = "dioxus-cli-config" version = "0.6.0-alpha.2" dependencies = [ - "built", "cargo-config2", "cargo_toml", "clap", @@ -2682,7 +2681,6 @@ dependencies = [ "dioxus", "dioxus-html", "dioxus-rsx", - "prettyplease", "proc-macro2", "quote", "rustversion", @@ -2691,6 +2689,10 @@ dependencies = [ "trybuild", ] +[[package]] +name = "dioxus-core-types" +version = "0.6.0-alpha.2" + [[package]] name = "dioxus-desktop" version = "0.6.0-alpha.2" @@ -2699,7 +2701,6 @@ dependencies = [ "cocoa", "core-foundation 0.10.0", "dioxus", - "dioxus-cli-config", "dioxus-core", "dioxus-hooks", "dioxus-hot-reload", @@ -2730,7 +2731,6 @@ dependencies = [ "tao", "thiserror", "tokio", - "tokio-tungstenite 0.23.1", "tracing", "urlencoding", "webbrowser", @@ -2838,22 +2838,15 @@ dependencies = [ name = "dioxus-hot-reload" version = "0.6.0-alpha.2" dependencies = [ - "chrono", "dioxus-cli-config", "dioxus-core", "dioxus-html", "dioxus-rsx", "dioxus-signals", - "execute", "futures-util", - "ignore", - "notify", - "once_cell", "serde", "serde_json", "tokio", - "tokio-stream", - "tokio-tungstenite 0.23.1", "tracing", "warnings", ] @@ -2878,7 +2871,6 @@ dependencies = [ "keyboard-types", "lazy-js-bundle", "manganis", - "rustversion", "serde", "serde_json", "serde_repr", @@ -2886,7 +2878,6 @@ dependencies = [ "tracing", "wasm-bindgen", "wasm-bindgen-futures", - "web-sys", ] [[package]] @@ -3171,6 +3162,7 @@ dependencies = [ "gloo-dialogs", "gloo-timers 0.3.0", "js-sys", + "lazy-js-bundle", "rustc-hash 1.1.0", "serde", "serde-wasm-bindgen", @@ -3187,7 +3179,6 @@ dependencies = [ name = "dioxus_server_macro" version = "0.6.0-alpha.2" dependencies = [ - "convert_case 0.6.0", "proc-macro2", "quote", "server_fn_macro", @@ -11517,22 +11508,7 @@ dependencies = [ "futures-util", "log", "tokio", - "tungstenite 0.21.0", -] - -[[package]] -name = "tokio-tungstenite" -version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6989540ced10490aaf14e6bad2e3d33728a2813310a0c71d1574304c49631cd" -dependencies = [ - "futures-util", - "log", - "native-tls", - "rustls 0.23.12", - "tokio", - "tokio-native-tls", - "tungstenite 0.23.0", + "tungstenite", ] [[package]] @@ -11852,25 +11828,6 @@ dependencies = [ "utf-8", ] -[[package]] -name = "tungstenite" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e2e2ce1e47ed2994fd43b04c8f618008d4cabdd5ee34027cf14f9d918edd9c8" -dependencies = [ - "byteorder", - "bytes", - "data-encoding", - "http 1.1.0", - "httparse", - "log", - "native-tls", - "rand 0.8.5", - "sha1", - "thiserror", - "utf-8", -] - [[package]] name = "twofish" version = "0.7.1" diff --git a/Cargo.toml b/Cargo.toml index 89b5388bc9..3a69921a38 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ members = [ "packages/core", "packages/cli", "packages/cli-config", + "packages/core-types", "packages/core-macro", "packages/config-macro", "packages/router-macro", @@ -169,6 +170,8 @@ convert_case = "0.6.0" tokio-tungstenite = { version = "0.23.1" } gloo-timers = "0.3.0" fluent-uri = { version = "0.2.0", features = ["serde"] } +internment = { version = "0.7.0" } +proc-macro2-diagnostics = { version = "0.10", default-features = false } # desktop wry = { version = "0.42.0", default-features = false } diff --git a/packages/cli-config/Cargo.toml b/packages/cli-config/Cargo.toml index ccc0afcbdf..ae2caa4573 100644 --- a/packages/cli-config/Cargo.toml +++ b/packages/cli-config/Cargo.toml @@ -24,12 +24,18 @@ tauri-utils = { workspace = true, optional = true } dirs = { workspace = true, optional = true } -[build-dependencies] -built = { version = "=0.7.3", features = ["git2"] } [features] default = ["read-config"] -cli = ["dep:tauri-bundler", "dep:tauri-utils", "read-from-args", "dep:toml", "dep:cargo_toml", "dep:dirs", "dep:cargo-config2"] +cli = [ + "dep:tauri-bundler", + "dep:tauri-utils", + "read-from-args", + "dep:toml", + "dep:cargo_toml", + "dep:dirs", + "dep:cargo-config2", +] read-config = [] read-from-args = ["dep:clap"] diff --git a/packages/cli-config/build.rs b/packages/cli-config/build.rs deleted file mode 100644 index 35a7ffaa56..0000000000 --- a/packages/cli-config/build.rs +++ /dev/null @@ -1,4 +0,0 @@ - -fn main() { - built::write_built_file().expect("Failed to acquire build-time information"); -} diff --git a/packages/cli-config/src/build_info.rs b/packages/cli-config/src/build_info.rs deleted file mode 100644 index d11fb6389f..0000000000 --- a/packages/cli-config/src/build_info.rs +++ /dev/null @@ -1,2 +0,0 @@ -// The file has been placed there by the build script. -include!(concat!(env!("OUT_DIR"), "/built.rs")); diff --git a/packages/cli-config/src/lib.rs b/packages/cli-config/src/lib.rs index 52dda9e531..9fc704c3c5 100644 --- a/packages/cli-config/src/lib.rs +++ b/packages/cli-config/src/lib.rs @@ -10,9 +10,6 @@ pub use bundle::*; mod serve; pub use serve::*; - -mod build_info; - /// An error that occurs when the dioxus CLI was not used to build the application. #[derive(Debug)] pub struct DioxusCLINotUsed; diff --git a/packages/cli/Cargo.toml b/packages/cli/Cargo.toml index 666633bace..a0f0e6dded 100644 --- a/packages/cli/Cargo.toml +++ b/packages/cli/Cargo.toml @@ -90,7 +90,7 @@ brotli = "6.0.0" dioxus-autofmt = { workspace = true } dioxus-check = { workspace = true } rsx-rosetta = { workspace = true } -dioxus-rsx = { workspace = true, features = ["serde", "hot-reload"] } +dioxus-rsx = { workspace = true, features = ["serde", "hot_reload"] } dioxus-html = { workspace = true, features = ["hot-reload-context"] } dioxus-core = { workspace = true, features = ["serialize"] } dioxus-hot-reload = { workspace = true, features = ["serve"] } diff --git a/packages/core-macro/Cargo.toml b/packages/core-macro/Cargo.toml index 266198368b..9d35ac3ab4 100644 --- a/packages/core-macro/Cargo.toml +++ b/packages/core-macro/Cargo.toml @@ -18,7 +18,6 @@ quote = { workspace = true } syn = { workspace = true, features = ["full", "extra-traits", "visit"] } dioxus-rsx = { workspace = true } convert_case = { workspace = true } -prettyplease = "0.2.15" # testing [dev-dependencies] diff --git a/packages/core-macro/src/utils.rs b/packages/core-macro/src/utils.rs index 918c8d2935..0480d147e6 100644 --- a/packages/core-macro/src/utils.rs +++ b/packages/core-macro/src/utils.rs @@ -3,8 +3,6 @@ use syn::parse::{Parse, ParseStream}; use syn::spanned::Spanned; use syn::{parse_quote, Expr, Lit, Meta, Token, Type}; -const FORMATTED_TYPE_START: &str = "static TY_AFTER_HERE:"; -const FORMATTED_TYPE_END: &str = "= unreachable!();"; /// Attempts to convert the given literal to a string. /// Converts ints and floats to their base 10 counterparts. @@ -29,20 +27,8 @@ pub fn format_type_string(ty: &Type) -> String { let ty_unformatted = ty.into_token_stream().to_string(); let ty_unformatted = ty_unformatted.trim(); - // This should always be valid syntax. - // Not Rust code, but syntax, which is the only thing that `syn` cares about. - let Ok(file_unformatted) = syn::parse_file(&format!( - "{FORMATTED_TYPE_START}{ty_unformatted}{FORMATTED_TYPE_END}" - )) else { - return ty_unformatted.to_string(); - }; - - let file_formatted = prettyplease::unparse(&file_unformatted); - - let file_trimmed = file_formatted.trim(); - let start_removed = file_trimmed.trim_start_matches(FORMATTED_TYPE_START); - let end_removed = start_removed.trim_end_matches(FORMATTED_TYPE_END); - let ty_formatted = end_removed.trim(); + // simply remove all whitespace + let ty_formatted = ty_unformatted.replace(" ", ""); ty_formatted.to_string() } diff --git a/packages/core-types/Cargo.toml b/packages/core-types/Cargo.toml new file mode 100644 index 0000000000..688d053aa8 --- /dev/null +++ b/packages/core-types/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "dioxus-core-types" +edition = "2021" +version.workspace = true + +[dependencies] diff --git a/packages/html/assets/script.js b/packages/core-types/src/core.rs similarity index 100% rename from packages/html/assets/script.js rename to packages/core-types/src/core.rs diff --git a/packages/core-types/src/hotreload.rs b/packages/core-types/src/hotreload.rs new file mode 100644 index 0000000000..098328f7fc --- /dev/null +++ b/packages/core-types/src/hotreload.rs @@ -0,0 +1,480 @@ +// use std::{ +// any::{Any, TypeId}, +// hash::{Hash, Hasher}, +// }; + +// #[cfg(feature = "serialize")] +// use crate::nodes::deserialize_string_leaky; +// // use crate::{ +// // Attribute, AttributeValue, DynamicNode, Template, TemplateAttribute, TemplateNode, VNode, VText, +// // }; + +// #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] +// #[doc(hidden)] +// #[derive(Debug, PartialEq, Clone)] +// pub struct HotreloadedLiteral { +// pub name: String, +// pub value: HotReloadLiteral, +// } + +// #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] +// #[doc(hidden)] +// #[derive(Debug, PartialEq, Clone)] +// pub enum HotReloadLiteral { +// Fmted(FmtedSegments), +// Float(f64), +// Int(i64), +// Bool(bool), +// } + +// impl HotReloadLiteral { +// pub fn as_fmted(&self) -> Option<&FmtedSegments> { +// match self { +// Self::Fmted(segments) => Some(segments), +// _ => None, +// } +// } + +// pub fn as_float(&self) -> Option { +// match self { +// Self::Float(f) => Some(*f), +// _ => None, +// } +// } + +// pub fn as_int(&self) -> Option { +// match self { +// Self::Int(i) => Some(*i), +// _ => None, +// } +// } + +// pub fn as_bool(&self) -> Option { +// match self { +// Self::Bool(b) => Some(*b), +// _ => None, +// } +// } +// } + +// impl Hash for HotReloadLiteral { +// fn hash(&self, state: &mut H) { +// match self { +// Self::Fmted(segments) => segments.hash(state), +// Self::Float(f) => f.to_bits().hash(state), +// Self::Int(i) => i.hash(state), +// Self::Bool(b) => b.hash(state), +// } +// } +// } + +// #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] +// #[doc(hidden)] +// #[derive(Debug, PartialEq, Eq, Clone, Hash)] +// pub struct FmtedSegments { +// pub(crate) segments: Vec, +// } + +// impl FmtedSegments { +// pub fn new(segments: Vec) -> Self { +// Self { segments } +// } + +// /// Render the formatted string by stitching together the segments +// pub(crate) fn render_with(&self, dynamic_text: &[String]) -> String { +// let mut out = String::new(); + +// for segment in &self.segments { +// match segment { +// FmtSegment::Literal { value } => out.push_str(value), +// FmtSegment::Dynamic { id } => out.push_str(&dynamic_text[*id]), +// } +// } + +// out +// } +// } + +// type StaticStr = &'static str; + +// #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] +// #[doc(hidden)] +// #[derive(Debug, PartialEq, Eq, Clone, Hash)] +// pub enum FmtSegment { +// Literal { +// #[cfg_attr( +// feature = "serialize", +// serde(deserialize_with = "deserialize_string_leaky") +// )] +// value: StaticStr, +// }, +// Dynamic { +// id: usize, +// }, +// } + +// // let __pool = DynamicValuePool::new( +// // vec![...], +// // vec![...], +// // vec![...], +// // ); +// // VNode::new( +// // None, +// // Template { +// // name: "...", +// // roots: &[...], +// // node_paths: &[..], +// // attr_paths: &[...], +// // }, +// // Box::new([...]), +// // Box::new([...]), +// // ) + +// // Open questions: +// // - How do we handle type coercion for different sized component property integers? +// // - Should non-string hot literals go through the centralized pool? +// // - Should formatted strings be a runtime concept? + +// #[doc(hidden)] +// pub struct DynamicLiteralPool { +// dynamic_text: Box<[String]>, +// } + +// impl DynamicLiteralPool { +// pub fn new(dynamic_text: Vec) -> Self { +// Self { +// dynamic_text: dynamic_text.into_boxed_slice(), +// } +// } + +// pub fn get_component_property<'a, T>( +// &self, +// id: usize, +// hot_reload: &'a HotReloadedTemplate, +// f: impl FnOnce(&'a HotReloadLiteral) -> Option, +// ) -> Option { +// f(hot_reload.component_values.get(id)?) +// } + +// /// Get a component property of a specific type at the component property index +// pub fn component_property( +// &mut self, +// id: usize, +// hot_reload: &HotReloadedTemplate, +// // We pass in the original value for better type inference +// // For example, if the original literal is `0i128`, we know the output must be the type `i128` +// _coherse_type: T, +// ) -> T { +// fn assert_type(t: T) -> T2 { +// *(Box::new(t) as Box).downcast::().unwrap() +// } +// let grab_float = || { +// self.get_component_property(id, hot_reload, HotReloadLiteral::as_float).unwrap_or_else(|| { +// tracing::error!("Expected a float component property, because the type was {}. The CLI gave the hot reloading engine a type of {:?}. This is probably caused by a bug in dioxus hot reloading. Please report this issue.", std::any::type_name::(), hot_reload.component_values.get(id)); +// Default::default() + +// }) +// }; +// let grab_int = || { +// self.get_component_property(id, hot_reload, HotReloadLiteral::as_int).unwrap_or_else(|| { +// tracing::error!("Expected a integer component property, because the type was {}. The CLI gave the hot reloading engine a type of {:?}. This is probably caused by a bug in dioxus hot reloading. Please report this issue.", std::any::type_name::(), hot_reload.component_values.get(id)); +// Default::default() +// }) +// }; +// let grab_bool = || { +// self.get_component_property(id, hot_reload, HotReloadLiteral::as_bool).unwrap_or_else(|| { +// tracing::error!("Expected a bool component property, because the type was {}. The CLI gave the hot reloading engine a type of {:?}. This is probably caused by a bug in dioxus hot reloading. Please report this issue.", std::any::type_name::(), hot_reload.component_values.get(id)); +// Default::default() +// }) +// }; +// let grab_fmted = || { +// self.get_component_property(id, hot_reload, |fmted| HotReloadLiteral::as_fmted(fmted).map(|segments| self.render_formatted(segments))).unwrap_or_else(|| { +// tracing::error!("Expected a string component property, because the type was {}. The CLI gave the hot reloading engine a type of {:?}. This is probably caused by a bug in dioxus hot reloading. Please report this issue.", std::any::type_name::(), hot_reload.component_values.get(id)); +// Default::default() +// }) +// }; +// match TypeId::of::() { +// // Any string types that accept a literal +// _ if TypeId::of::() == TypeId::of::() => assert_type(grab_fmted()), +// _ if TypeId::of::<&str>() == TypeId::of::() => { +// assert_type(Box::leak(grab_fmted().into_boxed_str()) as &'static str) +// } +// // Any integer types that accept a literal +// _ if TypeId::of::() == TypeId::of::() => assert_type(grab_int() as i128), +// _ if TypeId::of::() == TypeId::of::() => assert_type(grab_int()), +// _ if TypeId::of::() == TypeId::of::() => assert_type(grab_int() as i32), +// _ if TypeId::of::() == TypeId::of::() => assert_type(grab_int() as i16), +// _ if TypeId::of::() == TypeId::of::() => assert_type(grab_int() as i8), +// _ if TypeId::of::() == TypeId::of::() => assert_type(grab_int() as isize), +// _ if TypeId::of::() == TypeId::of::() => assert_type(grab_int() as u128), +// _ if TypeId::of::() == TypeId::of::() => assert_type(grab_int() as u64), +// _ if TypeId::of::() == TypeId::of::() => assert_type(grab_int() as u32), +// _ if TypeId::of::() == TypeId::of::() => assert_type(grab_int() as u16), +// _ if TypeId::of::() == TypeId::of::() => assert_type(grab_int() as u8), +// _ if TypeId::of::() == TypeId::of::() => assert_type(grab_int() as usize), +// // Any float types that accept a literal +// _ if TypeId::of::() == TypeId::of::() => assert_type(grab_float()), +// _ if TypeId::of::() == TypeId::of::() => assert_type(grab_float() as f32), +// // Any bool types that accept a literal +// _ if TypeId::of::() == TypeId::of::() => assert_type(grab_bool()), +// _ => panic!("Unsupported component property type"), +// } +// } + +// pub fn render_formatted(&self, segments: &FmtedSegments) -> String { +// segments.render_with(&self.dynamic_text) +// } +// } +// #[doc(hidden)] +// pub struct DynamicValuePool { +// dynamic_attributes: Box<[Box<[Attribute]>]>, +// dynamic_nodes: Box<[DynamicNode]>, +// literal_pool: DynamicLiteralPool, +// } + +// impl DynamicValuePool { +// pub fn new( +// dynamic_nodes: Vec, +// dynamic_attributes: Vec>, +// literal_pool: DynamicLiteralPool, +// ) -> Self { +// Self { +// dynamic_attributes: dynamic_attributes.into_boxed_slice(), +// dynamic_nodes: dynamic_nodes.into_boxed_slice(), +// literal_pool, +// } +// } + +// pub fn render_with(&mut self, hot_reload: &HotReloadedTemplate) -> VNode { +// // Get the node_paths from a depth first traversal of the template +// let key = hot_reload +// .key +// .as_ref() +// .map(|key| self.literal_pool.render_formatted(key)); +// let dynamic_nodes = hot_reload +// .dynamic_nodes +// .iter() +// .map(|node| self.render_dynamic_node(node)) +// .collect(); +// let dynamic_attrs = hot_reload +// .dynamic_attributes +// .iter() +// .map(|attr| self.render_attribute(attr)) +// .collect(); + +// VNode::new(key, hot_reload.template, dynamic_nodes, dynamic_attrs) +// } + +// fn render_dynamic_node(&mut self, node: &HotReloadDynamicNode) -> DynamicNode { +// match node { +// // If the node is dynamic, take it from the pool and return it +// HotReloadDynamicNode::Dynamic(id) => self.dynamic_nodes[*id].clone(), +// // Otherwise, format the text node and return it +// HotReloadDynamicNode::Formatted(segments) => DynamicNode::Text(VText { +// value: self.literal_pool.render_formatted(segments), +// }), +// } +// } + +// fn render_attribute(&mut self, attr: &HotReloadDynamicAttribute) -> Box<[Attribute]> { +// match attr { +// HotReloadDynamicAttribute::Dynamic(id) => self.dynamic_attributes[*id].clone(), +// HotReloadDynamicAttribute::Named(NamedAttribute { +// name, +// namespace, +// value, +// }) => Box::new([Attribute { +// name, +// namespace: *namespace, +// value: match value { +// HotReloadAttributeValue::Literal(HotReloadLiteral::Fmted(segments)) => { +// AttributeValue::Text(self.literal_pool.render_formatted(segments)) +// } +// HotReloadAttributeValue::Literal(HotReloadLiteral::Float(f)) => { +// AttributeValue::Float(*f) +// } +// HotReloadAttributeValue::Literal(HotReloadLiteral::Int(i)) => { +// AttributeValue::Int(*i) +// } +// HotReloadAttributeValue::Literal(HotReloadLiteral::Bool(b)) => { +// AttributeValue::Bool(*b) +// } +// HotReloadAttributeValue::Dynamic(id) => { +// self.dynamic_attributes[*id][0].value.clone() +// } +// }, +// volatile: false, +// }]), +// } +// } +// } + +// #[doc(hidden)] +// #[derive(Debug, Clone, PartialEq)] +// #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] +// pub struct HotReloadTemplateWithLocation { +// pub location: String, +// pub template: HotReloadedTemplate, +// } + +// type StaticTemplateArray = &'static [TemplateNode]; + +// #[doc(hidden)] +// #[derive(Debug, PartialEq, Clone)] +// #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] +// pub struct HotReloadedTemplate { +// pub key: Option, +// pub dynamic_nodes: Vec, +// pub dynamic_attributes: Vec, +// pub component_values: Vec, +// #[cfg_attr( +// feature = "serialize", +// serde(deserialize_with = "crate::nodes::deserialize_leaky") +// )] +// pub roots: StaticTemplateArray, +// /// The template that is computed from the hot reload roots +// template: Template, +// } + +// impl HotReloadedTemplate { +// pub fn new( +// key: Option, +// dynamic_nodes: Vec, +// dynamic_attributes: Vec, +// component_values: Vec, +// roots: &'static [TemplateNode], +// ) -> Self { +// let node_paths = Self::node_paths(roots); +// let attr_paths = Self::attr_paths(roots); + +// let template = Template { +// roots, +// node_paths, +// attr_paths, +// }; +// Self { +// key, +// dynamic_nodes, +// dynamic_attributes, +// component_values, +// roots, +// template, +// } +// } + +// fn node_paths(roots: &'static [TemplateNode]) -> &'static [&'static [u8]] { +// fn add_node_paths( +// roots: &[TemplateNode], +// node_paths: &mut Vec<&'static [u8]>, +// current_path: Vec, +// ) { +// for (idx, node) in roots.iter().enumerate() { +// let mut path = current_path.clone(); +// path.push(idx as u8); +// match node { +// TemplateNode::Element { children, .. } => { +// add_node_paths(children, node_paths, path); +// } +// TemplateNode::Text { .. } => {} +// TemplateNode::Dynamic { id } => { +// debug_assert_eq!(node_paths.len(), *id); +// node_paths.push(Box::leak(path.into_boxed_slice())); +// } +// } +// } +// } + +// let mut node_paths = Vec::new(); +// add_node_paths(roots, &mut node_paths, Vec::new()); +// let leaked: &'static [&'static [u8]] = Box::leak(node_paths.into_boxed_slice()); +// leaked +// } + +// fn attr_paths(roots: &'static [TemplateNode]) -> &'static [&'static [u8]] { +// fn add_attr_paths( +// roots: &[TemplateNode], +// attr_paths: &mut Vec<&'static [u8]>, +// current_path: Vec, +// ) { +// for (idx, node) in roots.iter().enumerate() { +// let mut path = current_path.clone(); +// path.push(idx as u8); +// if let TemplateNode::Element { +// children, attrs, .. +// } = node +// { +// for attr in *attrs { +// if let TemplateAttribute::Dynamic { id } = attr { +// debug_assert_eq!(attr_paths.len(), *id); +// attr_paths.push(Box::leak(path.clone().into_boxed_slice())); +// } +// } +// add_attr_paths(children, attr_paths, path); +// } +// } +// } + +// let mut attr_paths = Vec::new(); +// add_attr_paths(roots, &mut attr_paths, Vec::new()); +// let leaked: &'static [&'static [u8]] = Box::leak(attr_paths.into_boxed_slice()); +// leaked +// } +// } + +// #[doc(hidden)] +// #[derive(Debug, PartialEq, Clone, Hash)] +// #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] +// pub enum HotReloadDynamicNode { +// Dynamic(usize), +// Formatted(FmtedSegments), +// } + +// #[doc(hidden)] +// #[derive(Debug, PartialEq, Clone, Hash)] +// #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] +// pub enum HotReloadDynamicAttribute { +// Dynamic(usize), +// Named(NamedAttribute), +// } + +// #[doc(hidden)] +// #[derive(Debug, PartialEq, Clone, Hash)] +// #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] +// pub struct NamedAttribute { +// /// The name of this attribute. +// #[cfg_attr( +// feature = "serialize", +// serde(deserialize_with = "crate::nodes::deserialize_string_leaky") +// )] +// name: StaticStr, +// /// The namespace of this attribute. Does not exist in the HTML spec +// #[cfg_attr( +// feature = "serialize", +// serde(deserialize_with = "crate::nodes::deserialize_option_leaky") +// )] +// namespace: Option, + +// value: HotReloadAttributeValue, +// } + +// impl NamedAttribute { +// pub fn new( +// name: &'static str, +// namespace: Option<&'static str>, +// value: HotReloadAttributeValue, +// ) -> Self { +// Self { +// name, +// namespace, +// value, +// } +// } +// } + +// #[doc(hidden)] +// #[derive(Debug, PartialEq, Clone, Hash)] +// #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] +// pub enum HotReloadAttributeValue { +// Literal(HotReloadLiteral), +// Dynamic(usize), +// } diff --git a/packages/core-types/src/lib.rs b/packages/core-types/src/lib.rs new file mode 100644 index 0000000000..ce99d2d0da --- /dev/null +++ b/packages/core-types/src/lib.rs @@ -0,0 +1,2 @@ +mod core; +mod hotreload; diff --git a/packages/desktop/Cargo.toml b/packages/desktop/Cargo.toml index 3c89d18996..a2434407e5 100644 --- a/packages/desktop/Cargo.toml +++ b/packages/desktop/Cargo.toml @@ -13,13 +13,13 @@ keywords = ["dom", "ui", "gui", "react"] dioxus-core = { workspace = true, features = ["serialize"] } dioxus-html = { workspace = true, features = [ "serialize", - "native-bind", "mounted", "document", + "file-engine", ] } dioxus-signals = { workspace = true, optional = true } dioxus-interpreter-js = { workspace = true, features = ["binary-protocol", "serialize"] } -dioxus-cli-config = { workspace = true, features = ["read-config"] } +# dioxus-cli-config = { workspace = true, features = ["read-config"] } generational-box = { workspace = true } dioxus-hot-reload = { workspace = true, optional = true, features = ["serve", "client"]} thiserror = { workspace = true } @@ -38,6 +38,7 @@ tokio = { workspace = true, features = [ "time", "macros", "fs", + "io-util", ], optional = true } slab = { workspace = true } rustc-hash = { workspace = true } @@ -60,13 +61,13 @@ global-hotkey = { workspace = true } rfd = { workspace = true, default-features = false, features = ["xdg-portal", "tokio"] } muda = { workspace = true } -# use rustls on android -[target.'cfg(target_os = "android")'.dependencies] -tokio-tungstenite = { workspace = true, optional = true, features = ["rustls"]} +# # use rustls on android +# [target.'cfg(target_os = "android")'.dependencies] +# tokio-tungstenite = { workspace = true, optional = true, features = ["rustls"]} -# use native tls on other platforms -[target.'cfg(not(target_os = "android"))'.dependencies] -tokio-tungstenite = { workspace = true, optional = true, features = ["native-tls"]} +# # use native tls on other platforms +# [target.'cfg(not(target_os = "android"))'.dependencies] +# tokio-tungstenite = { workspace = true, optional = true, features = ["native-tls"]} [target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies] cocoa = { workspace = true } diff --git a/packages/desktop/src/app.rs b/packages/desktop/src/app.rs index ee93a829e3..e050010f0e 100644 --- a/packages/desktop/src/app.rs +++ b/packages/desktop/src/app.rs @@ -1,14 +1,14 @@ use crate::{ config::{Config, WindowCloseBehaviour}, event_handlers::WindowEventHandlers, - file_upload::{DesktopFileUploadForm, FileDialogRequest}, + file_upload::{DesktopFileUploadForm, FileDialogRequest, NativeFileEngine}, ipc::{IpcMessage, UserWindowEvent}, query::QueryResult, shortcut::ShortcutRegistry, webview::WebviewInstance, }; use dioxus_core::{ElementId, VirtualDom}; -use dioxus_html::{native_bind::NativeFileEngine, PlatformEventData}; +use dioxus_html::PlatformEventData; use std::{ any::Any, cell::{Cell, RefCell}, @@ -140,14 +140,14 @@ impl App { let proxy = self.shared.proxy.clone(); tokio::task::spawn(async move { - let Some(Ok(mut receiver)) = dioxus_hot_reload::NativeReceiver::create_from_cli().await - else { - return; - }; - - while let Some(Ok(msg)) = receiver.next().await { - _ = proxy.send_event(UserWindowEvent::HotReloadEvent(msg)); - } + // let Some(Ok(mut receiver)) = dioxus_hot_reload::NativeReceiver::create_from_cli().await + // else { + // return; + // }; + + // while let Some(Ok(msg)) = receiver.next().await { + // _ = proxy.send_event(UserWindowEvent::HotReloadEvent(msg)); + // } }); } diff --git a/packages/desktop/src/bindings.rs b/packages/desktop/src/bindings.rs new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/packages/desktop/src/bindings.rs @@ -0,0 +1 @@ + diff --git a/packages/desktop/src/config.rs b/packages/desktop/src/config.rs index 19f9551103..8ea95c36f5 100644 --- a/packages/desktop/src/config.rs +++ b/packages/desktop/src/config.rs @@ -49,18 +49,21 @@ impl Config { /// Initializes a new `WindowBuilder` with default values. #[inline] pub fn new() -> Self { - let dioxus_config = dioxus_cli_config::CURRENT_CONFIG.as_ref(); + // let dioxus_config = dioxus_cli_config::CURRENT_CONFIG.as_ref(); let mut window: WindowBuilder = WindowBuilder::new().with_title( - dioxus_config - .map(|c| c.application.name.clone()) - .unwrap_or("Dioxus App".to_string()), + "Dioxus App".to_string(), + // dioxus_config + // .map(|c| c.application.name.clone()) + // .unwrap_or("Dioxus App".to_string()), ); // During development we want the window to be on top so we can see it while we work - let always_on_top = dioxus_config - .map(|c| c.desktop.always_on_top) - .unwrap_or(true); + // let always_on_top = dioxus_config + // // .map(|c| c.desktop.always_on_top) + // .unwrap_or(true); + let always_on_top = true; + if cfg!(debug_assertions) { window = window.with_always_on_top(always_on_top); } diff --git a/packages/desktop/src/file_upload.rs b/packages/desktop/src/file_upload.rs index dba431ef4d..5b04479207 100644 --- a/packages/desktop/src/file_upload.rs +++ b/packages/desktop/src/file_upload.rs @@ -3,7 +3,6 @@ use dioxus_html::{ geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint}, input_data::{MouseButton, MouseButtonSet}, - native_bind::NativeFileEngine, point_interaction::{ InteractionElementOffset, InteractionLocation, ModifiersInteraction, PointerInteraction, }, @@ -237,3 +236,58 @@ impl PointerInteraction for DesktopFileDragEvent { self.mouse.trigger_button() } } + +use std::any::Any; +// use std::path::PathBuf; + +// use dioxus_html::FileEngine; +use tokio::fs::File; +use tokio::io::AsyncReadExt; + +pub struct NativeFileEngine { + files: Vec, +} + +impl NativeFileEngine { + pub fn new(files: Vec) -> Self { + Self { files } + } +} + +#[async_trait::async_trait(?Send)] +impl FileEngine for NativeFileEngine { + fn files(&self) -> Vec { + self.files + .iter() + .filter_map(|f| Some(f.to_str()?.to_string())) + .collect() + } + + async fn file_size(&self, file: &str) -> Option { + let file = File::open(file).await.ok()?; + Some(file.metadata().await.ok()?.len()) + } + + async fn read_file(&self, file: &str) -> Option> { + let mut file = File::open(file).await.ok()?; + + let mut contents = Vec::new(); + file.read_to_end(&mut contents).await.ok()?; + + Some(contents) + } + + async fn read_file_to_string(&self, file: &str) -> Option { + let mut file = File::open(file).await.ok()?; + + let mut contents = String::new(); + file.read_to_string(&mut contents).await.ok()?; + + Some(contents) + } + + async fn get_native_file(&self, file: &str) -> Option> { + let file = File::open(file).await.ok()?; + Some(Box::new(file)) + } +} diff --git a/packages/desktop/src/lib.rs b/packages/desktop/src/lib.rs index f1882d50ae..87a72858a0 100644 --- a/packages/desktop/src/lib.rs +++ b/packages/desktop/src/lib.rs @@ -6,6 +6,7 @@ mod app; mod assets; +mod bindings; mod config; mod desktop_context; mod document; diff --git a/packages/desktop/src/protocol.rs b/packages/desktop/src/protocol.rs index 0749eee888..179c862f91 100644 --- a/packages/desktop/src/protocol.rs +++ b/packages/desktop/src/protocol.rs @@ -289,10 +289,12 @@ fn get_asset_root() -> Option { } } - dioxus_cli_config::CURRENT_CONFIG - .as_ref() - .map(|c| c.application.out_dir.clone()) - .ok() + None + + // dioxus_cli_config::CURRENT_CONFIG + // .as_ref() + // .map(|c| c.application.out_dir.clone()) + // .ok() } /// Get the mime type from a path-like string diff --git a/packages/desktop/src/webview.rs b/packages/desktop/src/webview.rs index 1e762ef02c..eaec2320bd 100644 --- a/packages/desktop/src/webview.rs +++ b/packages/desktop/src/webview.rs @@ -1,5 +1,6 @@ use crate::element::DesktopElement; use crate::file_upload::DesktopFileDragEvent; +use crate::file_upload::NativeFileEngine; use crate::menubar::DioxusMenu; use crate::{ app::SharedContext, assets::AssetHandlerRegistry, document::DesktopDocument, edits::WryQueue, @@ -9,7 +10,6 @@ use crate::{ use dioxus_core::{Runtime, ScopeId, VirtualDom}; use dioxus_hooks::to_owned; use dioxus_html::document::Document; -use dioxus_html::native_bind::NativeFileEngine; use dioxus_html::{HasFileData, HtmlEvent, PlatformEventData}; use dioxus_interpreter_js::SynchronousEventResponse; use futures_util::{pin_mut, FutureExt}; diff --git a/packages/hot-reload/Cargo.toml b/packages/hot-reload/Cargo.toml index 22c562158d..5ef5e6606a 100644 --- a/packages/hot-reload/Cargo.toml +++ b/packages/hot-reload/Cargo.toml @@ -16,36 +16,38 @@ dioxus-html = { workspace = true, optional = true } dioxus-signals = { workspace = true, optional = true } dioxus-cli-config = { workspace = true, optional = true, features = ["read-config"] } -notify = { workspace = true, optional = true } -chrono = { version = "0.4.24", default-features = false, features = ["clock"], optional = true } -serde_json = "1.0.91" serde = { version = "1", features = ["derive"] } -execute = { version = "0.2.11", optional = true } -once_cell = { version = "1.17.0", optional = true } -ignore = { version = "0.4.19", optional = true } # hot reloading serve -tokio-stream = { version = "0.1.12", features = ["sync"], optional = true } futures-util = { workspace = true, features = ["async-await-macro"], optional = true } tokio = { workspace = true, features = ["sync", "rt-multi-thread"], optional = true } tracing = { workspace = true } warnings.workspace = true -# use rustls on android -[target.'cfg(target_os = "android")'.dependencies] -tokio-tungstenite = { workspace = true, optional = true, features = ["rustls"] } +# tokio-stream = { version = "0.1.12", features = ["sync"], optional = true } +# notify = { workspace = true, optional = true } +# chrono = { version = "0.4.24", default-features = false, features = ["clock"], optional = true } +# execute = { version = "0.2.11", optional = true } +# once_cell = { version = "1.17.0", optional = true } +# ignore = { version = "0.4.19", optional = true } + +# # use rustls on android +# [target.'cfg(target_os = "android")'.dependencies] +# tokio-tungstenite = { workspace = true, optional = true, features = ["rustls"] } -# use native tls on other platforms -[target.'cfg(not(target_os = "android"))'.dependencies] -tokio-tungstenite = { workspace = true, optional = true, features = ["native-tls"] } +# # use native tls on other platforms +# [target.'cfg(not(target_os = "android"))'.dependencies] +# tokio-tungstenite = { workspace = true, optional = true, features = ["native-tls"] } [dev-dependencies] tokio = { workspace = true, features = ["full"] } +serde_json = "1.0.91" [features] -default = ["dioxus-html"] +default = [] client = ["dep:dioxus-signals"] -serve = ["dep:tokio-stream", "dep:futures-util", "dep:tokio", "dep:tokio-tungstenite", "dep:dioxus-cli-config"] +serve = ["dep:futures-util", "dep:tokio", "dep:dioxus-cli-config"] +# serve = ["dep:tokio-stream", "dep:futures-util", "dep:tokio", "dep:tokio-tungstenite", "dep:dioxus-cli-config"] [package.metadata.docs.rs] cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"] diff --git a/packages/hot-reload/src/lib.rs b/packages/hot-reload/src/lib.rs index 1633d3a87a..e1956a547a 100644 --- a/packages/hot-reload/src/lib.rs +++ b/packages/hot-reload/src/lib.rs @@ -53,17 +53,3 @@ pub struct HotReloadMsg { /// A file changed that's not an asset or a rust file - best of luck! pub unknown_files: Vec, } - -#[test] -fn serialize_client_msg() { - let msg = ClientMsg::Log { - level: "info".to_string(), - messages: vec!["hello world".to_string()], - }; - - let json = serde_json::to_string(&msg).unwrap(); - assert_eq!( - json, - r#"{"Log":{"level":"info","messages":["hello world"]}}"# - ); -} diff --git a/packages/hot-reload/src/ws_receiver.rs b/packages/hot-reload/src/ws_receiver.rs index dbc159760b..4023da5f74 100644 --- a/packages/hot-reload/src/ws_receiver.rs +++ b/packages/hot-reload/src/ws_receiver.rs @@ -1,22 +1,22 @@ use crate::DevserverMsg; -use futures_util::{SinkExt, StreamExt}; -use tokio::net::TcpStream; -use tokio_tungstenite::{ - tungstenite::{Message, Result as TtResult}, - MaybeTlsStream, WebSocketStream, -}; +// use futures_util::{SinkExt, StreamExt}; +// use tokio::net::TcpStream; +// use tokio_tungstenite::{ +// tungstenite::{Message, Result as TtResult}, +// MaybeTlsStream, WebSocketStream, +// }; pub fn connect(mut callback: impl FnMut(DevserverMsg) + Send + 'static) { tokio::spawn(async move { - let Some(Ok(mut recv)) = NativeReceiver::create_from_cli().await else { - return; - }; - while let Some(msg) = recv.next().await { - match msg { - Ok(msg) => callback(msg), - Err(_e) => {} - } - } + // let Some(Ok(mut recv)) = NativeReceiver::create_from_cli().await else { + // return; + // }; + // while let Some(msg) = recv.next().await { + // match msg { + // Ok(msg) => callback(msg), + // Err(_e) => {} + // } + // } }); } @@ -24,53 +24,53 @@ pub fn connect(mut callback: impl FnMut(DevserverMsg) + Send + 'static) { /// /// Calling `next` will watch the channel for the next valid message from the devserver pub struct NativeReceiver { - socket: WebSocketStream>, + // socket: WebSocketStream>, } impl NativeReceiver { - /// Connect to the devserver - async fn create(url: String) -> TtResult { - let (socket, _ws) = tokio_tungstenite::connect_async(&url).await.unwrap(); - Ok(Self { socket }) - } + // /// Connect to the devserver + // async fn create(url: String) -> TtResult { + // let (socket, _ws) = tokio_tungstenite::connect_async(&url).await.unwrap(); + // Ok(Self { socket }) + // } - /// Connect to the devserver with an address from the CLI. Returns None if the current application was not run with the CLI - pub async fn create_from_cli() -> Option> { - // todo: allow external configuration of this address for use by mobile when launching - // from the ios-deploy tooling. This could be stored in a config file= that gets - // uploaded to the device. - let addr = - dioxus_cli_config::RuntimeCLIArguments::from_cli().map(|args| args.cli_address())?; - Some(Self::create(format!("ws://{addr}/_dioxus")).await) - } + // /// Connect to the devserver with an address from the CLI. Returns None if the current application was not run with the CLI + // pub async fn create_from_cli() -> Option> { + // // todo: allow external configuration of this address for use by mobile when launching + // // from the ios-deploy tooling. This could be stored in a config file= that gets + // // uploaded to the device. + // let addr = + // dioxus_cli_config::RuntimeCLIArguments::from_cli().map(|args| args.cli_address())?; + // Some(Self::create(format!("ws://{addr}/_dioxus")).await) + // } - /// Wait for the next message from the devserver - /// - /// Returns None when the connection is closed or socket.next() returns None - pub async fn next(&mut self) -> Option> { - loop { - let res = self.socket.next().await?; + // /// Wait for the next message from the devserver + // /// + // /// Returns None when the connection is closed or socket.next() returns None + // pub async fn next(&mut self) -> Option> { + // loop { + // let res = self.socket.next().await?; - match res { - Ok(res) => match res { - Message::Text(text) => { - // let leaked: &'static str = Box::leak(text.into_boxed_str()); - let msg = serde_json::from_str::(&text); - if let Ok(msg) = msg { - return Some(Ok(msg)); - } - } - // send a pong - Message::Ping(_) => { - let _ = self.socket.send(Message::Pong(vec![])).await; - } - Message::Close(_) => return None, - Message::Binary(_) => {} - Message::Pong(_) => {} - Message::Frame(_) => {} - }, - Err(e) => return Some(Err(e)), - }; - } - } + // match res { + // Ok(res) => match res { + // Message::Text(text) => { + // // let leaked: &'static str = Box::leak(text.into_boxed_str()); + // let msg = serde_json::from_str::(&text); + // if let Ok(msg) = msg { + // return Some(Ok(msg)); + // } + // } + // // send a pong + // Message::Ping(_) => { + // let _ = self.socket.send(Message::Pong(vec![])).await; + // } + // Message::Close(_) => return None, + // Message::Binary(_) => {} + // Message::Pong(_) => {} + // Message::Frame(_) => {} + // }, + // Err(e) => return Some(Err(e)), + // }; + // } + // } } diff --git a/packages/html/Cargo.toml b/packages/html/Cargo.toml index 4b127ed659..41dfbbfd5d 100644 --- a/packages/html/Cargo.toml +++ b/packages/html/Cargo.toml @@ -22,11 +22,11 @@ euclid = "0.22.7" enumset = "1.1.2" keyboard-types = { version = "0.7", default-features = false } async-trait = { version = "0.1.58", optional = true } -tokio = { workspace = true, features = ["fs", "io-util"], optional = true } -futures-channel = { workspace = true } serde_json = { version = "1", optional = true } tracing.workspace = true -rustversion = "1.0.17" + +tokio = { workspace = true, features = ["fs", "io-util"], optional = true } +futures-channel = { workspace = true } # todo: we shouldn't have any wasm-bindgen dependencies in the html cratea # [target.'cfg(target_arch = "wasm32")'.dependencies] @@ -34,30 +34,6 @@ wasm-bindgen = { workspace = true, optional = true } js-sys = { version = "0.3.56", optional = true } wasm-bindgen-futures = { workspace = true, optional = true } -[dependencies.web-sys] -optional = true -version = "0.3.56" -features = [ - "Touch", - "TouchList", - "TouchEvent", - "MouseEvent", - "DragEvent", - "InputEvent", - "HtmlInputElement", - "ClipboardEvent", - "KeyboardEvent", - "WheelEvent", - "AnimationEvent", - "TransitionEvent", - "PointerEvent", - "FocusEvent", - "CompositionEvent", - "CustomEvent", - "ResizeObserverEntry", - "ResizeObserverSize" -] - [build-dependencies] lazy-js-bundle = { workspace = true } @@ -79,12 +55,12 @@ serialize = [ "dioxus-core/serialize" ] mounted = [ - "web-sys?/Element", - "web-sys?/DomRect", - "web-sys?/ScrollIntoViewOptions", - "web-sys?/ScrollLogicalPosition", - "web-sys?/ScrollBehavior", - "web-sys?/HtmlElement", + # "web-sys?/Element", + # "web-sys?/DomRect", + # "web-sys?/ScrollIntoViewOptions", + # "web-sys?/ScrollLogicalPosition", + # "web-sys?/ScrollBehavior", + # "web-sys?/HtmlElement", ] document = [ "dep:serde", @@ -92,16 +68,17 @@ document = [ ] file-engine = [ "dep:async-trait", - "dep:js-sys", - "web-sys?/File", - "web-sys?/FileList", - "web-sys?/FileReader" + # "dep:js-sys", + # "web-sys?/File", + # "web-sys?/FileList", + # "web-sys?/FileReader" ] -wasm-bind = ["dep:web-sys", "dep:wasm-bindgen", "dep:wasm-bindgen-futures"] -native-bind = ["dep:tokio", "file-engine"] +# wasm-bind = ["dep:web-sys", "dep:wasm-bindgen", "dep:wasm-bindgen-futures"] +# native-bind = ["dep:tokio", "file-engine"] hot-reload-context = ["dep:dioxus-rsx", "dioxus-rsx/hot_reload_traits"] html-to-rsx = [] [package.metadata.docs.rs] cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"] -feature = ["html-to-rsx", "hot-reload-context", "html-to-rsx", "native-bind", "wasm-bind"] +feature = ["html-to-rsx", "hot-reload-context", "html-to-rsx"] +# feature = ["html-to-rsx", "hot-reload-context", "html-to-rsx", "native-bind", "wasm-bind"] diff --git a/packages/html/assets/style.css b/packages/html/assets/style.css deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/packages/html/src/document/bindings.rs b/packages/html/src/document/bindings.rs index ad8ef16066..6227372358 100644 --- a/packages/html/src/document/bindings.rs +++ b/packages/html/src/document/bindings.rs @@ -1,50 +1,35 @@ -/// Code for the Dioxus channel used to communicate between the dioxus and javascript code -#[cfg(feature = "native-bind")] +// /// Code for the Dioxus channel used to communicate between the dioxus and javascript code +// #[cfg(feature = "native-bind")] pub const NATIVE_EVAL_JS: &str = include_str!("../js/native_eval.js"); -#[cfg(feature = "wasm-bind")] -#[wasm_bindgen::prelude::wasm_bindgen] -pub struct JSOwner { - _owner: Box, -} +// #[cfg(feature = "wasm-bind")] +// #[wasm_bindgen::prelude::wasm_bindgen(module = "/src/js/eval.js")] +// extern "C" { +// pub type WebDioxusChannel; -#[cfg(feature = "wasm-bind")] -impl JSOwner { - pub fn new(owner: impl std::any::Any) -> Self { - Self { - _owner: Box::new(owner), - } - } -} +// #[wasm_bindgen(constructor)] +// pub fn new(owner: JSOwner) -> WebDioxusChannel; -#[cfg(feature = "wasm-bind")] -#[wasm_bindgen::prelude::wasm_bindgen(module = "/src/js/eval.js")] -extern "C" { - pub type WebDioxusChannel; +// #[wasm_bindgen(method, js_name = "rustSend")] +// pub fn rust_send(this: &WebDioxusChannel, value: wasm_bindgen::JsValue); - #[wasm_bindgen(constructor)] - pub fn new(owner: JSOwner) -> WebDioxusChannel; +// #[wasm_bindgen(method, js_name = "rustRecv")] +// pub async fn rust_recv(this: &WebDioxusChannel) -> wasm_bindgen::JsValue; - #[wasm_bindgen(method, js_name = "rustSend")] - pub fn rust_send(this: &WebDioxusChannel, value: wasm_bindgen::JsValue); +// #[wasm_bindgen(method)] +// pub fn send(this: &WebDioxusChannel, value: wasm_bindgen::JsValue); - #[wasm_bindgen(method, js_name = "rustRecv")] - pub async fn rust_recv(this: &WebDioxusChannel) -> wasm_bindgen::JsValue; +// #[wasm_bindgen(method)] +// pub async fn recv(this: &WebDioxusChannel) -> wasm_bindgen::JsValue; - #[wasm_bindgen(method)] - pub fn send(this: &WebDioxusChannel, value: wasm_bindgen::JsValue); +// #[wasm_bindgen(method)] +// pub fn weak(this: &WebDioxusChannel) -> WeakDioxusChannel; - #[wasm_bindgen(method)] - pub async fn recv(this: &WebDioxusChannel) -> wasm_bindgen::JsValue; +// pub type WeakDioxusChannel; - #[wasm_bindgen(method)] - pub fn weak(this: &WebDioxusChannel) -> WeakDioxusChannel; +// #[wasm_bindgen(method, js_name = "rustSend")] +// pub fn rust_send(this: &WeakDioxusChannel, value: wasm_bindgen::JsValue); - pub type WeakDioxusChannel; - - #[wasm_bindgen(method, js_name = "rustSend")] - pub fn rust_send(this: &WeakDioxusChannel, value: wasm_bindgen::JsValue); - - #[wasm_bindgen(method, js_name = "rustRecv")] - pub async fn rust_recv(this: &WeakDioxusChannel) -> wasm_bindgen::JsValue; -} +// #[wasm_bindgen(method, js_name = "rustRecv")] +// pub async fn rust_recv(this: &WeakDioxusChannel) -> wasm_bindgen::JsValue; +// } diff --git a/packages/html/src/document/mod.rs b/packages/html/src/document/mod.rs index 5cb13fad4e..147abdd9d7 100644 --- a/packages/html/src/document/mod.rs +++ b/packages/html/src/document/mod.rs @@ -35,12 +35,13 @@ fn create_element_in_head( attributes: &[(&str, String)], children: Option, ) -> String { - let helpers = include_str!("../js/head.js"); - let attributes = format_attributes(attributes); - let children = children - .map(|c| format!("\"{c}\"")) - .unwrap_or("null".to_string()); - format!(r#"{helpers};window.createElementInHead("{tag}", {attributes}, {children});"#) + todo!("create element in head") + // let helpers = include_str!("../js/head.js"); + // let attributes = format_attributes(attributes); + // let children = children + // .map(|c| format!("\"{c}\"")) + // .unwrap_or("null".to_string()); + // format!(r#"{helpers};window.createElementInHead("{tag}", {attributes}, {children});"#) } /// A provider for document-related functionality. By default most methods are driven through [`eval`]. @@ -85,43 +86,44 @@ pub trait Document { fn as_any(&self) -> &dyn std::any::Any; } -/// The default No-Op document -pub struct NoOpDocument; - -impl Document for NoOpDocument { - fn new_evaluator(&self, _js: String) -> GenerationalBox> { - tracing::error!("Eval is not supported on this platform. If you are using dioxus fullstack, you can wrap your code with `client! {{}}` to only include the code that runs eval in the client bundle."); - UnsyncStorage::owner().insert(Box::new(NoOpEvaluator)) - } - - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -struct NoOpEvaluator; -impl Evaluator for NoOpEvaluator { - fn send(&self, _data: serde_json::Value) -> Result<(), EvalError> { - Err(EvalError::Unsupported) - } - fn poll_recv( - &mut self, - _context: &mut Context<'_>, - ) -> Poll> { - Poll::Ready(Err(EvalError::Unsupported)) - } - fn poll_join( - &mut self, - _context: &mut Context<'_>, - ) -> Poll> { - Poll::Ready(Err(EvalError::Unsupported)) - } -} +// /// The default No-Op document +// pub struct NoOpDocument; + +// impl Document for NoOpDocument { +// fn new_evaluator(&self, _js: String) -> GenerationalBox> { +// tracing::error!("Eval is not supported on this platform. If you are using dioxus fullstack, you can wrap your code with `client! {{}}` to only include the code that runs eval in the client bundle."); +// UnsyncStorage::owner().insert(Box::new(NoOpEvaluator)) +// } + +// fn as_any(&self) -> &dyn std::any::Any { +// self +// } +// } + +// struct NoOpEvaluator; +// impl Evaluator for NoOpEvaluator { +// fn send(&self, _data: serde_json::Value) -> Result<(), EvalError> { +// Err(EvalError::Unsupported) +// } +// fn poll_recv( +// &mut self, +// _context: &mut Context<'_>, +// ) -> Poll> { +// Poll::Ready(Err(EvalError::Unsupported)) +// } +// fn poll_join( +// &mut self, +// _context: &mut Context<'_>, +// ) -> Poll> { +// Poll::Ready(Err(EvalError::Unsupported)) +// } +// } /// Get the document provider for the current platform or a no-op provider if the platform doesn't document functionality. pub fn document() -> Rc { dioxus_core::prelude::try_consume_context::>() - // Create a NoOp provider that always logs an error when trying to evaluate - // That way, we can still compile and run the code without a real provider - .unwrap_or_else(|| Rc::new(NoOpDocument) as Rc) + .expect("A document should exist with this renderer") + // Create a NoOp provider that always logs an error when trying to evaluate + // That way, we can still compile and run the code without a real provider + // .unwrap_or_else(|| Rc::new(NoOpDocument) as Rc) } diff --git a/packages/html/src/lib.rs b/packages/html/src/lib.rs index 18bcf6d5b6..5238592dbd 100644 --- a/packages/html/src/lib.rs +++ b/packages/html/src/lib.rs @@ -17,26 +17,23 @@ //! Currently, we don't validate for structures, but do validate attributes. pub mod elements; + #[cfg(feature = "hot-reload-context")] pub use elements::HtmlCtx; + #[cfg(feature = "html-to-rsx")] pub use elements::{map_html_attribute_to_rsx, map_html_element_to_rsx}; + pub mod events; pub(crate) mod file_data; pub use file_data::*; mod attribute_groups; pub mod geometry; pub mod input_data; -#[cfg(feature = "native-bind")] -pub mod native_bind; + pub mod point_interaction; mod render_template; -#[cfg(feature = "wasm-bind")] -mod web_sys_bind; - -#[cfg(feature = "wasm-bind")] -pub use web_sys_bind::*; #[cfg(feature = "serialize")] mod transit; diff --git a/packages/html/src/native_bind/mod.rs b/packages/html/src/native_bind/mod.rs deleted file mode 100644 index 4d84a4ab31..0000000000 --- a/packages/html/src/native_bind/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod native_file_engine; - -pub use native_file_engine::*; diff --git a/packages/html/src/native_bind/native_file_engine.rs b/packages/html/src/native_bind/native_file_engine.rs deleted file mode 100644 index 1c6ccef995..0000000000 --- a/packages/html/src/native_bind/native_file_engine.rs +++ /dev/null @@ -1,56 +0,0 @@ -use std::any::Any; -use std::path::PathBuf; - -use tokio::fs::File; -use tokio::io::AsyncReadExt; - -use crate::file_data::FileEngine; - -pub struct NativeFileEngine { - files: Vec, -} - -impl NativeFileEngine { - pub fn new(files: Vec) -> Self { - Self { files } - } -} - -#[cfg(feature = "file-engine")] -#[async_trait::async_trait(?Send)] -impl FileEngine for NativeFileEngine { - fn files(&self) -> Vec { - self.files - .iter() - .filter_map(|f| Some(f.to_str()?.to_string())) - .collect() - } - - async fn file_size(&self, file: &str) -> Option { - let file = File::open(file).await.ok()?; - Some(file.metadata().await.ok()?.len()) - } - - async fn read_file(&self, file: &str) -> Option> { - let mut file = File::open(file).await.ok()?; - - let mut contents = Vec::new(); - file.read_to_end(&mut contents).await.ok()?; - - Some(contents) - } - - async fn read_file_to_string(&self, file: &str) -> Option { - let mut file = File::open(file).await.ok()?; - - let mut contents = String::new(); - file.read_to_string(&mut contents).await.ok()?; - - Some(contents) - } - - async fn get_native_file(&self, file: &str) -> Option> { - let file = File::open(file).await.ok()?; - Some(Box::new(file)) - } -} diff --git a/packages/rsx/Cargo.toml b/packages/rsx/Cargo.toml index ec9a85b54f..5c1c264083 100644 --- a/packages/rsx/Cargo.toml +++ b/packages/rsx/Cargo.toml @@ -13,14 +13,15 @@ keywords = ["dom", "ui", "gui", "react"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -quote = { workspace = true } +internment = { workspace = true, optional = true } proc-macro2 = { workspace = true, features = ["span-locations"] } -dioxus-core = { workspace = true, optional = true } -syn = { workspace = true, features = ["full", "extra-traits", "visit", "visit-mut"] } +proc-macro2-diagnostics = { workspace = true } +quote = { workspace = true } serde = { workspace = true, features = ["derive"], optional = true } -internment = { version = "0.7.0", optional = true } +syn = { workspace = true, features = ["full", "extra-traits", "visit", "visit-mut"] } tracing = { workspace = true } -proc-macro2-diagnostics = { version = "0.10", default-features = false } +dioxus-core = { workspace = true, optional = true } # todo: this is being compiled in a proc macro + [features] default = [] diff --git a/packages/server-macro/Cargo.toml b/packages/server-macro/Cargo.toml index 8fb0aa275a..33e0fabde7 100644 --- a/packages/server-macro/Cargo.toml +++ b/packages/server-macro/Cargo.toml @@ -16,7 +16,6 @@ description = "Server function macros for Dioxus" proc-macro2 = "^1.0.63" quote = "^1.0.26" syn = { workspace = true, features = ["full"] } -convert_case = { workspace = true } server_fn_macro = "0.6.11" [lib] diff --git a/packages/web/Cargo.toml b/packages/web/Cargo.toml index df05cd51cc..98f545fd6e 100644 --- a/packages/web/Cargo.toml +++ b/packages/web/Cargo.toml @@ -11,7 +11,7 @@ keywords = ["dom", "ui", "gui", "react", "wasm"] [dependencies] dioxus-core = { workspace = true } -dioxus-html = { workspace = true, features = ["wasm-bind"] } +dioxus-html = { workspace = true } dioxus-hot-reload = { workspace = true, features = ["client"] } dioxus-signals = { workspace = true } dioxus-interpreter-js = { workspace = true, features = [ @@ -41,22 +41,44 @@ ciborium = { workspace = true, optional = true } [dependencies.web-sys] version = "0.3.56" features = [ + "AnimationEvent", + "ClipboardEvent", + "CloseEvent", + "Comment", + "CompositionEvent", + "console", + "CustomEvent", + "CustomEvent", + "DataTransfer", "Document", + "DragEvent", + "FocusEvent", "HtmlElement", + "HtmlFormElement", "HtmlInputElement", "HtmlSelectElement", "HtmlTextAreaElement", - "HtmlFormElement", + "InputEvent", + "KeyboardEvent", + "MouseEvent", + "NodeList", + "PointerEvent", + "ResizeObserverEntry", + "ResizeObserverSize", "Text", - "Comment", + "Touch", + "TouchEvent", + "TouchList", + "TransitionEvent", + "WheelEvent", "Window", - "DataTransfer", - "console", - "NodeList", - "CloseEvent", - "CustomEvent", ] + +[build-dependencies] +lazy-js-bundle = { workspace = true } + + [features] default = ["panic_hook", "mounted", "file_engine", "hot_reload", "document"] panic_hook = ["dep:console_error_panic_hook"] diff --git a/packages/web/src/bindings.rs b/packages/web/src/bindings.rs new file mode 100644 index 0000000000..05e55e0074 --- /dev/null +++ b/packages/web/src/bindings.rs @@ -0,0 +1,12 @@ +#[wasm_bindgen::prelude::wasm_bindgen] +pub struct JSOwner { + _owner: Box, +} + +impl JSOwner { + pub fn new(owner: impl std::any::Any) -> Self { + Self { + _owner: Box::new(owner), + } + } +} diff --git a/packages/web/src/document.rs b/packages/web/src/document.rs index ce8828a5e8..6a784f9acb 100644 --- a/packages/web/src/document.rs +++ b/packages/web/src/document.rs @@ -1,7 +1,7 @@ +use crate::bindings::JSOwner; use dioxus_core::ScopeId; -use dioxus_html::document::{ - Document, EvalError, Evaluator, JSOwner, WeakDioxusChannel, WebDioxusChannel, -}; +use dioxus_html::document::{Document, EvalError, Evaluator}; +// use dioxus_html::document::{Document, EvalError, Evaluator, WeakDioxusChannel, WebDioxusChannel}; use generational_box::{AnyStorage, GenerationalBox, UnsyncStorage}; use js_sys::Function; use serde::Serialize; diff --git a/packages/web/src/lib.rs b/packages/web/src/lib.rs index 44f7b94c5f..8e1a085e60 100644 --- a/packages/web/src/lib.rs +++ b/packages/web/src/lib.rs @@ -33,6 +33,7 @@ mod event; pub mod launch; mod mutations; pub use event::*; +pub mod bindings; #[cfg(feature = "document")] mod document; diff --git a/packages/html/src/web_sys_bind/events.rs b/packages/web/src/web_sys_bind/events.rs similarity index 100% rename from packages/html/src/web_sys_bind/events.rs rename to packages/web/src/web_sys_bind/events.rs diff --git a/packages/html/src/web_sys_bind/file_engine.rs b/packages/web/src/web_sys_bind/file_engine.rs similarity index 100% rename from packages/html/src/web_sys_bind/file_engine.rs rename to packages/web/src/web_sys_bind/file_engine.rs diff --git a/packages/html/src/web_sys_bind/mod.rs b/packages/web/src/web_sys_bind/mod.rs similarity index 100% rename from packages/html/src/web_sys_bind/mod.rs rename to packages/web/src/web_sys_bind/mod.rs From a8535147b1d97e501b2a6b58c37f8fe6993740ef Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Mon, 19 Aug 2024 11:11:36 -0700 Subject: [PATCH 021/139] clean up asset resolution and cli-dev profile --- .cargo/config.toml | 2 -- Cargo.lock | 4 +--- Cargo.toml | 22 ++++++++++----------- examples/meta.rs | 1 - examples/title.rs | 1 - packages/cli/src/builder/mod.rs | 4 +++- packages/cli/src/serve/mod.rs | 2 +- packages/cli/src/serve/watcher.rs | 4 +++- packages/desktop/src/index.html | 3 ++- packages/desktop/src/protocol.rs | 32 ++++++++++++------------------- packages/html/src/document/mod.rs | 13 ++++++------- packages/manganis/src/builder.rs | 12 ++++++++++++ 12 files changed, 51 insertions(+), 49 deletions(-) delete mode 100644 .cargo/config.toml diff --git a/.cargo/config.toml b/.cargo/config.toml deleted file mode 100644 index c24450e626..0000000000 --- a/.cargo/config.toml +++ /dev/null @@ -1,2 +0,0 @@ -[profile.dev.package."*"] -opt-level = 3 diff --git a/Cargo.lock b/Cargo.lock index 6bd99d9394..9cde83026a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2741,7 +2741,7 @@ dependencies = [ name = "dioxus-examples" version = "0.6.0-alpha.2" dependencies = [ - "base64 0.21.7", + "base64 0.22.1", "ciborium", "dioxus", "dioxus-ssr", @@ -2749,14 +2749,12 @@ dependencies = [ "futures-util", "getrandom 0.2.15", "http-range", - "manganis", "rand 0.8.5", "reqwest", "separator", "serde", "serde_json", "tokio", - "tracing-subscriber", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 3a69921a38..945c2d3678 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -194,17 +194,19 @@ cargo-config2 = "0.1.26" criterion = { version = "0.5" } -[profile.dev.package.dioxus-core-macro] -opt-level = 3 - # Enable a small amount of optimization in debug mode [profile.cli-dev] -inherits = "dev" +inherits = "release" opt-level = 1 +debug = true + +# [profile.cli-dev] +# inherits = "dev" +# opt-level = 1 -# Enable high optimizations for dependencies (incl. Bevy), but not for our code: -[profile.cli-dev.package."*"] -opt-level = 3 +# # Enable high optimizations for dependencies (incl. Bevy), but not for our code: +# [profile.cli-dev.package."*"] +# opt-level = 3 # Disable debug assertions to check the released path of core and other packages, but build without optimizations to keep build times quick [profile.release-unoptimized] @@ -228,12 +230,10 @@ publish = false version = "0.6.0-alpha.2" [dependencies] -manganis = { workspace = true, optional = true } reqwest = { workspace = true, features = ["json"], optional = true } +ciborium = { workspace = true, optional = true } +base64 = { workspace = true, optional = true } http-range = { version = "0.1.5", optional = true } -ciborium = { version = "0.2.1", optional = true } -base64 = { version = "0.21.0", optional = true } -tracing-subscriber = "0.3.17" [dev-dependencies] dioxus = { workspace = true, features = ["router"] } diff --git a/examples/meta.rs b/examples/meta.rs index fae916258f..39c2ef3d21 100644 --- a/examples/meta.rs +++ b/examples/meta.rs @@ -3,7 +3,6 @@ use dioxus::prelude::*; fn main() { - tracing_subscriber::fmt::init(); launch(app); } diff --git a/examples/title.rs b/examples/title.rs index ebe258963c..18d7bcbb64 100644 --- a/examples/title.rs +++ b/examples/title.rs @@ -3,7 +3,6 @@ use dioxus::prelude::*; fn main() { - tracing_subscriber::fmt::init(); launch(app); } diff --git a/packages/cli/src/builder/mod.rs b/packages/cli/src/builder/mod.rs index 61b059cc19..21a851058d 100644 --- a/packages/cli/src/builder/mod.rs +++ b/packages/cli/src/builder/mod.rs @@ -1,7 +1,7 @@ -use crate::build::Build; use crate::cli::serve::ServeArguments; use crate::dioxus_crate::DioxusCrate; use crate::Result; +use crate::{build::Build, config}; use dioxus_cli_config::{Platform, RuntimeCLIArguments}; use futures_util::stream::select_all; use futures_util::StreamExt; @@ -148,6 +148,7 @@ impl BuildResult { /// Open the executable if this is a native build pub fn open( &self, + config: &DioxusCrate, serve: &ServeArguments, fullstack_address: Option, workspace: &std::path::Path, @@ -168,6 +169,7 @@ impl BuildResult { // dioxus_cli_config::__private::SERVE_ENV, // serde_json::to_string(&arguments).unwrap(), // ) + .env("CARGO_MANIFEST_DIR", config.crate_dir()) .stderr(Stdio::piped()) .stdout(Stdio::piped()) .kill_on_drop(true) diff --git a/packages/cli/src/serve/mod.rs b/packages/cli/src/serve/mod.rs index 43c27bde73..71329efbe8 100644 --- a/packages/cli/src/serve/mod.rs +++ b/packages/cli/src/serve/mod.rs @@ -147,7 +147,7 @@ pub async fn serve_all( // If we have a build result, open it for build_result in results.iter() { - let child = build_result.open(&serve.server_arguments, server.fullstack_address(), &dioxus_crate.workspace_dir()); + let child = build_result.open(&dioxus_crate, &serve.server_arguments, server.fullstack_address(), &dioxus_crate.workspace_dir()); match child { Ok(Some(child_proc)) => builder.children.push((build_result.target_platform, child_proc)), Err(e) => { diff --git a/packages/cli/src/serve/watcher.rs b/packages/cli/src/serve/watcher.rs index 571ac98415..0e97a01c0e 100644 --- a/packages/cli/src/serve/watcher.rs +++ b/packages/cli/src/serve/watcher.rs @@ -203,15 +203,17 @@ impl Watcher { // If the extension is a backup file, or a hidden file, ignore it completely (no rebuilds) if is_backup_file(path.to_path_buf()) { - tracing::trace!("Ignoring backup file: {:?}", path); continue; } // If the path is ignored, don't watch it if self.ignore.matched(path, path.is_dir()).is_ignore() { + tracing::trace!("Ignoring update to file: {:?}", path); continue; } + tracing::trace!("Enqueuing hotreload update to file: {:?}", path); + modified_files.push(path.clone()); } diff --git a/packages/desktop/src/index.html b/packages/desktop/src/index.html index 45926e3f1f..423d42bc94 100644 --- a/packages/desktop/src/index.html +++ b/packages/desktop/src/index.html @@ -2,7 +2,8 @@ Dioxus app - + + diff --git a/packages/desktop/src/protocol.rs b/packages/desktop/src/protocol.rs index 179c862f91..9b58fe6603 100644 --- a/packages/desktop/src/protocol.rs +++ b/packages/desktop/src/protocol.rs @@ -117,14 +117,12 @@ fn assets_head() -> Option { } } -fn running_in_dev_mode() -> bool { - // If running under cargo, there's no bundle! - // There might be a smarter/more resilient way of doing this - std::env::var_os("CARGO").is_some() -} - fn resolve_resource(path: &Path) -> PathBuf { - let mut base_path = get_asset_root_or_default(); + let mut base_path = get_asset_root().unwrap_or_else(|| std::env::current_dir().unwrap()); + + println!("asset path: {:?}, {path:?}", base_path); + base_path.push(path); + println!("asset path resolved: {:?}", base_path); // // Even in dev mode, mobile needs to resolve the asset path from the bundle // if running_in_dev_mode() || cfg!(any(target_os = "ios", target_os = "android")) { @@ -261,14 +259,6 @@ fn module_loader(root_id: &str, headless: bool) -> String { ) } -/// Get the asset directory, following tauri/cargo-bundles directory discovery approach -/// -/// Defaults to the current directory if no asset directory is found, which is useful for development when the app -/// isn't bundled. -fn get_asset_root_or_default() -> PathBuf { - get_asset_root().unwrap_or_else(|| std::env::current_dir().unwrap()) -} - /// Get the asset directory, following tauri/cargo-bundles directory discovery approach /// /// Currently supports: @@ -280,21 +270,23 @@ fn get_asset_root_or_default() -> PathBuf { /// - [ ] Android #[allow(unreachable_code)] fn get_asset_root() -> Option { + // If the manifest_dir is set, we're in a cargo project and we can use that as the asset root + // This works with asset!() since asset!() is always relatif to the manifest dir, both in dev and bundled + if let Some(cargo_dir) = std::env::var("CARGO_MANIFEST_DIR").map(PathBuf::from).ok() { + return Some(cargo_dir); + } + // Use the prescence of the bundle to determine if we're in dev mode // todo: for other platforms, we should check their bundles too. This currently only works for macOS and iOS #[cfg(any(target_os = "macos", target_os = "ios"))] { + // Note that this will return `target/debug` if you're in debug mode - not reliable check if we're in dev mode if let Some(resources) = core_foundation::bundle::CFBundle::main_bundle().resources_path() { return dunce::canonicalize(resources).ok(); } } None - - // dioxus_cli_config::CURRENT_CONFIG - // .as_ref() - // .map(|c| c.application.out_dir.clone()) - // .ok() } /// Get the mime type from a path-like string diff --git a/packages/html/src/document/mod.rs b/packages/html/src/document/mod.rs index 147abdd9d7..6e6c498a84 100644 --- a/packages/html/src/document/mod.rs +++ b/packages/html/src/document/mod.rs @@ -35,13 +35,12 @@ fn create_element_in_head( attributes: &[(&str, String)], children: Option, ) -> String { - todo!("create element in head") - // let helpers = include_str!("../js/head.js"); - // let attributes = format_attributes(attributes); - // let children = children - // .map(|c| format!("\"{c}\"")) - // .unwrap_or("null".to_string()); - // format!(r#"{helpers};window.createElementInHead("{tag}", {attributes}, {children});"#) + let helpers = include_str!("../js/head.js"); + let attributes = format_attributes(attributes); + let children = children + .map(|c| format!("\"{c}\"")) + .unwrap_or("null".to_string()); + format!(r#"{helpers};window.createElementInHead("{tag}", {attributes}, {children});"#) } /// A provider for document-related functionality. By default most methods are driven through [`eval`]. diff --git a/packages/manganis/src/builder.rs b/packages/manganis/src/builder.rs index e460cc88e6..9714f6e7ba 100644 --- a/packages/manganis/src/builder.rs +++ b/packages/manganis/src/builder.rs @@ -1,3 +1,5 @@ +use std::path::{Path, PathBuf}; + /// Asset #[derive(Debug, PartialEq, Clone, Copy, Hash)] pub struct Asset { @@ -55,6 +57,16 @@ impl Asset { pub const fn typescript(self) -> TypeScriptAsset { TypeScriptAsset::new(self.src) } + + /// Get the path to the asset + pub fn path(&self) -> PathBuf { + PathBuf::from(self.src.input.to_string()) + } + + /// Get the path to the asset + pub fn relative_path(&self) -> PathBuf { + PathBuf::from(self.src.input.trim_start_matches("/").to_string()) + } } impl std::fmt::Display for Asset { From 38594e966d0369516b1aef202157d2610458c2c1 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Mon, 19 Aug 2024 13:59:21 -0700 Subject: [PATCH 022/139] wire up absolute paths for manganis asset in a particular mode --- .vscode/settings.json | 2 +- Cargo.lock | 15 +- Cargo.toml | 3 - examples/custom_assets.rs | 11 ++ examples/dynamic_asset.rs | 13 +- packages/desktop/src/protocol.rs | 207 +++++++++++--------------- packages/desktop/src/webview.rs | 15 +- packages/manganis-macro/src/asset.rs | 9 +- packages/manganis-resolver/Cargo.toml | 15 -- packages/manganis-resolver/src/lib.rs | 19 --- packages/manganis/Cargo.toml | 6 + packages/manganis/src/builder.rs | 55 ++++++- 12 files changed, 183 insertions(+), 187 deletions(-) delete mode 100644 packages/manganis-resolver/Cargo.toml delete mode 100644 packages/manganis-resolver/src/lib.rs diff --git a/.vscode/settings.json b/.vscode/settings.json index 567f72b45f..df398f22be 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,7 +7,7 @@ // "rust-analyzer.check.workspace": false, // "rust-analyzer.check.features": "all", // "rust-analyzer.cargo.features": "all", - // "rust-analyzer.check.allTargets": true, + "rust-analyzer.check.allTargets": false, // we don't want the formatter to kick in while we're working on dioxus itself "dioxus.formatOnSave": "disabled", } diff --git a/Cargo.lock b/Cargo.lock index 9cde83026a..c5147a1088 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6331,6 +6331,10 @@ dependencies = [ name = "manganis" version = "0.6.0-alpha.2" dependencies = [ + "core-foundation 0.10.0", + "dunce", + "infer 0.16.0", + "manganis-common", "manganis-macro", "once_cell", ] @@ -6386,17 +6390,6 @@ dependencies = [ "syn 2.0.74", ] -[[package]] -name = "manganis-resolver" -version = "0.6.0-alpha.2" -dependencies = [ - "core-foundation 0.9.4", - "dunce", - "infer 0.16.0", - "manganis", - "manganis-common", -] - [[package]] name = "markup5ever" version = "0.11.0" diff --git a/Cargo.toml b/Cargo.toml index 945c2d3678..f45f4a5b17 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,7 +62,6 @@ members = [ "packages/manganis-macro", "packages/manganis-common", "packages/manganis-cli-support", - "packages/manganis-resolver", "packages/manganis-test-package", "packages/manganis-test-package/test-package-dependency", "packages/manganis-test-package/test-package-nested-dependency", @@ -107,8 +106,6 @@ manganis = { path = "packages/manganis", version = "0.6.0-alpha.2" } manganis-common = { path = "packages/manganis-common", version = "0.6.0-alpha.2" } manganis-cli-support = { path = "packages/manganis-cli-support", version = "0.6.0-alpha.2" } manganis-macro = { path = "packages/manganis-macro", version = "0.6.0-alpha.2" } -manganis-resolver = { path = "packages/manganis-resolver", version = "0.6.0-alpha.2" } - warnings = { version = "0.2.0" } diff --git a/examples/custom_assets.rs b/examples/custom_assets.rs index 5bebddc01d..e1c7db7ff9 100644 --- a/examples/custom_assets.rs +++ b/examples/custom_assets.rs @@ -28,6 +28,17 @@ fn app() -> Element { div { h1 { "This should show an image:" } img { src: ASSET_PATH } + + // keep support for these too + // img { + // src: "/Users/jonkelley/Development/dioxus/examples/assets/logo.png" + // } + // img { + // src: "/examples/assets/logo.png" + // } + // img { + // src: "examples/assets/logo.png" + // } } } } diff --git a/examples/dynamic_asset.rs b/examples/dynamic_asset.rs index b254b7047f..db0c599cd5 100644 --- a/examples/dynamic_asset.rs +++ b/examples/dynamic_asset.rs @@ -15,17 +15,18 @@ fn main() { fn app() -> Element { use_asset_handler("logos", |request, response| { - // We get the original path - make sure you handle that! - if request.uri().path() != "/logos/logo.png" { - return; - } + todo!() + // // We get the original path - make sure you handle that! + // if request.uri().path() != "/logos/logo.png" { + // return; + // } - response.respond(Response::new(include_bytes!("./assets/logo.png").to_vec())); + // response.respond(Response::new(include_bytes!("./assets/logo.png").to_vec())); }); rsx! { head::Link { rel: "stylesheet", href: STYLE } h1 { "Dynamic Assets" } - img { src: "/logos/logo.png" } + img { src: "custom://logos/logo.png" } } } diff --git a/packages/desktop/src/protocol.rs b/packages/desktop/src/protocol.rs index 9b58fe6603..ee0e70c38c 100644 --- a/packages/desktop/src/protocol.rs +++ b/packages/desktop/src/protocol.rs @@ -27,6 +27,91 @@ const EVENTS_PATH: &str = "dioxus://index.html/__events"; static DEFAULT_INDEX: &str = include_str!("./index.html"); +/// Handle a request from the webview +/// +/// - Tries to stream edits if they're requested. +/// - If that doesn't match, tries a user provided asset handler +/// - If that doesn't match, tries to serve a file from the filesystem +pub(super) fn desktop_handler( + request: Request>, + asset_handlers: AssetHandlerRegistry, + responder: RequestAsyncResponder, + edit_state: &WebviewEdits, + custom_head: Option, + custom_index: Option, + root_name: &str, + headless: bool, +) { + // Try to serve the index file first + if let Some(index_bytes) = + index_request(&request, custom_head, custom_index, &root_name, headless) + { + return responder.respond(index_bytes); + } + + // If the request is asking for edits (ie binary protocol streaming), do that + let trimmed_uri = request.uri().path().trim_matches('/'); + if trimmed_uri == "__edits" { + return edit_state.wry_queue.handle_request(responder); + } + + // If the request is asking for an event response, do that + if trimmed_uri == "__events" { + return edit_state.handle_event(request, responder); + } + + match serve_asset(request) { + Ok(res) => responder.respond(res), + Err(_e) => responder.respond( + Response::builder() + .status(StatusCode::INTERNAL_SERVER_ERROR) + .body(String::from("Failed to serve asset").into_bytes()) + .unwrap(), + ), + } +} + +fn serve_asset(request: Request>) -> Result>> { + // If the user provided a custom asset handler, then call it and return the response if the request was handled. + // The path is the first part of the URI, so we need to trim the leading slash. + let mut asset = PathBuf::from( + urlencoding::decode(request.uri().path()) + .expect("expected URL to be UTF-8 encoded") + .as_ref(), + ); + + // If the asset doesn't exist, then we might need to try resolving it from the bundle root manually. + // We can mostly guarantee that manganis will give us absolute paths, so the only way we're getting these + // is if someone is passing in a relative path to their project, manually + // + // Do I *think* you should be doing this? No. + // Eventually we want to remove this, so consider it deprecated + if !asset.exists() { + let relative_path = request.uri().path().trim_start_matches('/'); + let bundle_root = get_asset_root().unwrap_or_else(|| std::env::current_dir().unwrap()); + asset = bundle_root.join(relative_path); + } + + // If the asset exists, then we can serve it! + if asset.exists() { + return Ok(Response::builder() + .header("Content-Type", get_mime_from_path(&asset)?) + .header("Access-Control-Allow-Origin", "*") + .body(std::fs::read(asset)?)?); + } + + // todo: we want to move the custom assets onto a different protocol or something + // if let Some(name) = path.iter().next().unwrap().to_str() { + // if asset_handlers.has_handler(name) { + // return asset_handlers.handle_request(name, request, responder); + // } + // } + + return Ok(Response::builder() + .status(StatusCode::NOT_FOUND) + .body(String::from("Not Found").into_bytes())?); +} + /// Build the index.html file we use for bootstrapping a new app /// /// We use wry/webview by building a special index.html that forms a bridge between the webview and your rust code @@ -36,7 +121,7 @@ static DEFAULT_INDEX: &str = include_str!("./index.html"); /// mess with UI elements. We make this decision since other renderers like LiveView are very separate and can /// never properly bridge the gap. Eventually of course, the idea is to build a custom CSS/HTML renderer where you /// *do* have native control over elements, but that still won't work with liveview. -pub(super) fn index_request( +fn index_request( request: &Request>, custom_head: Option, custom_index: Option, @@ -53,17 +138,8 @@ pub(super) fn index_request( // Insert a custom head if provided // We look just for the closing head tag. If a user provided a custom index with weird syntax, this might fail - let head = match custom_head { - Some(mut head) => { - if let Some(assets_head) = assets_head() { - head.push_str(&assets_head); - } - Some(head) - } - None => assets_head(), - }; - if let Some(head) = head { + if let Some(head) = custom_head { index.insert_str(index.find("").expect("Head element to exist"), &head); } @@ -82,47 +158,12 @@ pub(super) fn index_request( .ok() } -fn assets_head() -> Option { - #[cfg(any( - target_os = "windows", - target_os = "macos", - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd" - ))] - { - let assets_head_path = PathBuf::from("__assets_head.html"); - let head = resolve_resource(&assets_head_path); - match std::fs::read_to_string(&head) { - Ok(s) => Some(s), - Err(err) => { - tracing::warn!("Assets built with manganis cannot be preloaded (failed to read {head:?}). This warning may occur when you build a desktop application without the dioxus CLI. If you do not use manganis, you can ignore this warning: {err}."); - None - } - } - } - #[cfg(not(any( - target_os = "windows", - target_os = "macos", - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd" - )))] - { - None - } -} - fn resolve_resource(path: &Path) -> PathBuf { let mut base_path = get_asset_root().unwrap_or_else(|| std::env::current_dir().unwrap()); - println!("asset path: {:?}, {path:?}", base_path); + // println!("asset path: {:?}, {path:?}", base_path); base_path.push(path); - println!("asset path resolved: {:?}", base_path); + // println!("asset path resolved: {:?}", base_path); // // Even in dev mode, mobile needs to resolve the asset path from the bundle // if running_in_dev_mode() || cfg!(any(target_os = "ios", target_os = "android")) { @@ -152,76 +193,6 @@ fn resolve_resource(path: &Path) -> PathBuf { base_path } -/// Handle a request from the webview -/// -/// - Tries to stream edits if they're requested. -/// - If that doesn't match, tries a user provided asset handler -/// - If that doesn't match, tries to serve a file from the filesystem -pub(super) fn desktop_handler( - request: Request>, - asset_handlers: AssetHandlerRegistry, - responder: RequestAsyncResponder, - edit_state: &WebviewEdits, -) { - // If the request is asking for edits (ie binary protocol streaming), do that - let trimmed_uri = request.uri().path().trim_matches('/'); - if trimmed_uri == "__edits" { - return edit_state.wry_queue.handle_request(responder); - } - - // If the request is asking for an event response, do that - if trimmed_uri == "__events" { - return edit_state.handle_event(request, responder); - } - - // If the user provided a custom asset handler, then call it and return the response if the request was handled. - // The path is the first part of the URI, so we need to trim the leading slash. - let path = PathBuf::from( - urlencoding::decode(request.uri().path().trim_start_matches('/')) - .expect("expected URL to be UTF-8 encoded") - .as_ref(), - ); - - if path.parent().is_none() { - return tracing::error!("Asset request has no parent {path:?}"); - } - - if let Some(name) = path.iter().next().unwrap().to_str() { - if asset_handlers.has_handler(name) { - return asset_handlers.handle_request(name, request, responder); - } - } - - // Else, try to serve a file from the filesystem. - match serve_from_fs(path) { - Ok(res) => responder.respond(res), - Err(e) => { - tracing::error!("Error serving request from filesystem {}", e); - } - } -} - -fn serve_from_fs(path: PathBuf) -> Result>> { - // If the path is relative, we'll try to serve it from the assets directory. - let mut asset = resolve_resource(&path); - - // If we can't find it, make it absolute and try again - if !asset.exists() { - asset = PathBuf::from("/").join(&path); - } - - if !asset.exists() { - return Ok(Response::builder() - .status(StatusCode::NOT_FOUND) - .body(String::from("Not Found").into_bytes())?); - } - - Ok(Response::builder() - .header("Content-Type", get_mime_from_path(&asset)?) - .header("Access-Control-Allow-Origin", "*") - .body(std::fs::read(asset)?)?) -} - /// Construct the inline script that boots up the page and bridges the webview with rust code. /// /// The arguments here: diff --git a/packages/desktop/src/webview.rs b/packages/desktop/src/webview.rs index eaec2320bd..162c841427 100644 --- a/packages/desktop/src/webview.rs +++ b/packages/desktop/src/webview.rs @@ -196,19 +196,16 @@ impl WebviewInstance { edits ]; move |request, responder: RequestAsyncResponder| { - // Try to serve the index file first - if let Some(index_bytes) = protocol::index_request( - &request, + protocol::desktop_handler( + request, + asset_handlers.clone(), + responder, + &edits, custom_head.clone(), custom_index.clone(), &root_name, headless, - ) { - return responder.respond(index_bytes); - } - - // Otherwise, try to serve an asset, either from the user or the filesystem - protocol::desktop_handler(request, asset_handlers.clone(), responder, &edits); + ) } }; diff --git a/packages/manganis-macro/src/asset.rs b/packages/manganis-macro/src/asset.rs index 704a3e6758..4d614a7f4e 100644 --- a/packages/manganis-macro/src/asset.rs +++ b/packages/manganis-macro/src/asset.rs @@ -8,7 +8,7 @@ use proc_macro2::Ident; use proc_macro2::TokenStream as TokenStream2; use quote::{quote, quote_spanned, ToTokens}; use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, fs::File, sync::atomic::AtomicBool}; +use std::{collections::HashMap, fs::File, path::Path, sync::atomic::AtomicBool}; use std::{path::PathBuf, sync::atomic::Ordering}; use syn::{ parenthesized, parse::Parse, parse_macro_input, punctuated::Punctuated, token::Token, Expr, @@ -32,8 +32,13 @@ struct ResourceAsset { struct AssetError {} impl ResourceAsset { fn parse_any(input: &str) -> Result { + let absolute = std::env::var("CARGO_MANIFEST_DIR") + .map(|s| s.into()) + .unwrap_or_else(|_| std::env::current_dir().unwrap()); + let absolute = absolute.join(input.trim_start_matches('/')); + let input = PathBuf::from(input); - let local = Some(input.clone()); + let local = Some(absolute.clone()); let bundled = input.clone(); // let local = match input.canonicalized().is_file() { // true => Some(input.canonicalized()), diff --git a/packages/manganis-resolver/Cargo.toml b/packages/manganis-resolver/Cargo.toml deleted file mode 100644 index 7ea1df5679..0000000000 --- a/packages/manganis-resolver/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "manganis-resolver" -edition = "2021" -version.workspace = true - -[dependencies] -infer = { workspace = true } -manganis-common = { workspace = true } -manganis = { workspace = true } -dunce = "1.0.2" - -[target.'cfg(target_os = "macos")'.dependencies] -core-foundation = "0.9.3" - -[target.'cfg(target_os ="macos")'.dependencies] diff --git a/packages/manganis-resolver/src/lib.rs b/packages/manganis-resolver/src/lib.rs deleted file mode 100644 index f3bd86cb4e..0000000000 --- a/packages/manganis-resolver/src/lib.rs +++ /dev/null @@ -1,19 +0,0 @@ -// use manganis::Asset; -// use std::path::PathBuf; - -// pub struct VirtualFile {} - -// impl VirtualFile { -// /// Return an absolute, canonicalized path to the file -// pub fn path(&self) -> PathBuf { -// todo!() -// } -// } - -// pub fn resolve(asset: Asset) -> Result { -// todo!() -// } - -// pub fn resolve_macos(mg_path: &str) -> Result { -// todo!() -// } diff --git a/packages/manganis/Cargo.toml b/packages/manganis/Cargo.toml index 02d85c11e3..1cbb4cd061 100644 --- a/packages/manganis/Cargo.toml +++ b/packages/manganis/Cargo.toml @@ -24,6 +24,12 @@ once_cell = "1.19.0" # core-foundation = "0.9.3" # [target.'cfg(target_os ="macos")'.dependencies] +infer = { workspace = true } +manganis-common = { workspace = true } +dunce = "1.0.2" + +[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies] +core-foundation = "0.10.0" [features] diff --git a/packages/manganis/src/builder.rs b/packages/manganis/src/builder.rs index 9714f6e7ba..455ca6629a 100644 --- a/packages/manganis/src/builder.rs +++ b/packages/manganis/src/builder.rs @@ -71,7 +71,7 @@ impl Asset { impl std::fmt::Display for Asset { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.src.input) + write!(f, "{}", self.src.resolve().display()) } } @@ -84,13 +84,62 @@ pub struct AssetSource { /// The sourcefile of the asset pub source_file: &'static str, - /// + /// The absolute path to the asset on the filesystem pub local: &'static str, /// pub bundled: &'static str, } +impl AssetSource { + /// Return a canonicalized path to the asset + pub fn resolve(&self) -> PathBuf { + // if we're running with cargo in the loop, we can use the absolute path. + // this is non-bundled situations + if let Ok(_manifest_dir) = std::env::var("CARGO_MANIFEST_DIR") { + return PathBuf::from(self.local); + } + + // Otherwise, we need to resolve the bundled path against the basepath. + // on native this will be the bundled path + base_path() + .unwrap_or_else(|| std::env::current_dir().unwrap()) + .join(self.input.trim_start_matches('/')) + } +} + +fn base_path() -> Option { + // Use the prescence of the bundle to determine if we're in dev mode + // todo: for other platforms, we should check their bundles too. This currently only works for macOS and iOS + #[cfg(any(target_os = "macos", target_os = "ios"))] + { + // Note that this will return `target/debug` if you're in debug mode - not reliable check if we're in dev mode + if let Some(resources) = core_foundation::bundle::CFBundle::main_bundle().resources_path() { + return dunce::canonicalize(resources).ok(); + } + } + + // todo: this needs to be real canonicalizations + // let root; + + // #[cfg(target_os = "wasm32-unknown-unknown")] + // { + // root = "/".to_string(); + // } + + // #[cfg(not(target_os = "wasm32-unknown-unknown"))] + // { + // root = std::env::current_dir().unwrap(); + // } + + // let var_base_path = std::env::var("MANGANIS_BASE_PATH") + // .unwrap_or_else(|| "/".to_string()) + // .map(|p| PathBuf::from(p)); + + // var_base_path.unwrap_or(root) + None +} + /// pub struct CssAsset { src: AssetSource, @@ -210,7 +259,7 @@ pub struct ImageAsset { impl std::fmt::Display for ImageAsset { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - todo!() + write!(f, "{}", self.src.resolve().display()) } } From 4485c64980ed7d8c1c42636acd7fe3934f510192 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 21 Aug 2024 14:24:10 -0700 Subject: [PATCH 023/139] move document related things around to shorten compile times --- Cargo.lock | 73 +-- Cargo.toml | 3 + examples/rsx_usage.rs | 3 + packages/cli/Cargo.toml | 3 +- .../cli/src/serve/hot_reloading_file_map.rs | 3 +- packages/core-types/src/bubbles.rs | 100 ++++ packages/core-types/src/event.rs | 203 +++++++ packages/core-types/src/hr_context.rs | 19 + packages/core-types/src/lib.rs | 9 + packages/core/Cargo.toml | 4 +- packages/core/src/events.rs | 191 ------ packages/core/src/lib.rs | 1 + packages/core/src/nodes.rs | 132 +++-- packages/core/src/runtime.rs | 4 +- packages/desktop/Cargo.toml | 1 + packages/desktop/src/document.rs | 82 +-- packages/desktop/src/protocol.rs | 2 +- packages/desktop/src/webview.rs | 2 +- packages/dioxus/Cargo.toml | 5 +- packages/document/Cargo.toml | 10 + packages/{html => document}/docs/eval.md | 0 packages/{html => document}/docs/head.md | 0 packages/document/src/eval.rs | 121 ++++ packages/document/src/head.rs | 543 ++++++++++++++++++ packages/document/src/lib.rs | 87 +++ packages/fullstack/Cargo.toml | 6 +- packages/fullstack/src/document/web.rs | 17 +- packages/fullstack/src/launch.rs | 2 +- packages/hot-reload/Cargo.toml | 6 +- packages/hot-reload/src/ws_receiver.rs | 36 +- packages/html/Cargo.toml | 7 +- packages/html/build.rs | 9 - packages/html/src/document/eval.rs | 134 ----- packages/html/src/document/head.rs | 542 ----------------- packages/html/src/document/mod.rs | 94 +-- packages/html/src/elements.rs | 4 +- packages/html/src/events/mod.rs | 94 --- packages/html/src/lib.rs | 13 +- packages/html/src/render_template.rs | 40 -- packages/interpreter/Cargo.toml | 4 +- .../interpreter/src/write_native_mutations.rs | 2 +- packages/manganis-common/Cargo.toml | 8 +- packages/manganis-common/build.rs | 3 - .../manganis-common/src/asset/resource.rs | 162 +++--- packages/manganis-common/src/lib.rs | 2 +- packages/manganis-macro/Cargo.toml | 10 +- .../test-package-dependency/src/lib.rs | 8 +- .../test-package-nested-dependency/src/lib.rs | 11 +- packages/manganis/Cargo.toml | 12 +- packages/manganis/src/builder.rs | 25 +- packages/rsx/Cargo.toml | 6 +- packages/rsx/src/attribute.rs | 4 +- packages/rsx/src/hot_reload/context.rs | 18 - packages/rsx/src/hot_reload/diff.rs | 2 +- packages/rsx/src/hot_reload/mod.rs | 5 - packages/rsx/src/lib.rs | 3 - packages/rsx/src/node.rs | 4 +- packages/ssr/Cargo.toml | 1 + packages/ssr/src/renderer.rs | 4 +- packages/web/Cargo.toml | 7 +- packages/web/src/bindings.rs | 145 +++++ packages/web/src/document.rs | 225 ++++---- packages/web/src/event.rs | 88 ++- packages/web/src/hot_reload.rs | 2 +- packages/web/src/lib.rs | 4 +- packages/web/src/mutations.rs | 2 +- packages/{html => web}/tsconfig.json | 0 67 files changed, 1775 insertions(+), 1597 deletions(-) create mode 100644 packages/core-types/src/bubbles.rs create mode 100644 packages/core-types/src/event.rs create mode 100644 packages/core-types/src/hr_context.rs create mode 100644 packages/document/Cargo.toml rename packages/{html => document}/docs/eval.md (100%) rename packages/{html => document}/docs/head.md (100%) create mode 100644 packages/document/src/eval.rs create mode 100644 packages/document/src/head.rs create mode 100644 packages/document/src/lib.rs delete mode 100644 packages/html/build.rs delete mode 100644 packages/html/src/document/eval.rs delete mode 100644 packages/html/src/render_template.rs delete mode 100644 packages/manganis-common/build.rs rename packages/{html => web}/tsconfig.json (100%) diff --git a/Cargo.lock b/Cargo.lock index c5147a1088..05a224479c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1083,12 +1083,6 @@ dependencies = [ "cipher", ] -[[package]] -name = "borrow-or-share" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eeab4423108c5d7c744f4d234de88d18d636100093ae04caf4825134b9c3a32" - [[package]] name = "borsh" version = "1.5.1" @@ -2567,6 +2561,7 @@ dependencies = [ "dioxus-check", "dioxus-cli-config", "dioxus-core", + "dioxus-core-types", "dioxus-hot-reload", "dioxus-html", "dioxus-rsx", @@ -2650,13 +2645,13 @@ version = "0.6.0-alpha.2" dependencies = [ "const_format", "dioxus", + "dioxus-core-types", "dioxus-html", "dioxus-ssr", "futures-channel", "futures-util", "generational-box", "longest-increasing-subsequence", - "manganis", "pretty_assertions", "rand 0.8.5", "reqwest", @@ -2702,6 +2697,7 @@ dependencies = [ "core-foundation 0.10.0", "dioxus", "dioxus-core", + "dioxus-document", "dioxus-hooks", "dioxus-hot-reload", "dioxus-html", @@ -2737,6 +2733,16 @@ dependencies = [ "wry", ] +[[package]] +name = "dioxus-document" +version = "0.6.0-alpha.2" +dependencies = [ + "dioxus-core", + "dioxus-core-macro", + "dioxus-core-types", + "tracing", +] + [[package]] name = "dioxus-examples" version = "0.6.0-alpha.2" @@ -2781,7 +2787,9 @@ dependencies = [ "clap", "dioxus", "dioxus-cli-config", + "dioxus-core-types", "dioxus-desktop", + "dioxus-document", "dioxus-hot-reload", "dioxus-interpreter-js", "dioxus-lib", @@ -2841,7 +2849,6 @@ dependencies = [ "dioxus-html", "dioxus-rsx", "dioxus-signals", - "futures-util", "serde", "serde_json", "tokio", @@ -2856,15 +2863,13 @@ dependencies = [ "async-trait", "dioxus", "dioxus-core", - "dioxus-core-macro", - "dioxus-hooks", + "dioxus-core-types", "dioxus-html-internal-macro", "dioxus-rsx", "dioxus-web", "enumset", "euclid", "futures-channel", - "generational-box", "js-sys", "keyboard-types", "lazy-js-bundle", @@ -2894,7 +2899,7 @@ name = "dioxus-interpreter-js" version = "0.6.0-alpha.2" dependencies = [ "dioxus-core", - "dioxus-html", + "dioxus-core-types", "js-sys", "lazy-js-bundle", "serde", @@ -3050,13 +3055,13 @@ name = "dioxus-rsx" version = "0.6.0-alpha.2" dependencies = [ "dioxus-core", + "dioxus-core-types", "internment", "prettier-please", "prettyplease", "proc-macro2", "proc-macro2-diagnostics", "quote", - "serde", "syn 2.0.74", "tracing", ] @@ -3095,6 +3100,7 @@ dependencies = [ "dioxus", "dioxus-cli-config", "dioxus-core", + "dioxus-core-types", "dioxus-html", "dioxus-interpreter-js", "dioxus-signals", @@ -3144,10 +3150,13 @@ dependencies = [ name = "dioxus-web" version = "0.6.0-alpha.2" dependencies = [ + "async-trait", "ciborium", "console_error_panic_hook", "dioxus", "dioxus-core", + "dioxus-core-types", + "dioxus-document", "dioxus-hot-reload", "dioxus-html", "dioxus-interpreter-js", @@ -3728,17 +3737,6 @@ dependencies = [ "miniz_oxide", ] -[[package]] -name = "fluent-uri" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d77395429e0ce700a8378be6625660a4aa00ca5dc5cd1527193ebd0946cc9b3" -dependencies = [ - "borrow-or-share", - "ref-cast", - "serde", -] - [[package]] name = "flume" version = "0.11.0" @@ -6331,9 +6329,7 @@ dependencies = [ name = "manganis" version = "0.6.0-alpha.2" dependencies = [ - "core-foundation 0.10.0", "dunce", - "infer 0.16.0", "manganis-common", "manganis-macro", "once_cell", @@ -6371,10 +6367,6 @@ version = "0.6.0-alpha.2" dependencies = [ "anyhow", "base64 0.22.1", - "built", - "fluent-uri", - "infer 0.16.0", - "scratch", "serde", ] @@ -6382,7 +6374,6 @@ dependencies = [ name = "manganis-macro" version = "0.6.0-alpha.2" dependencies = [ - "base64 0.22.1", "proc-macro2", "quote", "serde", @@ -8376,26 +8367,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "ref-cast" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf0a6f84d5f1d581da8b41b47ec8600871962f2a528115b542b362d4b744931" -dependencies = [ - "ref-cast-impl", -] - -[[package]] -name = "ref-cast-impl" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.74", -] - [[package]] name = "regex" version = "1.10.6" diff --git a/Cargo.toml b/Cargo.toml index f45f4a5b17..f7510a6a08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -65,6 +65,7 @@ members = [ "packages/manganis-test-package", "packages/manganis-test-package/test-package-dependency", "packages/manganis-test-package/test-package-nested-dependency", + "packages/document", ] exclude = ["examples/mobile_demo", "examples/openid_connect_demo"] @@ -76,8 +77,10 @@ version = "0.6.0-alpha.2" dioxus = { path = "packages/dioxus", version = "0.6.0-alpha.2" } dioxus-lib = { path = "packages/dioxus-lib", version = "0.6.0-alpha.2" } dioxus-core = { path = "packages/core", version = "0.6.0-alpha.2" } +dioxus-core-types = { path = "packages/core-types", version = "0.6.0-alpha.2" } dioxus-core-macro = { path = "packages/core-macro", version = "0.6.0-alpha.2" } dioxus-config-macro = { path = "packages/config-macro", version = "0.6.0-alpha.2" } +dioxus-document = { path = "packages/document", version = "0.6.0-alpha.2" } dioxus-router = { path = "packages/router", version = "0.6.0-alpha.2" } dioxus-router-macro = { path = "packages/router-macro", version = "0.6.0-alpha.2" } dioxus-html = { path = "packages/html", version = "0.6.0-alpha.2", default-features = false } diff --git a/examples/rsx_usage.rs b/examples/rsx_usage.rs index 3bffe8c700..fb19ca8bfc 100644 --- a/examples/rsx_usage.rs +++ b/examples/rsx_usage.rs @@ -228,6 +228,9 @@ fn app() -> Element { // Or we can shell out to a helper function {format_dollars(10, 50)} + + // some text? + {Some("hello world!")} } } } diff --git a/packages/cli/Cargo.toml b/packages/cli/Cargo.toml index a0f0e6dded..7d1e609e25 100644 --- a/packages/cli/Cargo.toml +++ b/packages/cli/Cargo.toml @@ -90,9 +90,10 @@ brotli = "6.0.0" dioxus-autofmt = { workspace = true } dioxus-check = { workspace = true } rsx-rosetta = { workspace = true } -dioxus-rsx = { workspace = true, features = ["serde", "hot_reload"] } +dioxus-rsx = { workspace = true, features = ["hot_reload"] } dioxus-html = { workspace = true, features = ["hot-reload-context"] } dioxus-core = { workspace = true, features = ["serialize"] } +dioxus-core-types = { workspace = true } dioxus-hot-reload = { workspace = true, features = ["serve"] } ignore = "0.4.22" env_logger = "0.11.3" diff --git a/packages/cli/src/serve/hot_reloading_file_map.rs b/packages/cli/src/serve/hot_reloading_file_map.rs index d34b21400d..6ffe1d1279 100644 --- a/packages/cli/src/serve/hot_reloading_file_map.rs +++ b/packages/cli/src/serve/hot_reloading_file_map.rs @@ -1,7 +1,8 @@ use dioxus_core::internal::{HotReloadTemplateWithLocation, HotReloadedTemplate}; +use dioxus_core_types::HotReloadingContext; use dioxus_rsx::{ hot_reload::{diff_rsx, ChangedRsx}, - CallBody, HotReloadingContext, + CallBody, }; use krates::cm::MetadataCommand; use krates::Cmd; diff --git a/packages/core-types/src/bubbles.rs b/packages/core-types/src/bubbles.rs new file mode 100644 index 0000000000..86f09fdeff --- /dev/null +++ b/packages/core-types/src/bubbles.rs @@ -0,0 +1,100 @@ +/// Check if the event bubbles +/// +/// todo: this should not be in this crate, but this crate is a "root" crate and +/// has zero-deps, meaning it gets compiled before anything else. +/// +/// This function being here means we can use it in the interpreter without pulling in dioxus-html, +/// drastically shortening the crate grpah and thus compile times +/// +/// The real solution to this problem is that events need to mark themselves as "bubbling" or "not bubbling" +/// in their definition, which gets passed as part of the mutations. +pub fn event_bubbles(evt: &str) -> bool { + match evt { + "copy" => true, + "cut" => true, + "paste" => true, + "compositionend" => true, + "compositionstart" => true, + "compositionupdate" => true, + "keydown" => true, + "keypress" => true, + "keyup" => true, + "focus" => false, + "focusout" => true, + "focusin" => true, + "blur" => false, + "change" => true, + "input" => true, + "invalid" => true, + "reset" => true, + "submit" => true, + "click" => true, + "contextmenu" => true, + "doubleclick" => true, + "dblclick" => true, + "drag" => true, + "dragend" => true, + "dragenter" => false, + "dragexit" => false, + "dragleave" => true, + "dragover" => true, + "dragstart" => true, + "drop" => true, + "mousedown" => true, + "mouseenter" => false, + "mouseleave" => false, + "mousemove" => true, + "mouseout" => true, + "scroll" => false, + "mouseover" => true, + "mouseup" => true, + "pointerdown" => true, + "pointermove" => true, + "pointerup" => true, + "pointercancel" => true, + "gotpointercapture" => true, + "lostpointercapture" => true, + "pointerenter" => false, + "pointerleave" => false, + "pointerover" => true, + "pointerout" => true, + "select" => true, + "touchcancel" => true, + "touchend" => true, + "touchmove" => true, + "touchstart" => true, + "wheel" => true, + "abort" => false, + "canplay" => false, + "canplaythrough" => false, + "durationchange" => false, + "emptied" => false, + "encrypted" => true, + "ended" => false, + "error" => false, + "loadeddata" => false, + "loadedmetadata" => false, + "loadstart" => false, + "load" => false, + "pause" => false, + "play" => false, + "playing" => false, + "progress" => false, + "ratechange" => false, + "resize" => false, + "seeked" => false, + "seeking" => false, + "stalled" => false, + "suspend" => false, + "timeupdate" => false, + "volumechange" => false, + "waiting" => false, + "animationstart" => true, + "animationend" => true, + "animationiteration" => true, + "transitionend" => true, + "toggle" => true, + "mounted" => false, + _ => true, + } +} diff --git a/packages/core-types/src/event.rs b/packages/core-types/src/event.rs new file mode 100644 index 0000000000..c8a5ff68a8 --- /dev/null +++ b/packages/core-types/src/event.rs @@ -0,0 +1,203 @@ +use std::{any::Any, cell::RefCell, rc::Rc}; + +/// A wrapper around some generic data that handles the event's state +/// +/// +/// Prevent this event from continuing to bubble up the tree to parent elements. +/// +/// # Example +/// +/// ```rust, no_run +/// # use dioxus::prelude::*; +/// rsx! { +/// button { +/// onclick: move |evt: Event| { +/// evt.stop_propagation(); +/// } +/// } +/// }; +/// ``` +pub struct Event { + /// The data associated with this event + pub data: Rc, + pub(crate) metadata: Rc>, +} + +#[derive(Clone, Copy)] +pub(crate) struct EventMetadata { + pub(crate) propagates: bool, + pub(crate) prevent_default: bool, +} + +impl Event { + /// Create a new event from the inner data + pub fn new(data: Rc, propagates: bool) -> Self { + Self { + data, + metadata: Rc::new(RefCell::new(EventMetadata { + propagates, + prevent_default: false, + })), + } + } +} + +impl Event { + /// Map the event data to a new type + /// + /// # Example + /// + /// ```rust, no_run + /// # use dioxus::prelude::*; + /// rsx! { + /// button { + /// onclick: move |evt: MouseEvent| { + /// let data = evt.map(|data| data.client_coordinates()); + /// println!("{:?}", data.data()); + /// } + /// } + /// }; + /// ``` + pub fn map U>(&self, f: F) -> Event { + Event { + data: Rc::new(f(&self.data)), + metadata: self.metadata.clone(), + } + } + + /// Prevent this event from continuing to bubble up the tree to parent elements. + /// + /// # Example + /// + /// ```rust, no_run + /// # use dioxus::prelude::*; + /// rsx! { + /// button { + /// onclick: move |evt: Event| { + /// # #[allow(deprecated)] + /// evt.cancel_bubble(); + /// } + /// } + /// }; + /// ``` + #[deprecated = "use stop_propagation instead"] + pub fn cancel_bubble(&self) { + self.metadata.borrow_mut().propagates = false; + } + + /// Check if the event propagates up the tree to parent elements + pub fn propagates(&self) -> bool { + self.metadata.borrow().propagates + } + + /// Prevent this event from continuing to bubble up the tree to parent elements. + /// + /// # Example + /// + /// ```rust, no_run + /// # use dioxus::prelude::*; + /// rsx! { + /// button { + /// onclick: move |evt: Event| { + /// evt.stop_propagation(); + /// } + /// } + /// }; + /// ``` + pub fn stop_propagation(&self) { + self.metadata.borrow_mut().propagates = false; + } + + /// Get a reference to the inner data from this event + /// + /// ```rust, no_run + /// # use dioxus::prelude::*; + /// rsx! { + /// button { + /// onclick: move |evt: Event| { + /// let data = evt.data(); + /// async move { + /// println!("{:?}", data); + /// } + /// } + /// } + /// }; + /// ``` + pub fn data(&self) -> Rc { + self.data.clone() + } + + /// Prevent the default action of the event. + /// + /// # Example + /// + /// ```rust + /// # use dioxus::prelude::*; + /// fn App() -> Element { + /// rsx! { + /// a { + /// // You can prevent the default action of the event with `prevent_default` + /// onclick: move |event| { + /// event.prevent_default(); + /// }, + /// href: "https://dioxuslabs.com", + /// "don't go to the link" + /// } + /// } + /// } + /// ``` + /// + /// Note: This must be called synchronously when handling the event. Calling it after the event has been handled will have no effect. + /// + ///
+ /// + /// This method is not available on the LiveView renderer because LiveView handles all events over a websocket which cannot block. + /// + ///
+ #[track_caller] + pub fn prevent_default(&self) { + self.metadata.borrow_mut().prevent_default = true; + } + + /// Check if the default action of the event is enabled. + pub fn default_action_enabled(&self) -> bool { + !self.metadata.borrow().prevent_default + } +} + +impl Event { + /// Downcast this `Event`` to a concrete type + pub fn downcast(self) -> Option> { + let data = self.data.downcast::().ok()?; + Some(Event { + metadata: self.metadata.clone(), + data, + }) + } +} + +impl Clone for Event { + fn clone(&self) -> Self { + Self { + metadata: self.metadata.clone(), + data: self.data.clone(), + } + } +} + +impl std::ops::Deref for Event { + type Target = Rc; + fn deref(&self) -> &Self::Target { + &self.data + } +} + +impl std::fmt::Debug for Event { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("UiEvent") + .field("bubble_state", &self.propagates()) + .field("prevent_default", &!self.default_action_enabled()) + .field("data", &self.data) + .finish() + } +} diff --git a/packages/core-types/src/hr_context.rs b/packages/core-types/src/hr_context.rs new file mode 100644 index 0000000000..c38101093e --- /dev/null +++ b/packages/core-types/src/hr_context.rs @@ -0,0 +1,19 @@ +pub trait HotReloadingContext { + fn map_attribute( + element_name_rust: &str, + attribute_name_rust: &str, + ) -> Option<(&'static str, Option<&'static str>)>; + fn map_element(element_name_rust: &str) -> Option<(&'static str, Option<&'static str>)>; +} + +pub struct Empty; + +impl HotReloadingContext for Empty { + fn map_attribute(_: &str, _: &str) -> Option<(&'static str, Option<&'static str>)> { + None + } + + fn map_element(_: &str) -> Option<(&'static str, Option<&'static str>)> { + None + } +} diff --git a/packages/core-types/src/lib.rs b/packages/core-types/src/lib.rs index ce99d2d0da..5ecf2a4cb1 100644 --- a/packages/core-types/src/lib.rs +++ b/packages/core-types/src/lib.rs @@ -1,2 +1,11 @@ +pub mod bubbles; mod core; +mod event; mod hotreload; +pub mod hr_context; + +pub use bubbles::*; +pub use core::*; +pub use event::*; +pub use hotreload::*; +pub use hr_context::*; diff --git a/packages/core/Cargo.toml b/packages/core/Cargo.toml index 324f665e18..1a2fa16710 100644 --- a/packages/core/Cargo.toml +++ b/packages/core/Cargo.toml @@ -10,6 +10,7 @@ homepage = "https://dioxuslabs.com" keywords = ["dom", "ui", "gui", "react"] [dependencies] +dioxus-core-types = { workspace = true } rustc-hash = { workspace = true } longest-increasing-subsequence = "0.1.0" futures-util = { workspace = true, default-features = false, features = [ @@ -25,7 +26,6 @@ generational-box = { workspace = true } rustversion = "1.0.17" const_format = { workspace = true } warnings = { workspace = true } -manganis = { workspace = true, default-features = false, optional = true } [dev-dependencies] tokio = { workspace = true, features = ["full"] } @@ -48,7 +48,7 @@ features = [ [features] serialize = ["dep:serde"] -manganis = ["dep:manganis"] +# manganis = ["dep:manganis"] [package.metadata.docs.rs] cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"] diff --git a/packages/core/src/events.rs b/packages/core/src/events.rs index 320fb96d9e..bff80468a0 100644 --- a/packages/core/src/events.rs +++ b/packages/core/src/events.rs @@ -2,197 +2,6 @@ use crate::{global_context::current_scope_id, properties::SuperFrom, Runtime, Sc use generational_box::GenerationalBox; use std::{cell::RefCell, marker::PhantomData, rc::Rc}; -/// A wrapper around some generic data that handles the event's state -/// -/// -/// Prevent this event from continuing to bubble up the tree to parent elements. -/// -/// # Example -/// -/// ```rust, no_run -/// # use dioxus::prelude::*; -/// rsx! { -/// button { -/// onclick: move |evt: Event| { -/// evt.stop_propagation(); -/// } -/// } -/// }; -/// ``` -pub struct Event { - /// The data associated with this event - pub data: Rc, - pub(crate) metadata: Rc>, -} - -#[derive(Clone, Copy)] -pub(crate) struct EventMetadata { - pub(crate) propagates: bool, - pub(crate) prevent_default: bool, -} - -impl Event { - /// Create a new event from the inner data - pub fn new(data: Rc, propagates: bool) -> Self { - Self { - data, - metadata: Rc::new(RefCell::new(EventMetadata { - propagates, - prevent_default: false, - })), - } - } -} - -impl Event { - /// Map the event data to a new type - /// - /// # Example - /// - /// ```rust, no_run - /// # use dioxus::prelude::*; - /// rsx! { - /// button { - /// onclick: move |evt: MouseEvent| { - /// let data = evt.map(|data| data.client_coordinates()); - /// println!("{:?}", data.data()); - /// } - /// } - /// }; - /// ``` - pub fn map U>(&self, f: F) -> Event { - Event { - data: Rc::new(f(&self.data)), - metadata: self.metadata.clone(), - } - } - - /// Prevent this event from continuing to bubble up the tree to parent elements. - /// - /// # Example - /// - /// ```rust, no_run - /// # use dioxus::prelude::*; - /// rsx! { - /// button { - /// onclick: move |evt: Event| { - /// # #[allow(deprecated)] - /// evt.cancel_bubble(); - /// } - /// } - /// }; - /// ``` - #[deprecated = "use stop_propagation instead"] - pub fn cancel_bubble(&self) { - self.metadata.borrow_mut().propagates = false; - } - - /// Check if the event propagates up the tree to parent elements - pub fn propagates(&self) -> bool { - self.metadata.borrow().propagates - } - - /// Prevent this event from continuing to bubble up the tree to parent elements. - /// - /// # Example - /// - /// ```rust, no_run - /// # use dioxus::prelude::*; - /// rsx! { - /// button { - /// onclick: move |evt: Event| { - /// evt.stop_propagation(); - /// } - /// } - /// }; - /// ``` - pub fn stop_propagation(&self) { - self.metadata.borrow_mut().propagates = false; - } - - /// Get a reference to the inner data from this event - /// - /// ```rust, no_run - /// # use dioxus::prelude::*; - /// rsx! { - /// button { - /// onclick: move |evt: Event| { - /// let data = evt.data(); - /// async move { - /// println!("{:?}", data); - /// } - /// } - /// } - /// }; - /// ``` - pub fn data(&self) -> Rc { - self.data.clone() - } - - /// Prevent the default action of the event. - /// - /// # Example - /// - /// ```rust - /// # use dioxus::prelude::*; - /// fn App() -> Element { - /// rsx! { - /// a { - /// // You can prevent the default action of the event with `prevent_default` - /// onclick: move |event| { - /// event.prevent_default(); - /// }, - /// href: "https://dioxuslabs.com", - /// "don't go to the link" - /// } - /// } - /// } - /// ``` - /// - /// Note: This must be called synchronously when handling the event. Calling it after the event has been handled will have no effect. - /// - ///
- /// - /// This method is not available on the LiveView renderer because LiveView handles all events over a websocket which cannot block. - /// - ///
- #[track_caller] - pub fn prevent_default(&self) { - self.metadata.borrow_mut().prevent_default = true; - } - - /// Check if the default action of the event is enabled. - pub fn default_action_enabled(&self) -> bool { - !self.metadata.borrow().prevent_default - } -} - -impl Clone for Event { - fn clone(&self) -> Self { - Self { - metadata: self.metadata.clone(), - data: self.data.clone(), - } - } -} - -impl std::ops::Deref for Event { - type Target = Rc; - fn deref(&self) -> &Self::Target { - &self.data - } -} - -impl std::fmt::Debug for Event { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("UiEvent") - .field("bubble_state", &self.propagates()) - .field("prevent_default", &!self.default_action_enabled()) - .field("data", &self.data) - .finish() - } -} - /// The callback type generated by the `rsx!` macro when an `on` field is specified for components. /// /// This makes it possible to pass `move |evt| {}` style closures into components as property fields. diff --git a/packages/core/src/lib.rs b/packages/core/src/lib.rs index 4f2c747c13..c8fc9a7645 100644 --- a/packages/core/src/lib.rs +++ b/packages/core/src/lib.rs @@ -62,6 +62,7 @@ pub(crate) mod innerlude { pub use crate::suspense::*; pub use crate::tasks::*; pub use crate::virtual_dom::*; + pub use dioxus_core_types::*; /// An [`Element`] is a possibly-none [`VNode`] created by calling `render` on [`ScopeId`] or [`ScopeState`]. /// diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index ee809ee4e2..bd82081ce7 100644 --- a/packages/core/src/nodes.rs +++ b/packages/core/src/nodes.rs @@ -7,7 +7,6 @@ use crate::{ properties::ComponentFunction, }; use crate::{Properties, ScopeId, VirtualDom}; -use std::ops::Deref; use std::rc::Rc; use std::vec; use std::{ @@ -15,6 +14,7 @@ use std::{ cell::Cell, fmt::{Arguments, Debug}, }; +use std::{fmt::Display, ops::Deref}; /// The information about the #[derive(Debug)] @@ -773,11 +773,7 @@ impl AttributeValue { // TODO: maybe don't use the copy-variant of EventHandler here? // Maybe, create an Owned variant so we are less likely to run into leaks AttributeValue::Listener(EventHandler::leak(move |event: Event| { - let data = event.data.downcast::().unwrap(); - callback(Event { - metadata: event.metadata.clone(), - data, - }); + callback(event.downcast::().unwrap()); })) } @@ -862,14 +858,7 @@ impl IntoDynNode for DynamicNode { self } } -impl IntoDynNode for Option { - fn into_dyn_node(self) -> DynamicNode { - match self { - Some(val) => val.into_dyn_node(), - None => DynamicNode::default(), - } - } -} + impl IntoDynNode for &Element { fn into_dyn_node(self) -> DynamicNode { match self.as_ref() { @@ -894,25 +883,84 @@ impl IntoDynNode for &Option { } } } -impl IntoDynNode for &str { +pub struct DisplayMarker; +impl IntoDynNode for T +where + T: Display, +{ fn into_dyn_node(self) -> DynamicNode { DynamicNode::Text(VText { value: self.to_string(), }) } } -impl IntoDynNode for String { +pub struct OptionDisplayMarker; +impl IntoDynNode for Option +where + T: Display, +{ fn into_dyn_node(self) -> DynamicNode { - DynamicNode::Text(VText { value: self }) + todo!() + // DynamicNode::Text(VText { + // value: self.to_string(), + // }) } } -impl IntoDynNode for Arguments<'_> { + +impl IntoDynNode for Option { fn into_dyn_node(self) -> DynamicNode { - DynamicNode::Text(VText { - value: self.to_string(), - }) + match self { + Some(val) => val.into_dyn_node(), + None => DynamicNode::default(), + } + } +} + +// // struct DisplayMarker; +// // impl IntoDynNode for Option { +// // fn into_dyn_node(self) -> DynamicNode { +// // todo!() +// // } +// // } + +// impl IntoDynNode for &str { +// fn into_dyn_node(self) -> DynamicNode { +// DynamicNode::Text(VText { +// value: self.to_string(), +// }) +// } +// } +// impl IntoDynNode for String { +// fn into_dyn_node(self) -> DynamicNode { +// DynamicNode::Text(VText { value: self }) +// } +// } +// impl IntoDynNode for Arguments<'_> { +// fn into_dyn_node(self) -> DynamicNode { +// DynamicNode::Text(VText { +// value: self.to_string(), +// }) +// } +// } + +// Note that we're using the E as a generic but this is never crafted anyways. +pub struct FromNodeIterator; +impl IntoDynNode for T +where + T: Iterator, + I: IntoVNode, +{ + fn into_dyn_node(self) -> DynamicNode { + let children: Vec<_> = self.into_iter().map(|node| node.into_vnode()).collect(); + + if children.is_empty() { + DynamicNode::default() + } else { + DynamicNode::Fragment(children) + } } } + impl IntoDynNode for &VNode { fn into_dyn_node(self) -> DynamicNode { DynamicNode::Fragment(vec![self.clone()]) @@ -981,24 +1029,6 @@ impl IntoVNode for &Option { } } -// Note that we're using the E as a generic but this is never crafted anyways. -pub struct FromNodeIterator; -impl IntoDynNode for T -where - T: Iterator, - I: IntoVNode, -{ - fn into_dyn_node(self) -> DynamicNode { - let children: Vec<_> = self.into_iter().map(|node| node.into_vnode()).collect(); - - if children.is_empty() { - DynamicNode::default() - } else { - DynamicNode::Fragment(children) - } - } -} - /// A value that can be converted into an attribute value pub trait IntoAttributeValue { /// Convert into an attribute value @@ -1078,19 +1108,19 @@ impl IntoAttributeValue for Option { } } -#[cfg(feature = "manganis")] -impl IntoAttributeValue for manganis::ImageAsset { - fn into_value(self) -> AttributeValue { - AttributeValue::Text(self.to_string()) - } -} +// #[cfg(feature = "manganis")] +// impl IntoAttributeValue for manganis::ImageAsset { +// fn into_value(self) -> AttributeValue { +// AttributeValue::Text(self.to_string()) +// } +// } -#[cfg(feature = "manganis")] -impl IntoAttributeValue for manganis::Asset { - fn into_value(self) -> AttributeValue { - AttributeValue::Text(self.to_string()) - } -} +// #[cfg(feature = "manganis")] +// impl IntoAttributeValue for manganis::Asset { +// fn into_value(self) -> AttributeValue { +// AttributeValue::Text(self.to_string()) +// } +// } /// A trait for anything that has a dynamic list of attributes pub trait HasAttributes { diff --git a/packages/core/src/runtime.rs b/packages/core/src/runtime.rs index c4aba1e4f0..3c72fdb40c 100644 --- a/packages/core/src/runtime.rs +++ b/packages/core/src/runtime.rs @@ -364,9 +364,7 @@ impl Runtime { self.rendering.set(false); listener.call(uievent.clone()); self.rendering.set(true); - let metadata = uievent.metadata.borrow(); - - if !metadata.propagates { + if !uievent.propagates() { return; } } diff --git a/packages/desktop/Cargo.toml b/packages/desktop/Cargo.toml index a2434407e5..77cc7116ec 100644 --- a/packages/desktop/Cargo.toml +++ b/packages/desktop/Cargo.toml @@ -17,6 +17,7 @@ dioxus-html = { workspace = true, features = [ "document", "file-engine", ] } +dioxus-document = { workspace = true } dioxus-signals = { workspace = true, optional = true } dioxus-interpreter-js = { workspace = true, features = ["binary-protocol", "serialize"] } # dioxus-cli-config = { workspace = true, features = ["read-config"] } diff --git a/packages/desktop/src/document.rs b/packages/desktop/src/document.rs index 707b9d8322..cee204ab33 100644 --- a/packages/desktop/src/document.rs +++ b/packages/desktop/src/document.rs @@ -1,4 +1,4 @@ -use dioxus_html::document::{Document, EvalError, Evaluator}; +use dioxus_document::{Document, EvalError}; use generational_box::{AnyStorage, GenerationalBox, UnsyncStorage}; use crate::{query::Query, DesktopContext}; @@ -15,9 +15,9 @@ impl DesktopDocument { } impl Document for DesktopDocument { - fn new_evaluator(&self, js: String) -> GenerationalBox> { - DesktopEvaluator::create(self.desktop_ctx.clone(), js) - } + // fn new_evaluator(&self, js: String) -> GenerationalBox> { + // DesktopEvaluator::create(self.desktop_ctx.clone(), js) + // } fn set_title(&self, title: String) { self.desktop_ctx.window.set_title(&title); @@ -34,46 +34,46 @@ pub(crate) struct DesktopEvaluator { } impl DesktopEvaluator { - /// Creates a new evaluator for desktop-based targets. - pub fn create(desktop_ctx: DesktopContext, js: String) -> GenerationalBox> { - let ctx = desktop_ctx.clone(); - let query = desktop_ctx.query.new_query(&js, ctx); + // /// Creates a new evaluator for desktop-based targets. + // pub fn create(desktop_ctx: DesktopContext, js: String) -> GenerationalBox> { + // let ctx = desktop_ctx.clone(); + // let query = desktop_ctx.query.new_query(&js, ctx); - // We create a generational box that is owned by the query slot so that when we drop the query slot, the generational box is also dropped. - let owner = UnsyncStorage::owner(); - let query_id = query.id; - let query = owner.insert(Box::new(DesktopEvaluator { query }) as Box); - desktop_ctx.query.active_requests.slab.borrow_mut()[query_id].owner = Some(owner); + // // We create a generational box that is owned by the query slot so that when we drop the query slot, the generational box is also dropped. + // let owner = UnsyncStorage::owner(); + // let query_id = query.id; + // let query = owner.insert(Box::new(DesktopEvaluator { query }) as Box); + // desktop_ctx.query.active_requests.slab.borrow_mut()[query_id].owner = Some(owner); - query - } + // query + // } } -impl Evaluator for DesktopEvaluator { - fn poll_join( - &mut self, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - self.query - .poll_result(cx) - .map_err(|e| EvalError::Communication(e.to_string())) - } +// impl Evaluator for DesktopEvaluator { +// fn poll_join( +// &mut self, +// cx: &mut std::task::Context<'_>, +// ) -> std::task::Poll> { +// self.query +// .poll_result(cx) +// .map_err(|e| EvalError::Communication(e.to_string())) +// } - /// Sends a message to the evaluated JavaScript. - fn send(&self, data: serde_json::Value) -> Result<(), EvalError> { - if let Err(e) = self.query.send(data) { - return Err(EvalError::Communication(e.to_string())); - } - Ok(()) - } +// /// Sends a message to the evaluated JavaScript. +// fn send(&self, data: serde_json::Value) -> Result<(), EvalError> { +// if let Err(e) = self.query.send(data) { +// return Err(EvalError::Communication(e.to_string())); +// } +// Ok(()) +// } - /// Gets an UnboundedReceiver to receive messages from the evaluated JavaScript. - fn poll_recv( - &mut self, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - self.query - .poll_recv(cx) - .map_err(|e| EvalError::Communication(e.to_string())) - } -} +// /// Gets an UnboundedReceiver to receive messages from the evaluated JavaScript. +// fn poll_recv( +// &mut self, +// cx: &mut std::task::Context<'_>, +// ) -> std::task::Poll> { +// self.query +// .poll_recv(cx) +// .map_err(|e| EvalError::Communication(e.to_string())) +// } +// } diff --git a/packages/desktop/src/protocol.rs b/packages/desktop/src/protocol.rs index ee0e70c38c..109e70277a 100644 --- a/packages/desktop/src/protocol.rs +++ b/packages/desktop/src/protocol.rs @@ -1,5 +1,5 @@ use crate::{assets::*, webview::WebviewEdits}; -use dioxus_html::document::NATIVE_EVAL_JS; +use dioxus_document::NATIVE_EVAL_JS; use dioxus_interpreter_js::unified_bindings::SLEDGEHAMMER_JS; use dioxus_interpreter_js::NATIVE_JS; use serde::Deserialize; diff --git a/packages/desktop/src/webview.rs b/packages/desktop/src/webview.rs index 162c841427..4fc4e29fa6 100644 --- a/packages/desktop/src/webview.rs +++ b/packages/desktop/src/webview.rs @@ -8,8 +8,8 @@ use crate::{ DesktopContext, DesktopService, }; use dioxus_core::{Runtime, ScopeId, VirtualDom}; +use dioxus_document::Document; use dioxus_hooks::to_owned; -use dioxus_html::document::Document; use dioxus_html::{HasFileData, HtmlEvent, PlatformEventData}; use dioxus_interpreter_js::SynchronousEventResponse; use futures_util::{pin_mut, FutureExt}; diff --git a/packages/dioxus/Cargo.toml b/packages/dioxus/Cargo.toml index 6e54668fd9..b82eedb72a 100644 --- a/packages/dioxus/Cargo.toml +++ b/packages/dioxus/Cargo.toml @@ -34,7 +34,8 @@ axum = { workspace = true, optional = true } dioxus-hot-reload = { workspace = true, optional = true } [features] -default = ["macro", "html", "hot-reload", "signals", "hooks", "launch", "mounted", "file_engine", "document", "asset"] +default = ["macro", "html", "hot-reload", "signals", "hooks", "launch", "mounted", "document", "asset"] +# default = ["macro", "html", "hot-reload", "signals", "hooks", "launch", "mounted", "file_engine", "document", "asset"] minimal = ["macro", "html", "signals", "hooks", "launch"] signals = ["dep:dioxus-signals"] macro = ["dep:dioxus-core-macro"] @@ -43,7 +44,7 @@ hooks = ["dep:dioxus-hooks"] hot-reload = ["dep:dioxus-hot-reload", "dioxus-web?/hot_reload", "dioxus-fullstack?/hot-reload"] mounted = ["dioxus-web?/mounted", "dioxus-html?/mounted"] file_engine = ["dioxus-web?/file_engine"] -asset = ["dep:manganis", "dioxus-core/manganis"] +asset = ["dep:manganis"] document = ["dioxus-web?/document", "dioxus-html?/document"] launch = ["dep:dioxus-config-macro"] diff --git a/packages/document/Cargo.toml b/packages/document/Cargo.toml new file mode 100644 index 0000000000..530c95efef --- /dev/null +++ b/packages/document/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "dioxus-document" +edition = "2021" +version.workspace = true + +[dependencies] +dioxus-core = { workspace = true } +dioxus-core-types = { workspace = true } +dioxus-core-macro = { workspace = true } +tracing = { workspace = true } diff --git a/packages/html/docs/eval.md b/packages/document/docs/eval.md similarity index 100% rename from packages/html/docs/eval.md rename to packages/document/docs/eval.md diff --git a/packages/html/docs/head.md b/packages/document/docs/head.md similarity index 100% rename from packages/html/docs/head.md rename to packages/document/docs/head.md diff --git a/packages/document/src/eval.rs b/packages/document/src/eval.rs new file mode 100644 index 0000000000..75434c8c05 --- /dev/null +++ b/packages/document/src/eval.rs @@ -0,0 +1,121 @@ +#![allow(clippy::await_holding_refcell_ref)] +#![doc = include_str!("../docs/eval.md")] + +use dioxus_core::prelude::*; +// use generational_box::GenerationalBox; +use std::error::Error; +use std::fmt::Display; +use std::future::{poll_fn, Future, IntoFuture}; +use std::pin::Pin; +use std::rc::Rc; +use std::task::{Context, Poll}; + +use super::{document, Document}; + +// type EvalCreator = Rc UseEval>; + +pub const NATIVE_EVAL_JS: &str = include_str!("../../html/src/js/native_eval.js"); + +// /// Get a struct that can execute any JavaScript. +// /// +// /// # Safety +// /// +// /// Please be very careful with this function. A script with too many dynamic +// /// parts is practically asking for a hacker to find an XSS vulnerability in +// /// it. **This applies especially to web targets, where the JavaScript context +// /// has access to most, if not all of your application data.** +// #[must_use] +// pub fn eval_provider() -> EvalCreator { +// let doc = document(); + +// Rc::new(move |script: &str| UseEval::new(doc.new_evaluator(script.to_string()))) +// as Rc UseEval> +// } + +#[doc = include_str!("../docs/eval.md")] +#[doc(alias = "javascript")] +pub fn eval(script: &str) -> UseEval { + todo!() + // let document = use_hook(document); + // UseEval::new(document.new_evaluator(script.to_string())) +} + +/// A wrapper around the target platform's evaluator that lets you send and receive data from JavaScript spawned by [`eval`]. +/// +#[doc = include_str!("../docs/eval.md")] +#[derive(Clone, Copy)] +pub struct UseEval { + // evaluator: Rc, +} + +// impl UseEval { +// /// Creates a new UseEval +// pub fn new(evaluator: GenerationalBox>) -> Self { +// Self { evaluator } +// } + +// /// Sends a [`serde_json::Value`] to the evaluated JavaScript. +// pub fn send(&self, data: serde_json::Value) -> Result<(), EvalError> { +// match self.evaluator.try_read() { +// Ok(evaluator) => evaluator.send(data), +// Err(_) => Err(EvalError::Finished), +// } +// } + +// /// Gets an UnboundedReceiver to receive messages from the evaluated JavaScript. +// pub async fn recv(&mut self) -> Result { +// poll_fn(|cx| match self.evaluator.try_write() { +// Ok(mut evaluator) => evaluator.poll_recv(cx), +// Err(_) => Poll::Ready(Err(EvalError::Finished)), +// }) +// .await +// } + +// /// Gets the return value of the evaluated JavaScript. +// pub async fn join(self) -> Result { +// poll_fn(|cx| match self.evaluator.try_write() { +// Ok(mut evaluator) => evaluator.poll_join(cx), +// Err(_) => Poll::Ready(Err(EvalError::Finished)), +// }) +// .await +// } +// } + +// impl IntoFuture for UseEval { +// type Output = Result; +// type IntoFuture = Pin>>; + +// fn into_future(self) -> Self::IntoFuture { +// Box::pin(self.join()) +// } +// } + +/// Represents an error when evaluating JavaScript +#[derive(Debug)] +#[non_exhaustive] +pub enum EvalError { + /// The platform does not support evaluating JavaScript. + Unsupported, + + /// The provided JavaScript has already been ran. + Finished, + + /// The provided JavaScript is not valid and can't be ran. + InvalidJs(String), + + /// Represents an error communicating between JavaScript and Rust. + Communication(String), +} + +impl Display for EvalError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + EvalError::Unsupported => write!(f, "EvalError::Unsupported - eval is not supported on the current platform"), + EvalError::Finished => write!(f, "EvalError::Finished - eval has already ran"), + EvalError::InvalidJs(_) => write!(f, "EvalError::InvalidJs - the provided javascript is invalid"), + EvalError::Communication(_) => write!(f, "EvalError::Communication - there was an error trying to communicate with between javascript and rust"), + } + } +} + +impl Error for EvalError {} diff --git a/packages/document/src/head.rs b/packages/document/src/head.rs new file mode 100644 index 0000000000..eb15655ea4 --- /dev/null +++ b/packages/document/src/head.rs @@ -0,0 +1,543 @@ +#![doc = include_str!("../docs/head.md")] + +use std::{cell::RefCell, collections::HashSet, rc::Rc}; + +use dioxus_core::{prelude::*, DynamicNode}; +use dioxus_core_macro::*; + +/// Warn the user if they try to change props on a element that is injected into the head +#[allow(unused)] +fn use_update_warning(value: &T, name: &'static str) { + #[cfg(debug_assertions)] + { + let cloned_value = value.clone(); + let initial = use_hook(move || value.clone()); + + if initial != cloned_value { + tracing::warn!("Changing the props of `{name}` is not supported "); + } + } +} + +fn extract_single_text_node(children: &Element, component: &str) -> Option { + let vnode = match children { + Element::Ok(vnode) => vnode, + Element::Err(err) => { + tracing::error!("Error while rendering {component}: {err}"); + return None; + } + }; + // The title's children must be in one of two forms: + // 1. rsx! { "static text" } + // 2. rsx! { "title: {dynamic_text}" } + match vnode.template { + // rsx! { "static text" } + Template { + roots: &[TemplateNode::Text { text }], + node_paths: &[], + attr_paths: &[], + .. + } => Some(text.to_string()), + // rsx! { "title: {dynamic_text}" } + Template { + roots: &[TemplateNode::Dynamic { id }], + node_paths: &[&[0]], + attr_paths: &[], + .. + } => { + let node = &vnode.dynamic_nodes[id]; + match node { + DynamicNode::Text(text) => Some(text.value.clone()), + _ => { + tracing::error!("Error while rendering {component}: The children of {component} must be a single text node. It cannot be a component, if statement, loop, or a fragment"); + None + } + } + } + _ => { + tracing::error!( + "Error while rendering title: The children of title must be a single text node" + ); + None + } + } +} + +#[derive(Clone, Props, PartialEq)] +pub struct TitleProps { + /// The contents of the title tag. The children must be a single text node. + children: Element, +} + +/// Render the title of the page. On web renderers, this will set the [title](crate::elements::title) in the head. On desktop, it will set the window title. +/// +/// Unlike most head components, the Title can be modified after the first render. Only the latest update to the title will be reflected if multiple title components are rendered. +/// +/// +/// The children of the title component must be a single static or formatted string. If there are more children or the children contain components, conditionals, loops, or fragments, the title will not be updated. +/// +/// # Example +/// +/// ```rust, no_run +/// # use dioxus::prelude::*; +/// fn App() -> Element { +/// rsx! { +/// // You can use the Title component to render a title tag into the head of the page or window +/// Title { "My Page" } +/// } +/// } +/// ``` +#[component] +pub fn Title(props: TitleProps) -> Element { + let children = props.children; + let Some(text) = extract_single_text_node(&children, "Title") else { + return VNode::empty(); + }; + + // Update the title as it changes. NOTE: We don't use use_effect here because we need this to run on the server + let document = use_hook(document); + let last_text = use_hook(|| { + // Set the title initially + document.set_title(text.clone()); + Rc::new(RefCell::new(text.clone())) + }); + + // If the text changes, update the title + let mut last_text = last_text.borrow_mut(); + if text != *last_text { + document.set_title(text.clone()); + *last_text = text; + } + + VNode::empty() +} + +/// Props for the [`Meta`] component +#[derive(Clone, Props, PartialEq)] +pub struct MetaProps { + pub property: Option, + pub name: Option, + pub charset: Option, + pub http_equiv: Option, + pub content: Option, +} + +impl MetaProps { + pub(crate) fn attributes(&self) -> Vec<(&'static str, String)> { + let mut attributes = Vec::new(); + if let Some(property) = &self.property { + attributes.push(("property", property.clone())); + } + if let Some(name) = &self.name { + attributes.push(("name", name.clone())); + } + if let Some(charset) = &self.charset { + attributes.push(("charset", charset.clone())); + } + if let Some(http_equiv) = &self.http_equiv { + attributes.push(("http-equiv", http_equiv.clone())); + } + if let Some(content) = &self.content { + attributes.push(("content", content.clone())); + } + attributes + } +} + +/// Render a [`meta`](crate::elements::meta) tag into the head of the page. +/// +/// # Example +/// +/// ```rust, no_run +/// # use dioxus::prelude::*; +/// fn RedirectToDioxusHomepageWithoutJS() -> Element { +/// rsx! { +/// // You can use the meta component to render a meta tag into the head of the page +/// // This meta tag will redirect the user to the dioxuslabs homepage in 10 seconds +/// Meta { +/// http_equiv: "refresh", +/// content: "10;url=https://dioxuslabs.com", +/// } +/// } +/// } +/// ``` +/// +///
+/// +/// Any updates to the props after the first render will not be reflected in the head. +/// +///
+#[component] +pub fn Meta(props: MetaProps) -> Element { + use_update_warning(&props, "Meta {}"); + + use_hook(|| { + let document = document(); + document.create_meta(props); + }); + + VNode::empty() +} + +#[derive(Clone, Props, PartialEq)] +pub struct ScriptProps { + /// The contents of the script tag. If present, the children must be a single text node. + pub children: Element, + /// Scripts are deduplicated by their src attribute + pub src: Option, + pub defer: Option, + pub crossorigin: Option, + pub fetchpriority: Option, + pub integrity: Option, + pub nomodule: Option, + pub nonce: Option, + pub referrerpolicy: Option, + pub r#type: Option, +} + +impl ScriptProps { + pub(crate) fn attributes(&self) -> Vec<(&'static str, String)> { + let mut attributes = Vec::new(); + if let Some(defer) = &self.defer { + attributes.push(("defer", defer.to_string())); + } + if let Some(crossorigin) = &self.crossorigin { + attributes.push(("crossorigin", crossorigin.clone())); + } + if let Some(fetchpriority) = &self.fetchpriority { + attributes.push(("fetchpriority", fetchpriority.clone())); + } + if let Some(integrity) = &self.integrity { + attributes.push(("integrity", integrity.clone())); + } + if let Some(nomodule) = &self.nomodule { + attributes.push(("nomodule", nomodule.to_string())); + } + if let Some(nonce) = &self.nonce { + attributes.push(("nonce", nonce.clone())); + } + if let Some(referrerpolicy) = &self.referrerpolicy { + attributes.push(("referrerpolicy", referrerpolicy.clone())); + } + if let Some(r#type) = &self.r#type { + attributes.push(("type", r#type.clone())); + } + attributes + } + + pub fn script_contents(&self) -> Option { + extract_single_text_node(&self.children, "Script") + } +} + +/// Render a [`script`](crate::elements::script) tag into the head of the page. +/// +/// +/// If present, the children of the script component must be a single static or formatted string. If there are more children or the children contain components, conditionals, loops, or fragments, the script will not be added. +/// +/// +/// Any scripts you add will be deduplicated by their `src` attribute (if present). +/// +/// # Example +/// ```rust, no_run +/// # use dioxus::prelude::*; +/// fn LoadScript() -> Element { +/// rsx! { +/// // You can use the Script component to render a script tag into the head of the page +/// Script { +/// src: asset!("/assets/script.js"), +/// } +/// } +/// } +/// ``` +/// +///
+/// +/// Any updates to the props after the first render will not be reflected in the head. +/// +///
+#[component] +pub fn Script(props: ScriptProps) -> Element { + use_update_warning(&props, "Script {}"); + + use_hook(|| { + if let Some(src) = &props.src { + if !should_insert_script(src) { + return; + } + } + + let document = document(); + document.create_script(props); + }); + + VNode::empty() +} + +#[derive(Clone, Props, PartialEq)] +pub struct StyleProps { + /// Styles are deduplicated by their href attribute + pub href: Option, + pub media: Option, + pub nonce: Option, + pub title: Option, + /// The contents of the style tag. If present, the children must be a single text node. + pub children: Element, +} + +impl StyleProps { + pub(crate) fn attributes(&self) -> Vec<(&'static str, String)> { + let mut attributes = Vec::new(); + if let Some(href) = &self.href { + attributes.push(("href", href.clone())); + } + if let Some(media) = &self.media { + attributes.push(("media", media.clone())); + } + if let Some(nonce) = &self.nonce { + attributes.push(("nonce", nonce.clone())); + } + if let Some(title) = &self.title { + attributes.push(("title", title.clone())); + } + attributes + } + + pub fn style_contents(&self) -> Option { + extract_single_text_node(&self.children, "Title") + } +} + +/// Render a [`style`](crate::elements::style) tag into the head of the page. +/// +/// If present, the children of the style component must be a single static or formatted string. If there are more children or the children contain components, conditionals, loops, or fragments, the style will not be added. +/// +/// # Example +/// ```rust, no_run +/// # use dioxus::prelude::*; +/// fn RedBackground() -> Element { +/// rsx! { +/// // You can use the style component to render a style tag into the head of the page +/// // This style tag will set the background color of the page to red +/// Style { +/// r#" +/// body {{ +/// background-color: red; +/// }} +/// "# +/// } +/// } +/// } +/// ``` +/// +///
+/// +/// Any updates to the props after the first render will not be reflected in the head. +/// +///
+#[component] +pub fn Style(props: StyleProps) -> Element { + use_update_warning(&props, "Style {}"); + + use_hook(|| { + if let Some(href) = &props.href { + if !should_insert_style(href) { + return; + } + } + let document = document(); + document.create_style(props); + }); + + VNode::empty() +} + +use super::*; + +#[derive(Clone, Props, PartialEq)] +pub struct LinkProps { + pub rel: Option, + pub media: Option, + pub title: Option, + pub disabled: Option, + pub r#as: Option, + pub sizes: Option, + /// Links are deduplicated by their href attribute + pub href: Option, + pub crossorigin: Option, + pub referrerpolicy: Option, + pub fetchpriority: Option, + pub hreflang: Option, + pub integrity: Option, + pub r#type: Option, + pub blocking: Option, +} + +impl LinkProps { + pub(crate) fn attributes(&self) -> Vec<(&'static str, String)> { + let mut attributes = Vec::new(); + if let Some(rel) = &self.rel { + attributes.push(("rel", rel.clone())); + } + if let Some(media) = &self.media { + attributes.push(("media", media.clone())); + } + if let Some(title) = &self.title { + attributes.push(("title", title.clone())); + } + if let Some(disabled) = &self.disabled { + attributes.push(("disabled", disabled.to_string())); + } + if let Some(r#as) = &self.r#as { + attributes.push(("as", r#as.clone())); + } + if let Some(sizes) = &self.sizes { + attributes.push(("sizes", sizes.clone())); + } + if let Some(href) = &self.href { + attributes.push(("href", href.clone())); + } + if let Some(crossorigin) = &self.crossorigin { + attributes.push(("crossOrigin", crossorigin.clone())); + } + if let Some(referrerpolicy) = &self.referrerpolicy { + attributes.push(("referrerPolicy", referrerpolicy.clone())); + } + if let Some(fetchpriority) = &self.fetchpriority { + attributes.push(("fetchPriority", fetchpriority.clone())); + } + if let Some(hreflang) = &self.hreflang { + attributes.push(("hrefLang", hreflang.clone())); + } + if let Some(integrity) = &self.integrity { + attributes.push(("integrity", integrity.clone())); + } + if let Some(r#type) = &self.r#type { + attributes.push(("type", r#type.clone())); + } + if let Some(blocking) = &self.blocking { + attributes.push(("blocking", blocking.clone())); + } + attributes + } +} + +/// Render a [`link`](crate::elements::link) tag into the head of the page. +/// +/// > The [Link](https://docs.rs/dioxus-router/latest/dioxus_router/components/fn.Link.html) component in dioxus router and this component are completely different. +/// > This component links resources in the head of the page, while the router component creates clickable links in the body of the page. +/// +/// # Example +/// ```rust, no_run +/// # use dioxus::prelude::*; +/// fn RedBackground() -> Element { +/// rsx! { +/// // You can use the meta component to render a meta tag into the head of the page +/// // This meta tag will redirect the user to the dioxuslabs homepage in 10 seconds +/// head::Link { +/// href: asset!("/assets/style.css"), +/// rel: "stylesheet", +/// } +/// } +/// } +/// ``` +/// +///
+/// +/// Any updates to the props after the first render will not be reflected in the head. +/// +///
+#[doc(alias = "")] +#[component] +pub fn Link(props: LinkProps) -> Element { + use_update_warning(&props, "Link {}"); + + use_hook(|| { + if let Some(href) = &props.href { + if !should_insert_link(href) { + return; + } + } + let document = document(); + document.create_link(props); + }); + + VNode::empty() +} + +/// Render a `` element with a `rel="stylesheet"` attribute by default. +/// +/// # Example +/// +/// ```rust, no_run +/// # use dioxus::prelude::*; +/// fn App() -> Element { +/// rsx! { +/// Stylesheet { +/// href: "https://example.com/styles.css", +/// } +/// } +/// } +/// ``` +#[doc(alias = "")] +#[component] +pub fn Stylesheet(props: LinkProps) -> Element { + Link(LinkProps { + rel: Some("stylesheet".to_string()), + ..props + }) +} + +fn get_or_insert_root_context() -> T { + match ScopeId::ROOT.has_context::() { + Some(context) => context, + None => { + let context = T::default(); + ScopeId::ROOT.provide_context(context.clone()); + context + } + } +} + +#[derive(Default, Clone)] +struct LinkContext(DeduplicationContext); + +fn should_insert_link(href: &str) -> bool { + get_or_insert_root_context::() + .0 + .should_insert(href) +} + +#[derive(Default, Clone)] +struct ScriptContext(DeduplicationContext); + +fn should_insert_script(src: &str) -> bool { + get_or_insert_root_context::() + .0 + .should_insert(src) +} + +#[derive(Default, Clone)] +struct StyleContext(DeduplicationContext); + +fn should_insert_style(href: &str) -> bool { + get_or_insert_root_context::() + .0 + .should_insert(href) +} + +#[derive(Default, Clone)] +struct DeduplicationContext(Rc>>); + +impl DeduplicationContext { + fn should_insert(&self, href: &str) -> bool { + let mut set = self.0.borrow_mut(); + let present = set.contains(href); + if !present { + set.insert(href.to_string()); + true + } else { + false + } + } +} diff --git a/packages/document/src/lib.rs b/packages/document/src/lib.rs new file mode 100644 index 0000000000..b0ce8c4356 --- /dev/null +++ b/packages/document/src/lib.rs @@ -0,0 +1,87 @@ +use std::rc::Rc; + +mod head; +pub use head::*; + +mod eval; +pub use eval::*; + +/// Get the document provider for the current platform or a no-op provider if the platform doesn't document functionality. +pub fn document() -> Rc { + dioxus_core::prelude::try_consume_context::>() + .expect("A document should exist with this renderer") + // Create a NoOp provider that always logs an error when trying to evaluate + // That way, we can still compile and run the code without a real provider + // .unwrap_or_else(|| Rc::new(NoOpDocument) as Rc) +} + +/// A provider for document-related functionality. By default most methods are driven through [`eval`]. +pub trait Document { + /// Create a new evaluator for the document that evaluates JavaScript and facilitates communication between JavaScript and Rust. + // fn new_evaluator(&self, js: String) -> GenerationalBox>; + fn eval(&self, js: String) -> Result<(), EvalError> { + todo!() + } + + /// Set the title of the document + fn set_title(&self, title: String) { + self.eval(format!("document.title = {title:?};")); + } + + /// Create a new meta tag + fn create_meta(&self, props: MetaProps) { + let attributes = props.attributes(); + let js = create_element_in_head("meta", &attributes, None); + self.eval(js); + } + + /// Create a new script tag + fn create_script(&self, props: ScriptProps) { + let attributes = props.attributes(); + let js = create_element_in_head("script", &attributes, props.script_contents()); + self.eval(js); + } + + /// Create a new style tag + fn create_style(&self, props: StyleProps) { + let attributes = props.attributes(); + let js = create_element_in_head("style", &attributes, props.style_contents()); + self.eval(js); + } + + /// Create a new link tag + fn create_link(&self, props: head::LinkProps) { + let attributes = props.attributes(); + let js = create_element_in_head("link", &attributes, None); + self.eval(js); + } + + /// Get a reference to the document as `dyn Any` + fn as_any(&self) -> &dyn std::any::Any; +} + +fn format_attributes(attributes: &[(&str, String)]) -> String { + let mut formatted = String::from("["); + for (key, value) in attributes { + formatted.push_str(&format!("[{key:?}, {value:?}],")); + } + if formatted.ends_with(',') { + formatted.pop(); + } + formatted.push(']'); + formatted +} + +fn create_element_in_head( + tag: &str, + attributes: &[(&str, String)], + children: Option, +) -> String { + let helpers: &str = todo!(); + // let helpers = include_str!("../js/head.js"); + let attributes = format_attributes(attributes); + let children = children + .map(|c| format!("\"{c}\"")) + .unwrap_or("null".to_string()); + format!(r#"{helpers};window.createElementInHead("{tag}", {attributes}, {children});"#) +} diff --git a/packages/fullstack/Cargo.toml b/packages/fullstack/Cargo.toml index 20cbc6f304..f4629f3a62 100644 --- a/packages/fullstack/Cargo.toml +++ b/packages/fullstack/Cargo.toml @@ -51,6 +51,8 @@ ciborium = { workspace = true } base64 = { workspace = true } rustls = { workspace = true, optional = true } hyper-rustls = { workspace = true, optional = true } +dioxus-document = { workspace = true } +dioxus-core-types = { workspace = true } pin-project = { version = "1.1.2", optional = true } thiserror = { workspace = true, optional = true } @@ -76,11 +78,11 @@ tokio = { workspace = true, features = ["rt", "sync", "rt-multi-thread"], option dioxus = { workspace = true, features = ["fullstack"] } [features] -default = ["hot-reload", "panic_hook", "document", "file_engine", "mounted"] +default = ["hot-reload", "panic_hook", "document", "mounted"] panic_hook = ["dioxus-web?/panic_hook"] hot-reload = ["dioxus-web?/hot_reload", "dioxus-hot-reload/serve"] mounted = ["dioxus-web?/mounted"] -file_engine = ["dioxus-web?/file_engine"] +# file_engine = ["dioxus-web?/file_engine"] document = ["dioxus-web?/document"] web = ["dep:dioxus-web", "dep:web-sys"] desktop = ["dep:dioxus-desktop", "server_fn/reqwest", "dioxus_server_macro/reqwest"] diff --git a/packages/fullstack/src/document/web.rs b/packages/fullstack/src/document/web.rs index 3169fa05a3..eafeb149ee 100644 --- a/packages/fullstack/src/document/web.rs +++ b/packages/fullstack/src/document/web.rs @@ -1,7 +1,7 @@ #![allow(unused)] //! On the client, we use the [`WebDocument`] implementation to render the head for any elements that were not rendered on the server. -use dioxus_lib::events::Document; +use dioxus_document::{Document, LinkProps, MetaProps, ScriptProps, StyleProps}; use dioxus_web::WebDocument; fn head_element_written_on_server() -> bool { @@ -14,13 +14,6 @@ fn head_element_written_on_server() -> bool { pub(crate) struct FullstackWebDocument; impl Document for FullstackWebDocument { - fn new_evaluator( - &self, - js: String, - ) -> generational_box::GenerationalBox> { - WebDocument.new_evaluator(js) - } - fn set_title(&self, title: String) { if head_element_written_on_server() { return; @@ -28,28 +21,28 @@ impl Document for FullstackWebDocument { WebDocument.set_title(title); } - fn create_meta(&self, props: dioxus_lib::prelude::MetaProps) { + fn create_meta(&self, props: MetaProps) { if head_element_written_on_server() { return; } WebDocument.create_meta(props); } - fn create_script(&self, props: dioxus_lib::prelude::ScriptProps) { + fn create_script(&self, props: ScriptProps) { if head_element_written_on_server() { return; } WebDocument.create_script(props); } - fn create_style(&self, props: dioxus_lib::prelude::StyleProps) { + fn create_style(&self, props: StyleProps) { if head_element_written_on_server() { return; } WebDocument.create_style(props); } - fn create_link(&self, props: dioxus_lib::prelude::head::LinkProps) { + fn create_link(&self, props: LinkProps) { if head_element_written_on_server() { return; } diff --git a/packages/fullstack/src/launch.rs b/packages/fullstack/src/launch.rs index cbf73dc544..e13ab1429c 100644 --- a/packages/fullstack/src/launch.rs +++ b/packages/fullstack/src/launch.rs @@ -63,7 +63,7 @@ pub fn launch( let factory = move || { let mut vdom = factory(); let document = std::rc::Rc::new(crate::document::web::FullstackWebDocument) - as std::rc::Rc; + as std::rc::Rc; vdom.provide_root_context(document); vdom }; diff --git a/packages/hot-reload/Cargo.toml b/packages/hot-reload/Cargo.toml index 5ef5e6606a..14b3310380 100644 --- a/packages/hot-reload/Cargo.toml +++ b/packages/hot-reload/Cargo.toml @@ -10,7 +10,7 @@ description = "Hot reloading utilities for Dioxus" keywords = ["dom", "ui", "gui", "react", "hot-reloading"] [dependencies] -dioxus-rsx = { workspace = true, features = ["serde"] } +dioxus-rsx = { workspace = true } dioxus-core = { workspace = true, features = ["serialize"] } dioxus-html = { workspace = true, optional = true } dioxus-signals = { workspace = true, optional = true } @@ -19,8 +19,6 @@ dioxus-cli-config = { workspace = true, optional = true, features = ["read-confi serde = { version = "1", features = ["derive"] } # hot reloading serve -futures-util = { workspace = true, features = ["async-await-macro"], optional = true } -tokio = { workspace = true, features = ["sync", "rt-multi-thread"], optional = true } tracing = { workspace = true } warnings.workspace = true @@ -46,7 +44,7 @@ serde_json = "1.0.91" [features] default = [] client = ["dep:dioxus-signals"] -serve = ["dep:futures-util", "dep:tokio", "dep:dioxus-cli-config"] +serve = ["dep:dioxus-cli-config"] # serve = ["dep:tokio-stream", "dep:futures-util", "dep:tokio", "dep:tokio-tungstenite", "dep:dioxus-cli-config"] [package.metadata.docs.rs] diff --git a/packages/hot-reload/src/ws_receiver.rs b/packages/hot-reload/src/ws_receiver.rs index 4023da5f74..e8866d02d9 100644 --- a/packages/hot-reload/src/ws_receiver.rs +++ b/packages/hot-reload/src/ws_receiver.rs @@ -1,21 +1,29 @@ use crate::DevserverMsg; -// use futures_util::{SinkExt, StreamExt}; -// use tokio::net::TcpStream; -// use tokio_tungstenite::{ -// tungstenite::{Message, Result as TtResult}, -// MaybeTlsStream, WebSocketStream, -// }; +use std::io::Read; pub fn connect(mut callback: impl FnMut(DevserverMsg) + Send + 'static) { - tokio::spawn(async move { - // let Some(Ok(mut recv)) = NativeReceiver::create_from_cli().await else { - // return; - // }; - // while let Some(msg) = recv.next().await { - // match msg { - // Ok(msg) => callback(msg), - // Err(_e) => {} + // Hi! + // + // yes, we read-raw from a tcp socket + // don't think about it too much :) + // + // we just don't want to bring in tls + tokio for just hotreloading + std::thread::spawn(move || { + let connect = std::net::TcpStream::connect("127.0.0.1:8080"); + let Ok(mut stream) = connect else { + return; + }; + + loop {} + + // let mut buf = [0; 1024]; + // loop { + // let len = stream.read(&mut buf).unwrap(); + // if len == 0 { + // break; // } + // let msg = String::from_utf8_lossy(&buf[..len]); + // callback(serde_json::from_str(&msg).unwrap()); // } }); } diff --git a/packages/html/Cargo.toml b/packages/html/Cargo.toml index 41dfbbfd5d..af86a18385 100644 --- a/packages/html/Cargo.toml +++ b/packages/html/Cargo.toml @@ -11,11 +11,11 @@ keywords = ["dom", "ui", "gui", "react"] [dependencies] dioxus-core = { workspace = true } -dioxus-core-macro = { workspace = true } +dioxus-core-types = { workspace = true } dioxus-rsx = { workspace = true, optional = true } dioxus-html-internal-macro = { workspace = true } -dioxus-hooks = { workspace = true } -generational-box = { workspace = true } + +# generational-box = { workspace = true } serde = { version = "1", features = ["derive"], optional = true } serde_repr = { version = "0.1", optional = true } euclid = "0.22.7" @@ -25,7 +25,6 @@ async-trait = { version = "0.1.58", optional = true } serde_json = { version = "1", optional = true } tracing.workspace = true -tokio = { workspace = true, features = ["fs", "io-util"], optional = true } futures-channel = { workspace = true } # todo: we shouldn't have any wasm-bindgen dependencies in the html cratea diff --git a/packages/html/build.rs b/packages/html/build.rs deleted file mode 100644 index 88da265a46..0000000000 --- a/packages/html/build.rs +++ /dev/null @@ -1,9 +0,0 @@ -fn main() { - // If any TS files change, re-run the build script - lazy_js_bundle::LazyTypeScriptBindings::new() - .with_watching("./src/ts") - .with_binding("./src/ts/eval.ts", "./src/js/eval.js") - .with_binding("./src/ts/native_eval.ts", "./src/js/native_eval.js") - .with_binding("./src/ts/head.ts", "./src/js/head.js") - .run(); -} diff --git a/packages/html/src/document/eval.rs b/packages/html/src/document/eval.rs deleted file mode 100644 index cd9d2aa0f6..0000000000 --- a/packages/html/src/document/eval.rs +++ /dev/null @@ -1,134 +0,0 @@ -#![allow(clippy::await_holding_refcell_ref)] -#![doc = include_str!("../../docs/eval.md")] - -use dioxus_core::prelude::*; -use generational_box::GenerationalBox; -use std::error::Error; -use std::fmt::Display; -use std::future::{poll_fn, Future, IntoFuture}; -use std::pin::Pin; -use std::rc::Rc; -use std::task::{Context, Poll}; - -use super::document; - -/// The platform's evaluator. -pub trait Evaluator { - /// Sends a message to the evaluated JavaScript. - fn send(&self, data: serde_json::Value) -> Result<(), EvalError>; - /// Receive any queued messages from the evaluated JavaScript. - fn poll_recv( - &mut self, - context: &mut Context<'_>, - ) -> Poll>; - /// Gets the return value of the JavaScript - fn poll_join( - &mut self, - context: &mut Context<'_>, - ) -> Poll>; -} - -type EvalCreator = Rc UseEval>; - -/// Get a struct that can execute any JavaScript. -/// -/// # Safety -/// -/// Please be very careful with this function. A script with too many dynamic -/// parts is practically asking for a hacker to find an XSS vulnerability in -/// it. **This applies especially to web targets, where the JavaScript context -/// has access to most, if not all of your application data.** -#[must_use] -pub fn eval_provider() -> EvalCreator { - let eval_provider = document(); - - Rc::new(move |script: &str| UseEval::new(eval_provider.new_evaluator(script.to_string()))) - as Rc UseEval> -} - -#[doc = include_str!("../../docs/eval.md")] -#[doc(alias = "javascript")] -pub fn eval(script: &str) -> UseEval { - let document = use_hook(document); - UseEval::new(document.new_evaluator(script.to_string())) -} - -/// A wrapper around the target platform's evaluator that lets you send and receive data from JavaScript spawned by [`eval`]. -/// -#[doc = include_str!("../../docs/eval.md")] -#[derive(Clone, Copy)] -pub struct UseEval { - evaluator: GenerationalBox>, -} - -impl UseEval { - /// Creates a new UseEval - pub fn new(evaluator: GenerationalBox>) -> Self { - Self { evaluator } - } - - /// Sends a [`serde_json::Value`] to the evaluated JavaScript. - pub fn send(&self, data: serde_json::Value) -> Result<(), EvalError> { - match self.evaluator.try_read() { - Ok(evaluator) => evaluator.send(data), - Err(_) => Err(EvalError::Finished), - } - } - - /// Gets an UnboundedReceiver to receive messages from the evaluated JavaScript. - pub async fn recv(&mut self) -> Result { - poll_fn(|cx| match self.evaluator.try_write() { - Ok(mut evaluator) => evaluator.poll_recv(cx), - Err(_) => Poll::Ready(Err(EvalError::Finished)), - }) - .await - } - - /// Gets the return value of the evaluated JavaScript. - pub async fn join(self) -> Result { - poll_fn(|cx| match self.evaluator.try_write() { - Ok(mut evaluator) => evaluator.poll_join(cx), - Err(_) => Poll::Ready(Err(EvalError::Finished)), - }) - .await - } -} - -impl IntoFuture for UseEval { - type Output = Result; - type IntoFuture = Pin>>; - - fn into_future(self) -> Self::IntoFuture { - Box::pin(self.join()) - } -} - -/// Represents an error when evaluating JavaScript -#[derive(Debug)] -#[non_exhaustive] -pub enum EvalError { - /// The platform does not support evaluating JavaScript. - Unsupported, - - /// The provided JavaScript has already been ran. - Finished, - - /// The provided JavaScript is not valid and can't be ran. - InvalidJs(String), - - /// Represents an error communicating between JavaScript and Rust. - Communication(String), -} - -impl Display for EvalError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - EvalError::Unsupported => write!(f, "EvalError::Unsupported - eval is not supported on the current platform"), - EvalError::Finished => write!(f, "EvalError::Finished - eval has already ran"), - EvalError::InvalidJs(_) => write!(f, "EvalError::InvalidJs - the provided javascript is invalid"), - EvalError::Communication(_) => write!(f, "EvalError::Communication - there was an error trying to communicate with between javascript and rust"), - } - } -} - -impl Error for EvalError {} diff --git a/packages/html/src/document/head.rs b/packages/html/src/document/head.rs index 2700ba67e1..8b13789179 100644 --- a/packages/html/src/document/head.rs +++ b/packages/html/src/document/head.rs @@ -1,543 +1 @@ -#![doc = include_str!("../../docs/head.md")] -use std::{cell::RefCell, collections::HashSet, rc::Rc}; - -use dioxus_core::{prelude::*, DynamicNode}; -use dioxus_core_macro::*; - -/// Warn the user if they try to change props on a element that is injected into the head -#[allow(unused)] -fn use_update_warning(value: &T, name: &'static str) { - #[cfg(debug_assertions)] - { - let cloned_value = value.clone(); - let initial = use_hook(move || value.clone()); - - if initial != cloned_value { - tracing::warn!("Changing the props of `{name}` is not supported "); - } - } -} - -fn extract_single_text_node(children: &Element, component: &str) -> Option { - let vnode = match children { - Element::Ok(vnode) => vnode, - Element::Err(err) => { - tracing::error!("Error while rendering {component}: {err}"); - return None; - } - }; - // The title's children must be in one of two forms: - // 1. rsx! { "static text" } - // 2. rsx! { "title: {dynamic_text}" } - match vnode.template { - // rsx! { "static text" } - Template { - roots: &[TemplateNode::Text { text }], - node_paths: &[], - attr_paths: &[], - .. - } => Some(text.to_string()), - // rsx! { "title: {dynamic_text}" } - Template { - roots: &[TemplateNode::Dynamic { id }], - node_paths: &[&[0]], - attr_paths: &[], - .. - } => { - let node = &vnode.dynamic_nodes[id]; - match node { - DynamicNode::Text(text) => Some(text.value.clone()), - _ => { - tracing::error!("Error while rendering {component}: The children of {component} must be a single text node. It cannot be a component, if statement, loop, or a fragment"); - None - } - } - } - _ => { - tracing::error!( - "Error while rendering title: The children of title must be a single text node" - ); - None - } - } -} - -#[derive(Clone, Props, PartialEq)] -pub struct TitleProps { - /// The contents of the title tag. The children must be a single text node. - children: Element, -} - -/// Render the title of the page. On web renderers, this will set the [title](crate::elements::title) in the head. On desktop, it will set the window title. -/// -/// Unlike most head components, the Title can be modified after the first render. Only the latest update to the title will be reflected if multiple title components are rendered. -/// -/// -/// The children of the title component must be a single static or formatted string. If there are more children or the children contain components, conditionals, loops, or fragments, the title will not be updated. -/// -/// # Example -/// -/// ```rust, no_run -/// # use dioxus::prelude::*; -/// fn App() -> Element { -/// rsx! { -/// // You can use the Title component to render a title tag into the head of the page or window -/// Title { "My Page" } -/// } -/// } -/// ``` -#[component] -pub fn Title(props: TitleProps) -> Element { - let children = props.children; - let Some(text) = extract_single_text_node(&children, "Title") else { - return VNode::empty(); - }; - - // Update the title as it changes. NOTE: We don't use use_effect here because we need this to run on the server - let document = use_hook(document); - let last_text = use_hook(|| { - // Set the title initially - document.set_title(text.clone()); - Rc::new(RefCell::new(text.clone())) - }); - - // If the text changes, update the title - let mut last_text = last_text.borrow_mut(); - if text != *last_text { - document.set_title(text.clone()); - *last_text = text; - } - - VNode::empty() -} - -/// Props for the [`Meta`] component -#[derive(Clone, Props, PartialEq)] -pub struct MetaProps { - pub property: Option, - pub name: Option, - pub charset: Option, - pub http_equiv: Option, - pub content: Option, -} - -impl MetaProps { - pub(crate) fn attributes(&self) -> Vec<(&'static str, String)> { - let mut attributes = Vec::new(); - if let Some(property) = &self.property { - attributes.push(("property", property.clone())); - } - if let Some(name) = &self.name { - attributes.push(("name", name.clone())); - } - if let Some(charset) = &self.charset { - attributes.push(("charset", charset.clone())); - } - if let Some(http_equiv) = &self.http_equiv { - attributes.push(("http-equiv", http_equiv.clone())); - } - if let Some(content) = &self.content { - attributes.push(("content", content.clone())); - } - attributes - } -} - -/// Render a [`meta`](crate::elements::meta) tag into the head of the page. -/// -/// # Example -/// -/// ```rust, no_run -/// # use dioxus::prelude::*; -/// fn RedirectToDioxusHomepageWithoutJS() -> Element { -/// rsx! { -/// // You can use the meta component to render a meta tag into the head of the page -/// // This meta tag will redirect the user to the dioxuslabs homepage in 10 seconds -/// Meta { -/// http_equiv: "refresh", -/// content: "10;url=https://dioxuslabs.com", -/// } -/// } -/// } -/// ``` -/// -///
-/// -/// Any updates to the props after the first render will not be reflected in the head. -/// -///
-#[component] -pub fn Meta(props: MetaProps) -> Element { - use_update_warning(&props, "Meta {}"); - - use_hook(|| { - let document = document(); - document.create_meta(props); - }); - - VNode::empty() -} - -#[derive(Clone, Props, PartialEq)] -pub struct ScriptProps { - /// The contents of the script tag. If present, the children must be a single text node. - pub children: Element, - /// Scripts are deduplicated by their src attribute - pub src: Option, - pub defer: Option, - pub crossorigin: Option, - pub fetchpriority: Option, - pub integrity: Option, - pub nomodule: Option, - pub nonce: Option, - pub referrerpolicy: Option, - pub r#type: Option, -} - -impl ScriptProps { - pub(crate) fn attributes(&self) -> Vec<(&'static str, String)> { - let mut attributes = Vec::new(); - if let Some(defer) = &self.defer { - attributes.push(("defer", defer.to_string())); - } - if let Some(crossorigin) = &self.crossorigin { - attributes.push(("crossorigin", crossorigin.clone())); - } - if let Some(fetchpriority) = &self.fetchpriority { - attributes.push(("fetchpriority", fetchpriority.clone())); - } - if let Some(integrity) = &self.integrity { - attributes.push(("integrity", integrity.clone())); - } - if let Some(nomodule) = &self.nomodule { - attributes.push(("nomodule", nomodule.to_string())); - } - if let Some(nonce) = &self.nonce { - attributes.push(("nonce", nonce.clone())); - } - if let Some(referrerpolicy) = &self.referrerpolicy { - attributes.push(("referrerpolicy", referrerpolicy.clone())); - } - if let Some(r#type) = &self.r#type { - attributes.push(("type", r#type.clone())); - } - attributes - } - - pub fn script_contents(&self) -> Option { - extract_single_text_node(&self.children, "Script") - } -} - -/// Render a [`script`](crate::elements::script) tag into the head of the page. -/// -/// -/// If present, the children of the script component must be a single static or formatted string. If there are more children or the children contain components, conditionals, loops, or fragments, the script will not be added. -/// -/// -/// Any scripts you add will be deduplicated by their `src` attribute (if present). -/// -/// # Example -/// ```rust, no_run -/// # use dioxus::prelude::*; -/// fn LoadScript() -> Element { -/// rsx! { -/// // You can use the Script component to render a script tag into the head of the page -/// Script { -/// src: asset!("/assets/script.js"), -/// } -/// } -/// } -/// ``` -/// -///
-/// -/// Any updates to the props after the first render will not be reflected in the head. -/// -///
-#[component] -pub fn Script(props: ScriptProps) -> Element { - use_update_warning(&props, "Script {}"); - - use_hook(|| { - if let Some(src) = &props.src { - if !should_insert_script(src) { - return; - } - } - - let document = document(); - document.create_script(props); - }); - - VNode::empty() -} - -#[derive(Clone, Props, PartialEq)] -pub struct StyleProps { - /// Styles are deduplicated by their href attribute - pub href: Option, - pub media: Option, - pub nonce: Option, - pub title: Option, - /// The contents of the style tag. If present, the children must be a single text node. - pub children: Element, -} - -impl StyleProps { - pub(crate) fn attributes(&self) -> Vec<(&'static str, String)> { - let mut attributes = Vec::new(); - if let Some(href) = &self.href { - attributes.push(("href", href.clone())); - } - if let Some(media) = &self.media { - attributes.push(("media", media.clone())); - } - if let Some(nonce) = &self.nonce { - attributes.push(("nonce", nonce.clone())); - } - if let Some(title) = &self.title { - attributes.push(("title", title.clone())); - } - attributes - } - - pub fn style_contents(&self) -> Option { - extract_single_text_node(&self.children, "Title") - } -} - -/// Render a [`style`](crate::elements::style) tag into the head of the page. -/// -/// If present, the children of the style component must be a single static or formatted string. If there are more children or the children contain components, conditionals, loops, or fragments, the style will not be added. -/// -/// # Example -/// ```rust, no_run -/// # use dioxus::prelude::*; -/// fn RedBackground() -> Element { -/// rsx! { -/// // You can use the style component to render a style tag into the head of the page -/// // This style tag will set the background color of the page to red -/// Style { -/// r#" -/// body {{ -/// background-color: red; -/// }} -/// "# -/// } -/// } -/// } -/// ``` -/// -///
-/// -/// Any updates to the props after the first render will not be reflected in the head. -/// -///
-#[component] -pub fn Style(props: StyleProps) -> Element { - use_update_warning(&props, "Style {}"); - - use_hook(|| { - if let Some(href) = &props.href { - if !should_insert_style(href) { - return; - } - } - let document = document(); - document.create_style(props); - }); - - VNode::empty() -} - -use super::*; - -#[derive(Clone, Props, PartialEq)] -pub struct LinkProps { - pub rel: Option, - pub media: Option, - pub title: Option, - pub disabled: Option, - pub r#as: Option, - pub sizes: Option, - /// Links are deduplicated by their href attribute - pub href: Option, - pub crossorigin: Option, - pub referrerpolicy: Option, - pub fetchpriority: Option, - pub hreflang: Option, - pub integrity: Option, - pub r#type: Option, - pub blocking: Option, -} - -impl LinkProps { - pub(crate) fn attributes(&self) -> Vec<(&'static str, String)> { - let mut attributes = Vec::new(); - if let Some(rel) = &self.rel { - attributes.push(("rel", rel.clone())); - } - if let Some(media) = &self.media { - attributes.push(("media", media.clone())); - } - if let Some(title) = &self.title { - attributes.push(("title", title.clone())); - } - if let Some(disabled) = &self.disabled { - attributes.push(("disabled", disabled.to_string())); - } - if let Some(r#as) = &self.r#as { - attributes.push(("as", r#as.clone())); - } - if let Some(sizes) = &self.sizes { - attributes.push(("sizes", sizes.clone())); - } - if let Some(href) = &self.href { - attributes.push(("href", href.clone())); - } - if let Some(crossorigin) = &self.crossorigin { - attributes.push(("crossOrigin", crossorigin.clone())); - } - if let Some(referrerpolicy) = &self.referrerpolicy { - attributes.push(("referrerPolicy", referrerpolicy.clone())); - } - if let Some(fetchpriority) = &self.fetchpriority { - attributes.push(("fetchPriority", fetchpriority.clone())); - } - if let Some(hreflang) = &self.hreflang { - attributes.push(("hrefLang", hreflang.clone())); - } - if let Some(integrity) = &self.integrity { - attributes.push(("integrity", integrity.clone())); - } - if let Some(r#type) = &self.r#type { - attributes.push(("type", r#type.clone())); - } - if let Some(blocking) = &self.blocking { - attributes.push(("blocking", blocking.clone())); - } - attributes - } -} - -/// Render a [`link`](crate::elements::link) tag into the head of the page. -/// -/// > The [Link](https://docs.rs/dioxus-router/latest/dioxus_router/components/fn.Link.html) component in dioxus router and this component are completely different. -/// > This component links resources in the head of the page, while the router component creates clickable links in the body of the page. -/// -/// # Example -/// ```rust, no_run -/// # use dioxus::prelude::*; -/// fn RedBackground() -> Element { -/// rsx! { -/// // You can use the meta component to render a meta tag into the head of the page -/// // This meta tag will redirect the user to the dioxuslabs homepage in 10 seconds -/// head::Link { -/// href: asset!("/assets/style.css"), -/// rel: "stylesheet", -/// } -/// } -/// } -/// ``` -/// -///
-/// -/// Any updates to the props after the first render will not be reflected in the head. -/// -///
-#[doc(alias = "")] -#[component] -pub fn Link(props: LinkProps) -> Element { - use_update_warning(&props, "Link {}"); - - use_hook(|| { - if let Some(href) = &props.href { - if !should_insert_link(href) { - return; - } - } - let document = document(); - document.create_link(props); - }); - - VNode::empty() -} - -/// Render a `` element with a `rel="stylesheet"` attribute by default. -/// -/// # Example -/// -/// ```rust, no_run -/// # use dioxus::prelude::*; -/// fn App() -> Element { -/// rsx! { -/// Stylesheet { -/// href: "https://example.com/styles.css", -/// } -/// } -/// } -/// ``` -#[doc(alias = "")] -#[component] -pub fn Stylesheet(props: LinkProps) -> Element { - Link(LinkProps { - rel: Some("stylesheet".to_string()), - ..props - }) -} - -fn get_or_insert_root_context() -> T { - match ScopeId::ROOT.has_context::() { - Some(context) => context, - None => { - let context = T::default(); - ScopeId::ROOT.provide_context(context.clone()); - context - } - } -} - -#[derive(Default, Clone)] -struct LinkContext(DeduplicationContext); - -fn should_insert_link(href: &str) -> bool { - get_or_insert_root_context::() - .0 - .should_insert(href) -} - -#[derive(Default, Clone)] -struct ScriptContext(DeduplicationContext); - -fn should_insert_script(src: &str) -> bool { - get_or_insert_root_context::() - .0 - .should_insert(src) -} - -#[derive(Default, Clone)] -struct StyleContext(DeduplicationContext); - -fn should_insert_style(href: &str) -> bool { - get_or_insert_root_context::() - .0 - .should_insert(href) -} - -#[derive(Default, Clone)] -struct DeduplicationContext(Rc>>); - -impl DeduplicationContext { - fn should_insert(&self, href: &str) -> bool { - let mut set = self.0.borrow_mut(); - let present = set.contains(href); - if !present { - set.insert(href.to_string()); - true - } else { - false - } - } -} diff --git a/packages/html/src/document/mod.rs b/packages/html/src/document/mod.rs index 6e6c498a84..9e34b33cef 100644 --- a/packages/html/src/document/mod.rs +++ b/packages/html/src/document/mod.rs @@ -5,85 +5,16 @@ use std::{ task::{Context, Poll}, }; -use generational_box::{AnyStorage, GenerationalBox, UnsyncStorage}; +// use generational_box::{AnyStorage, GenerationalBox, UnsyncStorage}; -mod bindings; -#[allow(unused)] -pub use bindings::*; -mod eval; -pub use eval::*; +// mod bindings; +// #[allow(unused)] +// pub use bindings::*; -pub mod head; -pub use head::{ - Meta, MetaProps, Script, ScriptProps, Style, StyleProps, Stylesheet, Title, TitleProps, -}; - -fn format_attributes(attributes: &[(&str, String)]) -> String { - let mut formatted = String::from("["); - for (key, value) in attributes { - formatted.push_str(&format!("[{key:?}, {value:?}],")); - } - if formatted.ends_with(',') { - formatted.pop(); - } - formatted.push(']'); - formatted -} - -fn create_element_in_head( - tag: &str, - attributes: &[(&str, String)], - children: Option, -) -> String { - let helpers = include_str!("../js/head.js"); - let attributes = format_attributes(attributes); - let children = children - .map(|c| format!("\"{c}\"")) - .unwrap_or("null".to_string()); - format!(r#"{helpers};window.createElementInHead("{tag}", {attributes}, {children});"#) -} - -/// A provider for document-related functionality. By default most methods are driven through [`eval`]. -pub trait Document { - /// Create a new evaluator for the document that evaluates JavaScript and facilitates communication between JavaScript and Rust. - fn new_evaluator(&self, js: String) -> GenerationalBox>; - - /// Set the title of the document - fn set_title(&self, title: String) { - self.new_evaluator(format!("document.title = {title:?};")); - } - - /// Create a new meta tag - fn create_meta(&self, props: MetaProps) { - let attributes = props.attributes(); - let js = create_element_in_head("meta", &attributes, None); - self.new_evaluator(js); - } - - /// Create a new script tag - fn create_script(&self, props: ScriptProps) { - let attributes = props.attributes(); - let js = create_element_in_head("script", &attributes, props.script_contents()); - self.new_evaluator(js); - } - - /// Create a new style tag - fn create_style(&self, props: StyleProps) { - let attributes = props.attributes(); - let js = create_element_in_head("style", &attributes, props.style_contents()); - self.new_evaluator(js); - } - - /// Create a new link tag - fn create_link(&self, props: head::LinkProps) { - let attributes = props.attributes(); - let js = create_element_in_head("link", &attributes, None); - self.new_evaluator(js); - } - - /// Get a reference to the document as `dyn Any` - fn as_any(&self) -> &dyn std::any::Any; -} +// pub mod head; +// pub use head::{ +// Meta, MetaProps, Script, ScriptProps, Style, StyleProps, Stylesheet, Title, TitleProps, +// }; // /// The default No-Op document // pub struct NoOpDocument; @@ -117,12 +48,3 @@ pub trait Document { // Poll::Ready(Err(EvalError::Unsupported)) // } // } - -/// Get the document provider for the current platform or a no-op provider if the platform doesn't document functionality. -pub fn document() -> Rc { - dioxus_core::prelude::try_consume_context::>() - .expect("A document should exist with this renderer") - // Create a NoOp provider that always logs an error when trying to evaluate - // That way, we can still compile and run the code without a real provider - // .unwrap_or_else(|| Rc::new(NoOpDocument) as Rc) -} diff --git a/packages/html/src/elements.rs b/packages/html/src/elements.rs index 4d461db202..5e6defed66 100644 --- a/packages/html/src/elements.rs +++ b/packages/html/src/elements.rs @@ -2,9 +2,9 @@ use dioxus_core::prelude::IntoAttributeValue; use dioxus_core::HasAttributes; -use dioxus_html_internal_macro::impl_extension_attributes; #[cfg(feature = "hot-reload-context")] -use dioxus_rsx::HotReloadingContext; +use dioxus_core_types::HotReloadingContext; +use dioxus_html_internal_macro::impl_extension_attributes; #[cfg(feature = "hot-reload-context")] use crate::{map_global_attributes, map_svg_attributes}; diff --git a/packages/html/src/events/mod.rs b/packages/html/src/events/mod.rs index 29fcf0dedb..e9a5bb5115 100644 --- a/packages/html/src/events/mod.rs +++ b/packages/html/src/events/mod.rs @@ -303,97 +303,3 @@ pub use toggle::*; pub use touch::*; pub use transition::*; pub use wheel::*; - -pub fn event_bubbles(evt: &str) -> bool { - match evt { - "copy" => true, - "cut" => true, - "paste" => true, - "compositionend" => true, - "compositionstart" => true, - "compositionupdate" => true, - "keydown" => true, - "keypress" => true, - "keyup" => true, - "focus" => false, - "focusout" => true, - "focusin" => true, - "blur" => false, - "change" => true, - "input" => true, - "invalid" => true, - "reset" => true, - "submit" => true, - "click" => true, - "contextmenu" => true, - "doubleclick" => true, - "dblclick" => true, - "drag" => true, - "dragend" => true, - "dragenter" => false, - "dragexit" => false, - "dragleave" => true, - "dragover" => true, - "dragstart" => true, - "drop" => true, - "mousedown" => true, - "mouseenter" => false, - "mouseleave" => false, - "mousemove" => true, - "mouseout" => true, - "scroll" => false, - "mouseover" => true, - "mouseup" => true, - "pointerdown" => true, - "pointermove" => true, - "pointerup" => true, - "pointercancel" => true, - "gotpointercapture" => true, - "lostpointercapture" => true, - "pointerenter" => false, - "pointerleave" => false, - "pointerover" => true, - "pointerout" => true, - "select" => true, - "touchcancel" => true, - "touchend" => true, - "touchmove" => true, - "touchstart" => true, - "wheel" => true, - "abort" => false, - "canplay" => false, - "canplaythrough" => false, - "durationchange" => false, - "emptied" => false, - "encrypted" => true, - "ended" => false, - "error" => false, - "loadeddata" => false, - "loadedmetadata" => false, - "loadstart" => false, - "load" => false, - "pause" => false, - "play" => false, - "playing" => false, - "progress" => false, - "ratechange" => false, - "resize" => false, - "seeked" => false, - "seeking" => false, - "stalled" => false, - "suspend" => false, - "timeupdate" => false, - "volumechange" => false, - "waiting" => false, - "animationstart" => true, - "animationend" => true, - "animationiteration" => true, - "transitionend" => true, - "toggle" => true, - "mounted" => false, - _ => { - tracing::warn!("Unknown event name: {evt}"); - true - } - } -} diff --git a/packages/html/src/lib.rs b/packages/html/src/lib.rs index 5238592dbd..373bde5b4d 100644 --- a/packages/html/src/lib.rs +++ b/packages/html/src/lib.rs @@ -32,8 +32,6 @@ pub mod geometry; pub mod input_data; pub mod point_interaction; -mod render_template; - #[cfg(feature = "serialize")] mod transit; @@ -44,7 +42,6 @@ pub use transit::*; pub use attribute_groups::*; pub use elements::*; pub use events::*; -pub use render_template::*; #[cfg(feature = "document")] pub mod document; @@ -56,11 +53,11 @@ pub mod extensions { pub mod prelude { pub use crate::attribute_groups::{GlobalAttributesExtension, SvgAttributesExtension}; - #[cfg(feature = "document")] - pub use crate::document::{ - self, document, eval, head, Document, Meta, MetaProps, Script, ScriptProps, Style, - StyleProps, Stylesheet, Title, TitleProps, UseEval, - }; + // #[cfg(feature = "document")] + // pub use crate::document::{ + // self, document, eval, head, Document, Meta, MetaProps, Script, ScriptProps, Style, + // StyleProps, Stylesheet, Title, TitleProps, UseEval, + // }; pub use crate::elements::extensions::*; pub use crate::events::*; pub use crate::point_interaction::*; diff --git a/packages/html/src/render_template.rs b/packages/html/src/render_template.rs deleted file mode 100644 index 3705095385..0000000000 --- a/packages/html/src/render_template.rs +++ /dev/null @@ -1,40 +0,0 @@ -use dioxus_core::{Template, TemplateAttribute, TemplateNode}; -use std::fmt::Write; - -/// Render a template to an HTML string -/// -/// Useful for sending over the wire. Can be used to with innerHtml to create templates with little work -pub fn render_template_to_html(template: &Template) -> String { - let mut out = String::new(); - - for root in template.roots { - render_template_node(root, &mut out).unwrap(); - } - - out -} - -fn render_template_node(node: &TemplateNode, out: &mut String) -> std::fmt::Result { - match node { - TemplateNode::Element { - tag, - attrs, - children, - .. - } => { - write!(out, "<{tag}")?; - for attr in *attrs { - if let TemplateAttribute::Static { name, value, .. } = attr { - write!(out, "{name}=\"{value}\"")?; - } - } - for child in *children { - render_template_node(child, out)?; - } - write!(out, "")?; - } - TemplateNode::Text { text: t } => write!(out, "{t}")?, - TemplateNode::Dynamic { id: _ } => write!(out, "")?, - }; - Ok(()) -} diff --git a/packages/interpreter/Cargo.toml b/packages/interpreter/Cargo.toml index 7514d7f323..1a919d86ab 100644 --- a/packages/interpreter/Cargo.toml +++ b/packages/interpreter/Cargo.toml @@ -23,7 +23,7 @@ sledgehammer_utils = { version = "0.2", optional = true } serde = { version = "1.0", features = ["derive"], optional = true } dioxus-core = { workspace = true, optional = true } -dioxus-html = { workspace = true, optional = true } +dioxus-core-types = { workspace = true, optional = true } [build-dependencies] lazy-js-bundle = { workspace = true } @@ -40,5 +40,5 @@ webonly = [ "dep:web-sys", "sledgehammer_bindgen/web", ] -binary-protocol = ["sledgehammer", "dep:dioxus-core", "dep:dioxus-html"] +binary-protocol = ["sledgehammer", "dep:dioxus-core", "dep:dioxus-core-types"] minimal_bindings = [] diff --git a/packages/interpreter/src/write_native_mutations.rs b/packages/interpreter/src/write_native_mutations.rs index 6f18150243..f193cd3bdd 100644 --- a/packages/interpreter/src/write_native_mutations.rs +++ b/packages/interpreter/src/write_native_mutations.rs @@ -1,6 +1,6 @@ use crate::unified_bindings::Interpreter as Channel; use dioxus_core::{Template, TemplateAttribute, TemplateNode, WriteMutations}; -use dioxus_html::event_bubbles; +use dioxus_core_types::event_bubbles; use sledgehammer_utils::rustc_hash::FxHashMap; /// The state needed to apply mutations to a channel. This state should be kept across all mutations for the app diff --git a/packages/manganis-common/Cargo.toml b/packages/manganis-common/Cargo.toml index 58fa5c3d3f..ea22084afa 100644 --- a/packages/manganis-common/Cargo.toml +++ b/packages/manganis-common/Cargo.toml @@ -15,8 +15,8 @@ keywords = ["assets"] serde = { version = "1.0.183", features = ["derive"] } anyhow = "1" base64 = { workspace = true } -infer = { workspace = true } -fluent-uri = { version = "0.2.0", features = ["serde"] } +# infer = { workspace = true } +# fluent-uri = { version = "0.2.0", features = ["serde"] } # tracing = "0.1.40" @@ -28,7 +28,3 @@ fluent-uri = { version = "0.2.0", features = ["serde"] } [features] default = ["html"] html = [] - -[build-dependencies] -built = { version = "0.7", features = ["git2"] } -scratch = "1.0.7" diff --git a/packages/manganis-common/build.rs b/packages/manganis-common/build.rs deleted file mode 100644 index d8f91cb913..0000000000 --- a/packages/manganis-common/build.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - built::write_built_file().expect("Failed to acquire build-time information"); -} diff --git a/packages/manganis-common/src/asset/resource.rs b/packages/manganis-common/src/asset/resource.rs index 50e3cb5504..7e3d79abe4 100644 --- a/packages/manganis-common/src/asset/resource.rs +++ b/packages/manganis-common/src/asset/resource.rs @@ -7,11 +7,11 @@ use std::{ use anyhow::Context; use base64::Engine; -use fluent_uri::{ - component::{Authority, Scheme}, - encoding::EStr, - UriRef, -}; +// use fluent_uri::{ +// component::{Authority, Scheme}, +// encoding::EStr, +// UriRef, +// }; use serde::{Deserialize, Serialize}; use crate::{config, AssetError, FileOptions}; @@ -27,7 +27,7 @@ pub struct ResourceAsset { /// The input URI /// /// This is basically whatever the user passed in to the macro - pub input: UriRef, + pub input: PathBuf, /// The local URI for fallbacks /// @@ -35,7 +35,7 @@ pub struct ResourceAsset { /// it's resolved to an absolute path since we transform all schema-less URIs to file:// URIs. /// /// If the aset is relative, this will be None since we can't figure it out at compile time. - pub local: Option>, + pub local: Option, /// The output URI that makes it into the final bundle. /// This explicitly has the `bundle://` scheme to make it clear that it is a bundle URI. @@ -44,7 +44,7 @@ pub struct ResourceAsset { /// final "flat" architecture. /// /// bundle://asset/path/to/file.txt - pub bundled: UriRef, + pub bundled: PathBuf, /// The options for the resource pub options: Option, @@ -61,11 +61,6 @@ impl ResourceAsset { todo!() } - /// - pub fn original(&self) -> &UriRef { - todo!() - } - /// Set the file options pub fn with_options(self, options: FileOptions) -> Self { todo!() @@ -128,60 +123,62 @@ impl ResourceAsset { /// Parse a string as a file or folder source pub fn parse_any(src: &str) -> Result { - // Process the input as a URI - let input: UriRef = src.parse().unwrap(); - - let local = match input.scheme().map(|x| x.as_str()) { - // For http and https, we just use the input as is - // In fallback mode we end up just passing the URI through - Some("http") | Some("https") => Some(input.clone()), - - // For file, we use the local path - // This will be `file://` in dev - // In release this will be `bundle://` - // Join the URI against the filesystem - None if input.path().is_absolute() => { - let manifest_dir: PathBuf = std::env::var("CARGO_MANIFEST_DIR").unwrap().into(); - let manifest_dir = manifest_dir.canonicalize().unwrap(); - let _local = manifest_dir.join(input.path().as_str()); - Some(UriRef::::parse(format!("file://{}", _local.display())).unwrap()) - } - None => None, - - Some(scheme) => { - panic!("Unsupported scheme: {}", scheme); - } - }; - - // Generate the bundled URI - // - // We: - // - flatten the URI with a hash - // - change the scheme to `bundle` - // - add the authority of pkg-name.bundle - // - // This results in a bundle-per dependency - let pkg_name = std::env::var("CARGO_PKG_NAME").unwrap(); - let bundled = UriRef::builder() - .scheme(Scheme::new_or_panic("bundle")) - .authority_with(|b| b.host(EStr::new_or_panic(&format!("{}.bundle", pkg_name)))) - .path(local.as_ref().map(|x| x.path()).unwrap_or_default()) - .build() - .unwrap(); - - Ok(Self { - input, - local, - bundled, - options: None, - }) - } - - /// - pub fn make_unique_id(uri: &UriRef) -> String { todo!() + // // Process the input as a URI + // let input: UriRef = src.parse().unwrap(); + + // let local = match input.scheme().map(|x| x.as_str()) { + // // For http and https, we just use the input as is + // // In fallback mode we end up just passing the URI through + // Some("http") | Some("https") => Some(input.clone()), + + // // For file, we use the local path + // // This will be `file://` in dev + // // In release this will be `bundle://` + // // Join the URI against the filesystem + // None if input.path().is_absolute() => { + // tood!() + // // let manifest_dir: PathBuf = std::env::var("CARGO_MANIFEST_DIR").unwrap().into(); + // // let manifest_dir = manifest_dir.canonicalize().unwrap(); + // // let _local = manifest_dir.join(input.path().as_str()); + // // // Some(UriRef::::parse(format!("file://{}", _local.display())).unwrap()) + // } + // None => None, + + // Some(scheme) => { + // panic!("Unsupported scheme: {}", scheme); + // } + // }; + + // // Generate the bundled URI + // // + // // We: + // // - flatten the URI with a hash + // // - change the scheme to `bundle` + // // - add the authority of pkg-name.bundle + // // + // // This results in a bundle-per dependency + // let pkg_name = std::env::var("CARGO_PKG_NAME").unwrap(); + // let bundled = UriRef::builder() + // .scheme(Scheme::new_or_panic("bundle")) + // .authority_with(|b| b.host(EStr::new_or_panic(&format!("{}.bundle", pkg_name)))) + // .path(local.as_ref().map(|x| x.path()).unwrap_or_default()) + // .build() + // .unwrap(); + + // Ok(Self { + // input, + // local, + // bundled, + // options: None, + // }) } + // /// + // pub fn make_unique_id(uri: &UriRef) -> String { + // todo!() + // } + /// pub fn is_dir(&self) -> bool { todo!() @@ -478,22 +475,23 @@ fn ext_of_mime(mime: &str) -> &str { /// Get the mime type from a path-like string fn get_mime_from_path(trimmed: &Path) -> std::io::Result<&'static str> { - if trimmed.extension().is_some_and(|ext| ext == "svg") { - return Ok("image/svg+xml"); - } + todo!() + // if trimmed.extension().is_some_and(|ext| ext == "svg") { + // return Ok("image/svg+xml"); + // } + + // let res = match infer::get_from_path(trimmed)?.map(|f| f.mime_type()) { + // Some(f) => { + // if f == "text/plain" { + // get_mime_by_ext(trimmed) + // } else { + // f + // } + // } + // None => get_mime_by_ext(trimmed), + // }; - let res = match infer::get_from_path(trimmed)?.map(|f| f.mime_type()) { - Some(f) => { - if f == "text/plain" { - get_mime_by_ext(trimmed) - } else { - f - } - } - None => get_mime_by_ext(trimmed), - }; - - Ok(res) + // Ok(res) } /// Get the mime type from a URI using its extension @@ -531,7 +529,7 @@ pub fn get_mime_from_ext(extension: Option<&str>) -> &'static str { } fn hash_version(hash: &mut DefaultHasher) { - // Hash the current version of manganis. If this changes, we need to regenerate the unique name - crate::built::PKG_VERSION.hash(hash); - crate::built::GIT_COMMIT_HASH.hash(hash); + // // Hash the current version of manganis. If this changes, we need to regenerate the unique name + // crate::built::PKG_VERSION.hash(hash); + // crate::built::GIT_COMMIT_HASH.hash(hash); } diff --git a/packages/manganis-common/src/lib.rs b/packages/manganis-common/src/lib.rs index e964569ef4..c4cc9f9088 100644 --- a/packages/manganis-common/src/lib.rs +++ b/packages/manganis-common/src/lib.rs @@ -2,7 +2,7 @@ //! Common types and methods for the manganis asset system mod asset; -mod built; +// mod built; mod config; mod file; diff --git a/packages/manganis-macro/Cargo.toml b/packages/manganis-macro/Cargo.toml index ba5e64c14a..0695cc83dd 100644 --- a/packages/manganis-macro/Cargo.toml +++ b/packages/manganis-macro/Cargo.toml @@ -15,19 +15,17 @@ proc-macro = true [dependencies] proc-macro2 = { version = "1.0" } quote = "1.0" -syn = { version = "2.0", features = ["full", "extra-traits"] } serde_json = "1.0" -base64 = { workspace = true, optional = true } +syn = { version = "2.0", features = ["full", "extra-traits"] } serde = { workspace = true, features = ["derive"] } +# base64 = { workspace = true, optional = true } # manganis-common = { workspace = true } # manganis-cli-support = { workspace = true, optional = true } # manganis-common = { path = "../common", version = "0.3.0-alpha.1" } # manganis-cli-support = { path = "../cli-support", version = "0.3.0-alpha.1", optional = true } # base64 = { version = "0.21.5", optional = true } -# [build-dependencies] -# manganis-common = { workspace = true } [features] -default = ["url-encoding"] -url-encoding = ["base64"] +default = [] +# url-encoding = ["base64"] diff --git a/packages/manganis-test-package/test-package-dependency/src/lib.rs b/packages/manganis-test-package/test-package-dependency/src/lib.rs index 5eb015a968..c311568a33 100644 --- a/packages/manganis-test-package/test-package-dependency/src/lib.rs +++ b/packages/manganis-test-package/test-package-dependency/src/lib.rs @@ -2,7 +2,7 @@ mod file; pub use file::*; pub use test_package_nested_dependency::*; -const _: &str = manganis::classes!("flex flex-col p-1"); -const _: &str = manganis::classes!("flex flex-col p-2"); -const _: &str = manganis::classes!("flex flex-col p-3"); -const _: &str = manganis::classes!("flex flex-col p-4"); +// const _: &str = manganis::classes!("flex flex-col p-1"); +// const _: &str = manganis::classes!("flex flex-col p-2"); +// const _: &str = manganis::classes!("flex flex-col p-3"); +// const _: &str = manganis::classes!("flex flex-col p-4"); diff --git a/packages/manganis-test-package/test-package-nested-dependency/src/lib.rs b/packages/manganis-test-package/test-package-nested-dependency/src/lib.rs index f22f81ddba..5be1c68cf6 100644 --- a/packages/manganis-test-package/test-package-nested-dependency/src/lib.rs +++ b/packages/manganis-test-package/test-package-nested-dependency/src/lib.rs @@ -1,13 +1,16 @@ mod file; pub use file::*; + mod font; pub use font::*; + mod js; pub use js::*; + mod folder; pub use folder::*; -const _: &str = manganis::classes!("flex flex-row p-1"); -const _: &str = manganis::classes!("flex flex-row p-2"); -const _: &str = manganis::classes!("flex flex-row p-3"); -const _: &str = manganis::classes!("flex flex-row p-4"); +// const _: &str = manganis::classes!("flex flex-row p-1"); +// const _: &str = manganis::classes!("flex flex-row p-2"); +// const _: &str = manganis::classes!("flex flex-row p-3"); +// const _: &str = manganis::classes!("flex flex-row p-4"); diff --git a/packages/manganis/Cargo.toml b/packages/manganis/Cargo.toml index 1cbb4cd061..fafdf4c158 100644 --- a/packages/manganis/Cargo.toml +++ b/packages/manganis/Cargo.toml @@ -18,22 +18,22 @@ once_cell = "1.19.0" # dirs = "5.0.1" # infer = { workspace = true } # manganis-common = { workspace = true } -# dunce = "1.0.2" +dunce = "1.0.2" # [target.'cfg(target_os = "macos")'.dependencies] # core-foundation = "0.9.3" # [target.'cfg(target_os ="macos")'.dependencies] -infer = { workspace = true } +# infer = { workspace = true } manganis-common = { workspace = true } -dunce = "1.0.2" +# dunce = "1.0.2" -[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies] -core-foundation = "0.10.0" +# [target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies] +# core-foundation = "0.10.0" [features] default = ["macro"] html = [] -url-encoding = ["manganis-macro/url-encoding"] +# url-encoding = ["manganis-macro/url-encoding"] macro = ["dep:manganis-macro"] diff --git a/packages/manganis/src/builder.rs b/packages/manganis/src/builder.rs index 455ca6629a..cff27b4771 100644 --- a/packages/manganis/src/builder.rs +++ b/packages/manganis/src/builder.rs @@ -113,10 +113,29 @@ fn base_path() -> Option { // todo: for other platforms, we should check their bundles too. This currently only works for macOS and iOS #[cfg(any(target_os = "macos", target_os = "ios"))] { - // Note that this will return `target/debug` if you're in debug mode - not reliable check if we're in dev mode - if let Some(resources) = core_foundation::bundle::CFBundle::main_bundle().resources_path() { - return dunce::canonicalize(resources).ok(); + // usually the bundle is + // .app + // Contents + // Resources + // some_asset + // macOS + // somebinary + // + // but not always! + // + // we fallback to using the .app's directory itself if it doesn't exist - which is inline + // with how tauri-bundle works + // + // we would normally just want to use core-foundation, but it's much faster for compile times + // to not pull in CF in a build/proc-macro, so it's a teeny bit hand-rolled + let cur_exe = std::env::current_exe().ok()?; + let mut resources_dir = cur_exe.parent()?.parent()?.join("Resources"); + if !resources_dir.exists() { + resources_dir = cur_exe.parent()?.to_path_buf(); } + + // Note that this will return `target/debug` if you're in debug mode - not reliable check if we're in dev mode + return dunce::canonicalize(resources_dir).ok(); } // todo: this needs to be real canonicalizations diff --git a/packages/rsx/Cargo.toml b/packages/rsx/Cargo.toml index 5c1c264083..f87ad8d60b 100644 --- a/packages/rsx/Cargo.toml +++ b/packages/rsx/Cargo.toml @@ -17,17 +17,15 @@ internment = { workspace = true, optional = true } proc-macro2 = { workspace = true, features = ["span-locations"] } proc-macro2-diagnostics = { workspace = true } quote = { workspace = true } -serde = { workspace = true, features = ["derive"], optional = true } syn = { workspace = true, features = ["full", "extra-traits", "visit", "visit-mut"] } tracing = { workspace = true } dioxus-core = { workspace = true, optional = true } # todo: this is being compiled in a proc macro - +dioxus-core-types = { workspace = true, optional = true } [features] default = [] hot_reload_traits = [] -hot_reload = ["dep:internment", "dep:dioxus-core", "hot_reload_traits", "serde"] -serde = ["dep:serde", "dioxus-core/serialize"] +hot_reload = ["dep:internment", "dep:dioxus-core", "dep:dioxus-core-types", "hot_reload_traits"] [dev-dependencies] prettyplease = { workspace = true } diff --git a/packages/rsx/src/attribute.rs b/packages/rsx/src/attribute.rs index 5c11a7af08..666564ebf8 100644 --- a/packages/rsx/src/attribute.rs +++ b/packages/rsx/src/attribute.rs @@ -167,7 +167,7 @@ impl Attribute { } #[cfg(feature = "hot_reload")] - pub(crate) fn html_tag_and_namespace( + pub(crate) fn html_tag_and_namespace( &self, ) -> (&'static str, Option<&'static str>) { let attribute_name_rust = self.name.to_string(); @@ -182,7 +182,7 @@ impl Attribute { } #[cfg(feature = "hot_reload")] - pub fn to_template_attribute( + pub fn to_template_attribute( &self, ) -> dioxus_core::TemplateAttribute { use dioxus_core::TemplateAttribute; diff --git a/packages/rsx/src/hot_reload/context.rs b/packages/rsx/src/hot_reload/context.rs index c38101093e..8b13789179 100644 --- a/packages/rsx/src/hot_reload/context.rs +++ b/packages/rsx/src/hot_reload/context.rs @@ -1,19 +1 @@ -pub trait HotReloadingContext { - fn map_attribute( - element_name_rust: &str, - attribute_name_rust: &str, - ) -> Option<(&'static str, Option<&'static str>)>; - fn map_element(element_name_rust: &str) -> Option<(&'static str, Option<&'static str>)>; -} -pub struct Empty; - -impl HotReloadingContext for Empty { - fn map_attribute(_: &str, _: &str) -> Option<(&'static str, Option<&'static str>)> { - None - } - - fn map_element(_: &str) -> Option<(&'static str, Option<&'static str>)> { - None - } -} diff --git a/packages/rsx/src/hot_reload/diff.rs b/packages/rsx/src/hot_reload/diff.rs index 628bcc8175..79af95c65e 100644 --- a/packages/rsx/src/hot_reload/diff.rs +++ b/packages/rsx/src/hot_reload/diff.rs @@ -64,11 +64,11 @@ //! more difficult to match future templates. use crate::innerlude::*; -use crate::HotReloadingContext; use dioxus_core::internal::{ FmtedSegments, HotReloadAttributeValue, HotReloadDynamicAttribute, HotReloadDynamicNode, HotReloadLiteral, HotReloadedTemplate, NamedAttribute, }; +use dioxus_core_types::HotReloadingContext; use std::collections::HashMap; use super::last_build_state::LastBuildState; diff --git a/packages/rsx/src/hot_reload/mod.rs b/packages/rsx/src/hot_reload/mod.rs index 5976317f8c..e07a8a204d 100644 --- a/packages/rsx/src/hot_reload/mod.rs +++ b/packages/rsx/src/hot_reload/mod.rs @@ -3,11 +3,6 @@ mod collect; #[cfg(feature = "hot_reload")] pub use collect::*; -#[cfg(feature = "hot_reload_traits")] -mod context; -#[cfg(feature = "hot_reload_traits")] -pub use context::*; - #[cfg(feature = "hot_reload")] mod diff; #[cfg(feature = "hot_reload")] diff --git a/packages/rsx/src/lib.rs b/packages/rsx/src/lib.rs index 4fd6c86e38..4f4f61a4d7 100644 --- a/packages/rsx/src/lib.rs +++ b/packages/rsx/src/lib.rs @@ -80,9 +80,6 @@ pub use template_body::TemplateBody; pub mod hot_reload; -#[cfg(feature = "hot_reload_traits")] -pub use hot_reload::HotReloadingContext; - use quote::{quote, ToTokens, TokenStreamExt}; use syn::{ parse::{Parse, ParseStream}, diff --git a/packages/rsx/src/node.rs b/packages/rsx/src/node.rs index 8d84727423..dd68a7c8d1 100644 --- a/packages/rsx/src/node.rs +++ b/packages/rsx/src/node.rs @@ -130,7 +130,9 @@ impl BodyNode { /// /// dioxus-core uses this to understand templates at compiletime #[cfg(feature = "hot_reload")] - pub fn to_template_node(&self) -> dioxus_core::TemplateNode { + pub fn to_template_node( + &self, + ) -> dioxus_core::TemplateNode { use dioxus_core::TemplateNode; match self { BodyNode::Element(el) => { diff --git a/packages/ssr/Cargo.toml b/packages/ssr/Cargo.toml index 9f47568472..b694b0d822 100644 --- a/packages/ssr/Cargo.toml +++ b/packages/ssr/Cargo.toml @@ -11,6 +11,7 @@ keywords = ["dom", "ui", "gui", "react", "ssr"] [dependencies] dioxus-core = { workspace = true, features = ["serialize"] } dioxus-html = { workspace = true, features = ["document"]} +dioxus-core-types = { workspace = true } dioxus-cli-config = { workspace = true, features = ["read-config"], optional = true } dioxus-interpreter-js = { workspace = true } generational-box = { workspace = true } diff --git a/packages/ssr/src/renderer.rs b/packages/ssr/src/renderer.rs index dc8c0ca2f9..baa7b4e55e 100644 --- a/packages/ssr/src/renderer.rs +++ b/packages/ssr/src/renderer.rs @@ -1,7 +1,7 @@ use super::cache::Segment; use crate::cache::StringCache; - use dioxus_core::{prelude::*, AttributeValue, DynamicNode}; +use dioxus_core_types::event_bubbles; use rustc_hash::FxHashMap; use std::fmt::Write; use std::sync::Arc; @@ -243,7 +243,7 @@ impl Renderer { // then write any listeners for name in accumulated_listeners.drain(..) { write!(buf, ",{}:", &name[2..])?; - write!(buf, "{}", dioxus_html::event_bubbles(&name[2..]) as u8)?; + write!(buf, "{}", event_bubbles(&name[2..]) as u8)?; } } diff --git a/packages/web/Cargo.toml b/packages/web/Cargo.toml index 98f545fd6e..ce4a060aa7 100644 --- a/packages/web/Cargo.toml +++ b/packages/web/Cargo.toml @@ -10,8 +10,11 @@ homepage = "https://dioxuslabs.com/learn/0.5/getting_started" keywords = ["dom", "ui", "gui", "react", "wasm"] [dependencies] + +dioxus-core-types = { workspace = true } dioxus-core = { workspace = true } dioxus-html = { workspace = true } +dioxus-document = { workspace = true } dioxus-hot-reload = { workspace = true, features = ["client"] } dioxus-signals = { workspace = true } dioxus-interpreter-js = { workspace = true, features = [ @@ -31,6 +34,7 @@ futures-util = { workspace = true, features = [ "async-await", "async-await-macro", ] } +async-trait = { workspace = true } futures-channel = { workspace = true } serde_json = { version = "1.0", optional = true } serde = { version = "1.0", optional = true } @@ -80,7 +84,8 @@ lazy-js-bundle = { workspace = true } [features] -default = ["panic_hook", "mounted", "file_engine", "hot_reload", "document"] +default = ["panic_hook", "mounted","hot_reload", "document"] +# default = ["panic_hook", "mounted", "file_engine", "hot_reload", "document"] panic_hook = ["dep:console_error_panic_hook"] hydrate = ["web-sys/Comment", "ciborium", "dep:serde"] mounted = ["web-sys/Element", "dioxus-html/mounted"] diff --git a/packages/web/src/bindings.rs b/packages/web/src/bindings.rs index 05e55e0074..71280c5e55 100644 --- a/packages/web/src/bindings.rs +++ b/packages/web/src/bindings.rs @@ -1,12 +1,157 @@ +/// #[wasm_bindgen::prelude::wasm_bindgen] pub struct JSOwner { _owner: Box, } impl JSOwner { + /// pub fn new(owner: impl std::any::Any) -> Self { Self { _owner: Box::new(owner), } } } + +use std::any::Any; + +// use dioxus_html::FileEngine; +use futures_channel::oneshot; +use js_sys::Uint8Array; +use wasm_bindgen::{prelude::Closure, JsCast}; + +#[cfg(feature = "file-engine")] +use web_sys::{File, FileList, FileReader}; + +#[cfg(feature = "file-engine")] +/// A file engine for the web platform +pub struct WebFileEngine { + file_reader: FileReader, + file_list: FileList, +} + +#[cfg(feature = "file-engine")] +impl WebFileEngine { + /// Create a new file engine from a file list + pub fn new(file_list: FileList) -> Option { + Some(Self { + file_list, + file_reader: FileReader::new().ok()?, + }) + } + + fn len(&self) -> usize { + self.file_list.length() as usize + } + + fn get(&self, index: usize) -> Option { + self.file_list.item(index as u32) + } + + fn find(&self, name: &str) -> Option { + (0..self.len()) + .filter_map(|i| self.get(i)) + .find(|f| f.name() == name) + } +} + +#[cfg(feature = "file-engine")] +#[async_trait::async_trait(?Send)] +impl FileEngine for WebFileEngine { + fn files(&self) -> Vec { + (0..self.len()) + .filter_map(|i| self.get(i).map(|f| f.name())) + .collect() + } + + async fn file_size(&self, file: &str) -> Option { + let file = self.find(file)?; + Some(file.size() as u64) + } + + // read a file to bytes + async fn read_file(&self, file: &str) -> Option> { + let file = self.find(file)?; + + let file_reader = self.file_reader.clone(); + let (rx, tx) = oneshot::channel(); + let on_load: Closure = Closure::new({ + let mut rx = Some(rx); + move || { + let result = file_reader.result(); + let _ = rx + .take() + .expect("multiple files read without refreshing the channel") + .send(result); + } + }); + + self.file_reader + .set_onload(Some(on_load.as_ref().unchecked_ref())); + on_load.forget(); + self.file_reader.read_as_array_buffer(&file).ok()?; + + if let Ok(Ok(js_val)) = tx.await { + let as_u8_arr = Uint8Array::new(&js_val); + let as_u8_vec = as_u8_arr.to_vec(); + + Some(as_u8_vec) + } else { + None + } + } + + // read a file to string + async fn read_file_to_string(&self, file: &str) -> Option { + let file = self.find(file)?; + + let file_reader = self.file_reader.clone(); + let (rx, tx) = oneshot::channel(); + let on_load: Closure = Closure::new({ + let mut rx = Some(rx); + move || { + let result = file_reader.result(); + let _ = rx + .take() + .expect("multiple files read without refreshing the channel") + .send(result); + } + }); + + self.file_reader + .set_onload(Some(on_load.as_ref().unchecked_ref())); + on_load.forget(); + self.file_reader.read_as_text(&file).ok()?; + + if let Ok(Ok(js_val)) = tx.await { + js_val.as_string() + } else { + None + } + } + + async fn get_native_file(&self, file: &str) -> Option> { + let file = self.find(file)?; + Some(Box::new(file)) + } +} + +#[cfg(feature = "file-engine")] +/// Helper trait for WebFileEngine +#[async_trait::async_trait(?Send)] +pub trait WebFileEngineExt { + /// returns web_sys::File + #[cfg(feature = "file-engine")] + async fn get_web_file(&self, file: &str) -> Option; +} + +#[cfg(feature = "file-engine")] +#[async_trait::async_trait(?Send)] +impl WebFileEngineExt for std::sync::Arc { + #[cfg(feature = "file-engine")] + async fn get_web_file(&self, file: &str) -> Option { + let native_file = self.get_native_file(file).await?; + let ret = native_file.downcast::().ok()?; + Some(*ret) + } +} diff --git a/packages/web/src/document.rs b/packages/web/src/document.rs index 6a784f9acb..c3ab0ce00c 100644 --- a/packages/web/src/document.rs +++ b/packages/web/src/document.rs @@ -1,6 +1,7 @@ use crate::bindings::JSOwner; use dioxus_core::ScopeId; -use dioxus_html::document::{Document, EvalError, Evaluator}; +use dioxus_document::{Document, EvalError}; +// use dioxus_html::document::{Document, EvalError, Evaluator}; // use dioxus_html::document::{Document, EvalError, Evaluator, WeakDioxusChannel, WebDioxusChannel}; use generational_box::{AnyStorage, GenerationalBox, UnsyncStorage}; use js_sys::Function; @@ -22,9 +23,9 @@ pub fn init_document() { /// The web-target's document provider. pub struct WebDocument; impl Document for WebDocument { - fn new_evaluator(&self, js: String) -> GenerationalBox> { - WebEvaluator::create(js) - } + // fn new_evaluator(&self, js: String) -> GenerationalBox> { + // WebEvaluator::create(js) + // } fn as_any(&self) -> &dyn std::any::Any { self @@ -41,111 +42,111 @@ const PROMISE_WRAPPER: &str = r#" type NextPoll = Pin>>>; -/// Represents a web-target's JavaScript evaluator. -struct WebEvaluator { - channels: WeakDioxusChannel, - next_future: Option, - result: Option>, -} - -impl WebEvaluator { - /// Creates a new evaluator for web-based targets. - fn create(js: String) -> GenerationalBox> { - let owner = UnsyncStorage::owner(); - - let generational_box = owner.invalid(); - - // add the drop handler to DioxusChannel so that it gets dropped when the channel is dropped in js - let channels = WebDioxusChannel::new(JSOwner::new(owner)); - - // The Rust side of the channel is a weak reference to the DioxusChannel - let weak_channels = channels.weak(); - - // Wrap the evaluated JS in a promise so that wasm can continue running (send/receive data from js) - let code = PROMISE_WRAPPER.replace("{JS_CODE}", &js); - - let result = match Function::new_with_args("dioxus", &code).call1(&JsValue::NULL, &channels) - { - Ok(result) => { - if let Ok(stringified) = js_sys::JSON::stringify(&result) { - if !stringified.is_undefined() && stringified.is_valid_utf16() { - let string: String = stringified.into(); - Value::from_str(&string).map_err(|e| { - EvalError::Communication(format!("Failed to parse result - {}", e)) - }) - } else { - Err(EvalError::Communication( - "Failed to stringify result".into(), - )) - } - } else { - Err(EvalError::Communication( - "Failed to stringify result".into(), - )) - } - } - Err(err) => Err(EvalError::InvalidJs( - err.as_string().unwrap_or("unknown".to_string()), - )), - }; - - generational_box.set(Box::new(Self { - channels: weak_channels, - result: Some(result), - next_future: None, - }) as Box); - - generational_box - } -} - -impl Evaluator for WebEvaluator { - /// Runs the evaluated JavaScript. - fn poll_join( - &mut self, - _cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - if let Some(result) = self.result.take() { - std::task::Poll::Ready(result) - } else { - std::task::Poll::Ready(Err(EvalError::Finished)) - } - } - - /// Sends a message to the evaluated JavaScript. - fn send(&self, data: serde_json::Value) -> Result<(), EvalError> { - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - - let data = match data.serialize(&serializer) { - Ok(d) => d, - Err(e) => return Err(EvalError::Communication(e.to_string())), - }; - - self.channels.rust_send(data); - Ok(()) - } - - /// Gets an UnboundedReceiver to receive messages from the evaluated JavaScript. - fn poll_recv( - &mut self, - context: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - if self.next_future.is_none() { - let channels: WebDioxusChannel = self.channels.clone().into(); - let pinned = Box::pin(async move { - let fut = channels.rust_recv(); - let data = fut.await; - serde_wasm_bindgen::from_value::(data) - .map_err(|err| EvalError::Communication(err.to_string())) - }); - self.next_future = Some(pinned); - } - let fut = self.next_future.as_mut().unwrap(); - let mut pinned = std::pin::pin!(fut); - let result = pinned.as_mut().poll(context); - if result.is_ready() { - self.next_future = None; - } - result - } -} +// /// Represents a web-target's JavaScript evaluator. +// struct WebEvaluator { +// channels: WeakDioxusChannel, +// next_future: Option, +// result: Option>, +// } + +// impl WebEvaluator { +// /// Creates a new evaluator for web-based targets. +// fn create(js: String) -> GenerationalBox> { +// let owner = UnsyncStorage::owner(); + +// let generational_box = owner.invalid(); + +// // add the drop handler to DioxusChannel so that it gets dropped when the channel is dropped in js +// let channels = WebDioxusChannel::new(JSOwner::new(owner)); + +// // The Rust side of the channel is a weak reference to the DioxusChannel +// let weak_channels = channels.weak(); + +// // Wrap the evaluated JS in a promise so that wasm can continue running (send/receive data from js) +// let code = PROMISE_WRAPPER.replace("{JS_CODE}", &js); + +// let result = match Function::new_with_args("dioxus", &code).call1(&JsValue::NULL, &channels) +// { +// Ok(result) => { +// if let Ok(stringified) = js_sys::JSON::stringify(&result) { +// if !stringified.is_undefined() && stringified.is_valid_utf16() { +// let string: String = stringified.into(); +// Value::from_str(&string).map_err(|e| { +// EvalError::Communication(format!("Failed to parse result - {}", e)) +// }) +// } else { +// Err(EvalError::Communication( +// "Failed to stringify result".into(), +// )) +// } +// } else { +// Err(EvalError::Communication( +// "Failed to stringify result".into(), +// )) +// } +// } +// Err(err) => Err(EvalError::InvalidJs( +// err.as_string().unwrap_or("unknown".to_string()), +// )), +// }; + +// generational_box.set(Box::new(Self { +// channels: weak_channels, +// result: Some(result), +// next_future: None, +// }) as Box); + +// generational_box +// } +// } + +// impl Evaluator for WebEvaluator { +// /// Runs the evaluated JavaScript. +// fn poll_join( +// &mut self, +// _cx: &mut std::task::Context<'_>, +// ) -> std::task::Poll> { +// if let Some(result) = self.result.take() { +// std::task::Poll::Ready(result) +// } else { +// std::task::Poll::Ready(Err(EvalError::Finished)) +// } +// } + +// /// Sends a message to the evaluated JavaScript. +// fn send(&self, data: serde_json::Value) -> Result<(), EvalError> { +// let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + +// let data = match data.serialize(&serializer) { +// Ok(d) => d, +// Err(e) => return Err(EvalError::Communication(e.to_string())), +// }; + +// self.channels.rust_send(data); +// Ok(()) +// } + +// /// Gets an UnboundedReceiver to receive messages from the evaluated JavaScript. +// fn poll_recv( +// &mut self, +// context: &mut std::task::Context<'_>, +// ) -> std::task::Poll> { +// if self.next_future.is_none() { +// let channels: WebDioxusChannel = self.channels.clone().into(); +// let pinned = Box::pin(async move { +// let fut = channels.rust_recv(); +// let data = fut.await; +// serde_wasm_bindgen::from_value::(data) +// .map_err(|err| EvalError::Communication(err.to_string())) +// }); +// self.next_future = Some(pinned); +// } +// let fut = self.next_future.as_mut().unwrap(); +// let mut pinned = std::pin::pin!(fut); +// let result = pinned.as_mut().poll(context); +// if result.is_ready() { +// self.next_future = None; +// } +// result +// } +// } diff --git a/packages/web/src/event.rs b/packages/web/src/event.rs index 24f58f0ebe..99162e0cb1 100644 --- a/packages/web/src/event.rs +++ b/packages/web/src/event.rs @@ -26,7 +26,8 @@ impl HtmlEventConverter for WebEventConverter { &self, event: &dioxus_html::PlatformEventData, ) -> dioxus_html::AnimationData { - downcast_event(event).raw.clone().into() + // downcast_event(event).raw.clone().into() + todo!() } #[inline(always)] @@ -34,7 +35,8 @@ impl HtmlEventConverter for WebEventConverter { &self, event: &dioxus_html::PlatformEventData, ) -> dioxus_html::ClipboardData { - downcast_event(event).raw.clone().into() + // downcast_event(event).raw.clone().into() + todo!() } #[inline(always)] @@ -42,7 +44,8 @@ impl HtmlEventConverter for WebEventConverter { &self, event: &dioxus_html::PlatformEventData, ) -> dioxus_html::CompositionData { - downcast_event(event).raw.clone().into() + // downcast_event(event).raw.clone().into() + todo!() } #[inline(always)] @@ -53,7 +56,8 @@ impl HtmlEventConverter for WebEventConverter { #[inline(always)] fn convert_focus_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::FocusData { - downcast_event(event).raw.clone().into() + // downcast_event(event).raw.clone().into() + todo!() } #[inline(always)] @@ -74,12 +78,14 @@ impl HtmlEventConverter for WebEventConverter { &self, event: &dioxus_html::PlatformEventData, ) -> dioxus_html::KeyboardData { - downcast_event(event).raw.clone().into() + // downcast_event(event).raw.clone().into() + todo!() } #[inline(always)] fn convert_media_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::MediaData { - downcast_event(event).raw.clone().into() + // downcast_event(event).raw.clone().into() + todo!() } #[allow(unused_variables)] @@ -87,11 +93,12 @@ impl HtmlEventConverter for WebEventConverter { fn convert_mounted_data(&self, event: &dioxus_html::PlatformEventData) -> MountedData { #[cfg(feature = "mounted")] { - MountedData::from( - event - .downcast::() - .expect("event should be a web_sys::Element"), - ) + // MountedData::from( + // event + // .downcast::() + // .expect("event should be a web_sys::Element"), + // ) + todo!() } #[cfg(not(feature = "mounted"))] { @@ -101,7 +108,8 @@ impl HtmlEventConverter for WebEventConverter { #[inline(always)] fn convert_mouse_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::MouseData { - downcast_event(event).raw.clone().into() + // downcast_event(event).raw.clone().into() + todo!() } #[inline(always)] @@ -109,7 +117,8 @@ impl HtmlEventConverter for WebEventConverter { &self, event: &dioxus_html::PlatformEventData, ) -> dioxus_html::PointerData { - downcast_event(event).raw.clone().into() + // downcast_event(event).raw.clone().into() + todo!() } #[inline(always)] @@ -117,7 +126,8 @@ impl HtmlEventConverter for WebEventConverter { &self, event: &dioxus_html::PlatformEventData, ) -> dioxus_html::ResizeData { - downcast_event(event).raw.clone().into() + // downcast_event(event).raw.clone().into() + todo!() } #[inline(always)] @@ -125,7 +135,8 @@ impl HtmlEventConverter for WebEventConverter { &self, event: &dioxus_html::PlatformEventData, ) -> dioxus_html::ScrollData { - ScrollData::from(downcast_event(event).raw.clone()) + // ScrollData::from(downcast_event(event).raw.clone()) + todo!() } #[inline(always)] @@ -133,7 +144,8 @@ impl HtmlEventConverter for WebEventConverter { &self, event: &dioxus_html::PlatformEventData, ) -> dioxus_html::SelectionData { - downcast_event(event).raw.clone().into() + // downcast_event(event).raw.clone().into() + todo!() } #[inline(always)] @@ -141,12 +153,14 @@ impl HtmlEventConverter for WebEventConverter { &self, event: &dioxus_html::PlatformEventData, ) -> dioxus_html::ToggleData { - downcast_event(event).raw.clone().into() + // downcast_event(event).raw.clone().into() + todo!() } #[inline(always)] fn convert_touch_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::TouchData { - downcast_event(event).raw.clone().into() + // downcast_event(event).raw.clone().into() + todo!() } #[inline(always)] @@ -154,12 +168,14 @@ impl HtmlEventConverter for WebEventConverter { &self, event: &dioxus_html::PlatformEventData, ) -> dioxus_html::TransitionData { - downcast_event(event).raw.clone().into() + // downcast_event(event).raw.clone().into() + todo!() } #[inline(always)] fn convert_wheel_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::WheelData { - downcast_event(event).raw.clone().into() + // downcast_event(event).raw.clone().into() + todo!() } } @@ -456,13 +472,15 @@ impl HasFormData for WebFormData { impl HasFileData for WebFormData { #[cfg(feature = "file_engine")] fn files(&self) -> Option> { + use crate::bindings::WebFileEngine; + let files = self .element .dyn_ref() .and_then(|input: &web_sys::HtmlInputElement| { input.files().and_then(|files| { #[allow(clippy::arc_with_non_send_sync)] - dioxus_html::WebFileEngine::new(files).map(|f| { + WebFileEngine::new(files).map(|f| { std::sync::Arc::new(f) as std::sync::Arc }) }) @@ -496,47 +514,57 @@ impl HasMouseData for WebDragData { impl PointerInteraction for WebDragData { fn trigger_button(&self) -> Option { - self.raw.trigger_button() + // self.raw.trigger_button() + todo!() } fn held_buttons(&self) -> dioxus_html::input_data::MouseButtonSet { - self.raw.held_buttons() + // self.raw.held_buttons() + todo!() } } impl ModifiersInteraction for WebDragData { fn modifiers(&self) -> dioxus_html::prelude::Modifiers { - self.raw.modifiers() + // self.raw.modifiers() + todo!() } } impl InteractionElementOffset for WebDragData { fn coordinates(&self) -> dioxus_html::geometry::Coordinates { - self.raw.coordinates() + // self.raw.coordinates() + todo!() } fn element_coordinates(&self) -> dioxus_html::geometry::ElementPoint { - self.raw.element_coordinates() + // self.raw.element_coordinates() + todo!() } } impl InteractionLocation for WebDragData { fn client_coordinates(&self) -> dioxus_html::geometry::ClientPoint { - self.raw.client_coordinates() + // self.raw.client_coordinates() + todo!() } fn screen_coordinates(&self) -> dioxus_html::geometry::ScreenPoint { - self.raw.screen_coordinates() + // self.raw.screen_coordinates() + todo!() } fn page_coordinates(&self) -> dioxus_html::geometry::PagePoint { - self.raw.page_coordinates() + // self.raw.page_coordinates() + todo!() } } impl HasFileData for WebDragData { #[cfg(feature = "file_engine")] fn files(&self) -> Option> { + use crate::bindings::WebFileEngine; + let files = self .raw .dyn_ref::() @@ -544,7 +572,7 @@ impl HasFileData for WebDragData { drag_event.data_transfer().and_then(|dt| { dt.files().and_then(|files| { #[allow(clippy::arc_with_non_send_sync)] - dioxus_html::WebFileEngine::new(files).map(|f| { + WebFileEngine::new(files).map(|f| { std::sync::Arc::new(f) as std::sync::Arc }) }) diff --git a/packages/web/src/hot_reload.rs b/packages/web/src/hot_reload.rs index b099c72e38..28bd82f23d 100644 --- a/packages/web/src/hot_reload.rs +++ b/packages/web/src/hot_reload.rs @@ -7,8 +7,8 @@ use std::fmt::Display; use std::time::Duration; use dioxus_core::ScopeId; +use dioxus_document::eval; use dioxus_hot_reload::{DevserverMsg, HotReloadMsg}; -use dioxus_html::prelude::eval; use futures_channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender}; use js_sys::JsString; use wasm_bindgen::JsCast; diff --git a/packages/web/src/lib.rs b/packages/web/src/lib.rs index 8e1a085e60..1df8caccde 100644 --- a/packages/web/src/lib.rs +++ b/packages/web/src/lib.rs @@ -33,7 +33,9 @@ mod event; pub mod launch; mod mutations; pub use event::*; -pub mod bindings; + +/// +mod bindings; #[cfg(feature = "document")] mod document; diff --git a/packages/web/src/mutations.rs b/packages/web/src/mutations.rs index 592c2e5f4b..b0c2b7e845 100644 --- a/packages/web/src/mutations.rs +++ b/packages/web/src/mutations.rs @@ -2,7 +2,7 @@ use crate::dom::WebsysDom; use dioxus_core::prelude::*; use dioxus_core::WriteMutations; use dioxus_core::{AttributeValue, ElementId}; -use dioxus_html::event_bubbles; +use dioxus_core_types::event_bubbles; use dioxus_interpreter_js::minimal_bindings; use wasm_bindgen::JsCast; use wasm_bindgen::JsValue; diff --git a/packages/html/tsconfig.json b/packages/web/tsconfig.json similarity index 100% rename from packages/html/tsconfig.json rename to packages/web/tsconfig.json From 80dbb4950a5db263a55b2e1e5df65e7df313d9a0 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 21 Aug 2024 20:56:07 -0700 Subject: [PATCH 024/139] move most things to `document::Item` and then decompose the cli-config --- Cargo.lock | 1503 +---------------- Cargo.toml | 3 +- examples/all_events.rs | 2 +- examples/calculator.rs | 2 +- examples/calculator_mutable.rs | 2 +- examples/clock.rs | 2 +- examples/control_focus.rs | 2 +- examples/counters.rs | 2 +- examples/crm.rs | 4 +- examples/custom_assets.rs | 18 +- examples/dynamic_asset.rs | 2 +- examples/eval.rs | 24 +- examples/file_explorer.rs | 4 +- examples/file_upload.rs | 2 +- examples/flat_router.rs | 2 +- examples/global.rs | 2 +- examples/image_generator_openai.rs | 2 +- examples/link.rs | 2 +- examples/meta.rs | 10 +- examples/overlay.rs | 2 +- examples/read_size.rs | 2 +- examples/reducer.rs | 2 +- examples/resize.rs | 2 +- examples/router.rs | 2 +- examples/tailwind/src/main.rs | 2 +- examples/title.rs | 2 +- examples/todomvc.rs | 2 +- examples/weather_app.rs | 2 +- examples/window_event.rs | 2 +- packages/cli-config/.gitignore | 4 - packages/cli-config/Cargo.toml | 43 - packages/cli-config/README.md | 5 - packages/cli-config/src/bundle.rs | 271 --- packages/cli-config/src/lib.rs | 77 - packages/cli/Cargo.toml | 1 - packages/cli/src/builder/cargo.rs | 2 +- packages/cli/src/builder/mod.rs | 34 +- packages/cli/src/builder/prepare_html.rs | 4 +- packages/cli/src/bundle_utils.rs | 165 ++ packages/cli/src/cli/build.rs | 5 +- packages/cli/src/cli/bundle.rs | 6 +- packages/cli/src/cli/serve.rs | 2 +- packages/cli/src/config.rs | 17 + .../src/config.rs => cli/src/config/app.rs} | 165 +- packages/cli/src/config/bundle.rs | 105 ++ packages/cli/src/config/desktop.rs | 0 packages/cli/src/config/dioxus_config.rs | 63 + packages/cli/src/config/platform.rs | 99 ++ .../src => cli/src/config}/serve.rs | 87 +- packages/cli/src/config/web.rs | 0 packages/cli/src/dioxus_crate.rs | 6 +- packages/cli/src/main.rs | 2 + packages/cli/src/serve/output.rs | 4 +- packages/cli/src/serve/proxy.rs | 2 +- packages/cli/src/serve/server.rs | 2 +- packages/core-macro/src/props/mod.rs | 4 +- packages/core-types/src/formatter.rs | 14 + packages/core-types/src/lib.rs | 2 + packages/core/src/nodes.rs | 31 +- packages/core/tests/suspense.rs | 2 +- packages/desktop/headless_tests/eval.rs | 19 +- packages/desktop/headless_tests/rendering.rs | 2 +- packages/desktop/headless_tests/utils.rs | 2 +- packages/desktop/src/webview.rs | 12 +- packages/dioxus/Cargo.toml | 2 + packages/dioxus/src/lib.rs | 4 + packages/document/Cargo.toml | 2 + packages/document/src/eval.rs | 15 +- packages/document/src/head.rs | 2 +- packages/fullstack/Cargo.toml | 6 +- .../fullstack/examples/hackernews/src/main.rs | 2 +- packages/fullstack/src/document/server.rs | 2 +- packages/hot-reload/Cargo.toml | 4 +- packages/hot-reload/src/client.rs | 2 +- packages/liveview/Cargo.toml | 4 +- packages/liveview/src/config.rs | 24 +- packages/liveview/src/eval.rs | 92 +- packages/manganis-cli-support/Cargo.toml | 6 +- packages/manganis-cli-support/src/file.rs | 81 +- packages/manganis/Cargo.toml | 1 + packages/manganis/src/builder.rs | 13 + .../playwright-tests/fullstack/src/main.rs | 2 +- .../nested-suspense/src/main.rs | 2 +- packages/playwright-tests/web/src/main.rs | 25 +- packages/router/Cargo.toml | 2 +- packages/router/src/history/web.rs | 11 +- packages/rsx/tests/hotreload_pattern.rs | 3 +- packages/runtime-config/Cargo.toml | 6 + packages/runtime-config/README.md | 7 + packages/runtime-config/src/lib.rs | 1 + packages/ssr/Cargo.toml | 4 +- packages/static-generation/Cargo.toml | 5 +- 92 files changed, 915 insertions(+), 2284 deletions(-) delete mode 100644 packages/cli-config/.gitignore delete mode 100644 packages/cli-config/Cargo.toml delete mode 100644 packages/cli-config/README.md delete mode 100644 packages/cli-config/src/bundle.rs delete mode 100644 packages/cli-config/src/lib.rs create mode 100644 packages/cli/src/bundle_utils.rs create mode 100644 packages/cli/src/config.rs rename packages/{cli-config/src/config.rs => cli/src/config/app.rs} (53%) create mode 100644 packages/cli/src/config/bundle.rs create mode 100644 packages/cli/src/config/desktop.rs create mode 100644 packages/cli/src/config/dioxus_config.rs create mode 100644 packages/cli/src/config/platform.rs rename packages/{cli-config/src => cli/src/config}/serve.rs (74%) create mode 100644 packages/cli/src/config/web.rs create mode 100644 packages/core-types/src/formatter.rs create mode 100644 packages/runtime-config/Cargo.toml create mode 100644 packages/runtime-config/README.md create mode 100644 packages/runtime-config/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 05a224479c..7e689793e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,16 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "Inflector" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" -dependencies = [ - "lazy_static", - "regex", -] - [[package]] name = "addr2line" version = "0.22.0" @@ -83,7 +73,6 @@ dependencies = [ "const-random", "getrandom 0.2.15", "once_cell", - "serde", "version_check", "zerocopy", ] @@ -336,18 +325,6 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341" -[[package]] -name = "ast_node" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9184f2b369b3e8625712493c89b785881f27eedc6cde480a81883cef78868b2" -dependencies = [ - "proc-macro2", - "quote", - "swc_macros_common", - "syn 2.0.74", -] - [[package]] name = "async-broadcast" version = "0.7.1" @@ -566,17 +543,6 @@ dependencies = [ "terminal-prompt", ] -[[package]] -name = "auto_impl" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.74", -] - [[package]] name = "autocfg" version = "1.3.0" @@ -912,15 +878,6 @@ dependencies = [ "smallvec", ] -[[package]] -name = "better_scoped_tls" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "794edcc9b3fb07bb4aecaa11f093fd45663b4feadb782d68303a2268bc2701de" -dependencies = [ - "scoped-tls", -] - [[package]] name = "bigdecimal" version = "0.3.1" @@ -1128,24 +1085,6 @@ dependencies = [ "alloc-stdlib", ] -[[package]] -name = "browserslist-rs" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdf0ca73de70c3da94e4194e4a01fe732378f55d47cf4c0588caab22a0dbfa14" -dependencies = [ - "ahash 0.8.11", - "chrono", - "either", - "indexmap 2.4.0", - "itertools 0.13.0", - "nom", - "once_cell", - "serde", - "serde_json", - "thiserror", -] - [[package]] name = "bstr" version = "1.10.0" @@ -1180,9 +1119,6 @@ name = "bumpalo" version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" -dependencies = [ - "allocator-api2", -] [[package]] name = "bytecheck" @@ -1339,7 +1275,7 @@ dependencies = [ "remove_dir_all", "rhai", "sanitize-filename", - "semver 1.0.23", + "semver", "serde", "tempfile", "thiserror", @@ -1365,7 +1301,7 @@ checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" dependencies = [ "camino", "cargo-platform", - "semver 1.0.23", + "semver", "serde", "serde_json", "thiserror", @@ -2194,7 +2130,7 @@ dependencies = [ "curve25519-dalek-derive", "digest", "fiat-crypto", - "rustc_version 0.4.0", + "rustc_version", "subtle", "zeroize", ] @@ -2361,16 +2297,6 @@ dependencies = [ "matches", ] -[[package]] -name = "debugid" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" -dependencies = [ - "serde", - "uuid", -] - [[package]] name = "der" version = "0.7.9" @@ -2432,7 +2358,7 @@ dependencies = [ "convert_case 0.4.0", "proc-macro2", "quote", - "rustc_version 0.4.0", + "rustc_version", "syn 2.0.74", ] @@ -2486,7 +2412,9 @@ dependencies = [ "dioxus-config-macro", "dioxus-core", "dioxus-core-macro", + "dioxus-core-types", "dioxus-desktop", + "dioxus-document", "dioxus-fullstack", "dioxus-hooks", "dioxus-hot-reload", @@ -2559,7 +2487,6 @@ dependencies = [ "ctrlc", "dioxus-autofmt", "dioxus-check", - "dioxus-cli-config", "dioxus-core", "dioxus-core-types", "dioxus-hot-reload", @@ -2614,23 +2541,6 @@ dependencies = [ "zip", ] -[[package]] -name = "dioxus-cli-config" -version = "0.6.0-alpha.2" -dependencies = [ - "cargo-config2", - "cargo_toml", - "clap", - "dirs", - "once_cell", - "serde", - "serde_json", - "tauri-bundler", - "tauri-utils", - "toml", - "tracing", -] - [[package]] name = "dioxus-config-macro" version = "0.6.0-alpha.2" @@ -2740,6 +2650,8 @@ dependencies = [ "dioxus-core", "dioxus-core-macro", "dioxus-core-types", + "serde", + "serde_json", "tracing", ] @@ -2786,7 +2698,6 @@ dependencies = [ "ciborium", "clap", "dioxus", - "dioxus-cli-config", "dioxus-core-types", "dioxus-desktop", "dioxus-document", @@ -2844,7 +2755,6 @@ dependencies = [ name = "dioxus-hot-reload" version = "0.6.0-alpha.2" dependencies = [ - "dioxus-cli-config", "dioxus-core", "dioxus-html", "dioxus-rsx", @@ -2930,8 +2840,9 @@ version = "0.6.0-alpha.2" dependencies = [ "axum 0.7.5", "dioxus", - "dioxus-cli-config", "dioxus-core", + "dioxus-core-types", + "dioxus-document", "dioxus-hot-reload", "dioxus-html", "dioxus-interpreter-js", @@ -3016,7 +2927,6 @@ dependencies = [ "console_error_panic_hook", "criterion", "dioxus", - "dioxus-cli-config", "dioxus-fullstack", "dioxus-lib", "dioxus-liveview", @@ -3066,6 +2976,10 @@ dependencies = [ "tracing", ] +[[package]] +name = "dioxus-runtime-config" +version = "0.6.0-alpha.2" + [[package]] name = "dioxus-signals" version = "0.6.0-alpha.2" @@ -3098,7 +3012,6 @@ dependencies = [ "async-trait", "chrono", "dioxus", - "dioxus-cli-config", "dioxus-core", "dioxus-core-types", "dioxus-html", @@ -3108,7 +3021,7 @@ dependencies = [ "fs_extra", "generational-box", "http 1.1.0", - "lru 0.12.4", + "lru", "rustc-hash 1.1.0", "serde", "serde_json", @@ -3124,7 +3037,6 @@ dependencies = [ "axum 0.7.5", "criterion", "dioxus", - "dioxus-cli-config", "dioxus-fullstack", "dioxus-hot-reload", "dioxus-lib", @@ -3706,7 +3618,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" dependencies = [ "memoffset", - "rustc_version 0.4.0", + "rustc_version", ] [[package]] @@ -3815,17 +3727,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "from_variant" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32016f1242eb82af5474752d00fd8ebcd9004bd69b462b1c91de833972d08ed4" -dependencies = [ - "proc-macro2", - "swc_macros_common", - "syn 2.0.74", -] - [[package]] name = "fs-err" version = "2.11.0" @@ -4992,15 +4893,6 @@ dependencies = [ "ahash 0.7.8", ] -[[package]] -name = "hashbrown" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" -dependencies = [ - "ahash 0.8.11", -] - [[package]] name = "hashbrown" version = "0.14.5" @@ -5160,20 +5052,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "hstr" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dae404c0c5d4e95d4858876ab02eecd6a196bb8caa42050dfa809938833fc412" -dependencies = [ - "hashbrown 0.14.5", - "new_debug_unreachable", - "once_cell", - "phf 0.11.2", - "rustc-hash 1.1.0", - "triomphe", -] - [[package]] name = "html5ever" version = "0.26.0" @@ -5456,12 +5334,6 @@ dependencies = [ "unicode-normalization", ] -[[package]] -name = "if_chain" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" - [[package]] name = "ignore" version = "0.4.22" @@ -5706,18 +5578,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "is-macro" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2069faacbe981460232f880d26bf3c7634e322d49053aa48c27e3ae642f728f1" -dependencies = [ - "Inflector", - "proc-macro2", - "quote", - "syn 2.0.74", -] - [[package]] name = "is-terminal" version = "0.4.13" @@ -5879,15 +5739,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "jsonc-parser" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b56a20e76235284255a09fcd1f45cf55d3c524ea657ebd3854735925c57743d" -dependencies = [ - "serde_json", -] - [[package]] name = "k256" version = "0.13.3" @@ -5951,7 +5802,7 @@ dependencies = [ "camino", "cfg-expr 0.16.0", "petgraph", - "semver 1.0.23", + "semver", "serde", "serde_json", ] @@ -6271,15 +6122,6 @@ dependencies = [ "imgref", ] -[[package]] -name = "lru" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "718e8fae447df0c7e1ba7f5189829e63fd536945c8988d61444c19039f16b670" -dependencies = [ - "hashbrown 0.13.2", -] - [[package]] name = "lru" version = "0.12.4" @@ -6329,6 +6171,7 @@ dependencies = [ name = "manganis" version = "0.6.0-alpha.2" dependencies = [ + "dioxus-core-types", "dunce", "manganis-common", "manganis-macro", @@ -6354,8 +6197,6 @@ dependencies = [ "rustc-hash 1.1.0", "serde", "serde_json", - "swc", - "swc_common", "tracing", "tracing-subscriber", "url", @@ -6465,31 +6306,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "miette" -version = "7.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4edc8853320c2a0dab800fbda86253c8938f6ea88510dc92c5f1ed20e794afc1" -dependencies = [ - "cfg-if", - "miette-derive", - "owo-colors", - "textwrap", - "thiserror", - "unicode-width", -] - -[[package]] -name = "miette-derive" -version = "7.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.74", -] - [[package]] name = "mime" version = "0.3.17" @@ -6766,15 +6582,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" -[[package]] -name = "normpath" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a9da8c9922c35a1033d76f7272dfc2e7ee20392083d75aeea6ced23c6266578" -dependencies = [ - "winapi", -] - [[package]] name = "normpath" version = "1.3.0" @@ -6836,7 +6643,6 @@ checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ "num-integer", "num-traits", - "serde", ] [[package]] @@ -6924,16 +6730,6 @@ dependencies = [ "libm", ] -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi 0.3.9", - "libc", -] - [[package]] name = "num_enum" version = "0.7.3" @@ -7319,18 +7115,6 @@ dependencies = [ "path-dedot", ] -[[package]] -name = "path-clean" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecba01bf2678719532c5e3059e0b5f0811273d94b397088b82e3bd0a78c78fdd" - -[[package]] -name = "path-clean" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17359afc20d7ab31fdb42bb844c8b3bb1dabd7dcf7e68428492da7f16966fcef" - [[package]] name = "path-dedot" version = "3.1.1" @@ -7812,24 +7596,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" -[[package]] -name = "preset_env_base" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b30eab18be480c194938e433e269d5298a279f6410f02fbc73f3576a325c110" -dependencies = [ - "ahash 0.8.11", - "anyhow", - "browserslist-rs", - "dashmap", - "from_variant", - "once_cell", - "semver 1.0.23", - "serde", - "st-map", - "tracing", -] - [[package]] name = "prettier-please" version = "0.3.0" @@ -8015,15 +7781,6 @@ dependencies = [ "prost", ] -[[package]] -name = "psm" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" -dependencies = [ - "cc", -] - [[package]] name = "ptr_meta" version = "0.1.4" @@ -8131,12 +7888,6 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" -[[package]] -name = "radix_fmt" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce082a9940a7ace2ad4a8b7d0b1eac6aa378895f18be598230c5f2284ac05426" - [[package]] name = "railwind" version = "0.1.5" @@ -8245,7 +7996,7 @@ dependencies = [ "compact_str", "crossterm", "itertools 0.13.0", - "lru 0.12.4", + "lru", "paste", "stability", "strum 0.26.3", @@ -8421,7 +8172,7 @@ dependencies = [ "cvt", "fs_at", "libc", - "normpath 1.3.0", + "normpath", "windows-sys 0.52.0", ] @@ -8729,22 +8480,13 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver 0.9.0", -] - [[package]] name = "rustc_version" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.23", + "semver", ] [[package]] @@ -8869,12 +8611,6 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" -[[package]] -name = "ryu-js" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad97d4ce1560a5e27cec89519dc8300d1aa6035b099821261c651486a19e44d5" - [[package]] name = "same-file" version = "1.0.6" @@ -8994,15 +8730,6 @@ dependencies = [ "thin-slice", ] -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser", -] - [[package]] name = "semver" version = "1.0.23" @@ -9012,12 +8739,6 @@ dependencies = [ "serde", ] -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - [[package]] name = "send_wrapper" version = "0.6.0" @@ -9439,7 +9160,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f20798defa0e9d4eff9ca451c7f84774c7378a9c3b5a40112cfa2b3eadb97ae2" dependencies = [ - "lru 0.12.4", + "lru", "once_cell", "rustc-hash 1.1.0", ] @@ -9471,12 +9192,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "smawk" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" - [[package]] name = "socket2" version = "0.5.7" @@ -9524,25 +9239,6 @@ dependencies = [ "system-deps", ] -[[package]] -name = "sourcemap" -version = "9.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dab08a862c70980b8e23698b507e272317ae52a608a164a844111f5372374f1f" -dependencies = [ - "base64-simd", - "bitvec", - "data-encoding", - "debugid", - "if_chain", - "rustc-hash 1.1.0", - "rustc_version 0.2.3", - "serde", - "serde_json", - "unicode-id-start", - "url", -] - [[package]] name = "spin" version = "0.9.8" @@ -9795,16 +9491,6 @@ dependencies = [ "uuid", ] -[[package]] -name = "st-map" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8257dd592de7614be71a2342d36ba2d527ddad3f9a0c8d09d6ceed4c371531e4" -dependencies = [ - "arrayvec", - "static-map-macro", -] - [[package]] name = "stability" version = "0.2.1" @@ -9822,1105 +9508,135 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] -name = "stacker" -version = "0.1.15" +name = "static_assertions" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c886bd4480155fd3ef527d45e9ac8dd7118a898a46530b7b94c3e21866259fce" -dependencies = [ - "cc", - "cfg-if", - "libc", - "psm", - "winapi", -] - -[[package]] -name = "static-map-macro" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "710e9696ef338691287aeb937ee6ffe60022f579d3c8d2fd9d58973a9a10a466" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.74", -] - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "string_cache" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" -dependencies = [ - "new_debug_unreachable", - "once_cell", - "parking_lot", - "phf_shared 0.10.0", - "precomputed-hash", - "serde", -] - -[[package]] -name = "string_cache_codegen" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" -dependencies = [ - "phf_generator 0.10.0", - "phf_shared 0.10.0", - "proc-macro2", - "quote", -] - -[[package]] -name = "string_enum" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05e383308aebc257e7d7920224fa055c632478d92744eca77f99be8fa1545b90" -dependencies = [ - "proc-macro2", - "quote", - "swc_macros_common", - "syn 2.0.74", -] - -[[package]] -name = "stringprep" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" -dependencies = [ - "unicode-bidi", - "unicode-normalization", - "unicode-properties", -] - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - -[[package]] -name = "strum" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" - -[[package]] -name = "strum" -version = "0.26.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" -dependencies = [ - "strum_macros 0.26.4", -] - -[[package]] -name = "strum_macros" -version = "0.24.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "rustversion", - "syn 1.0.109", -] - -[[package]] -name = "strum_macros" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.74", -] - -[[package]] -name = "subprocess" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c2e86926081dda636c546d8c5e641661049d7562a68f5488be4a1f7f66f6086" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - -[[package]] -name = "supports-color" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6398cde53adc3c4557306a96ce67b302968513830a77a95b2b17305d9719a89" -dependencies = [ - "is-terminal", - "is_ci", -] - -[[package]] -name = "suspense-carousel" -version = "0.6.0-alpha.2" -dependencies = [ - "dioxus", - "gloo-timers 0.3.0", - "serde", - "tokio", -] - -[[package]] -name = "swc" -version = "0.283.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cb7fe4bd6a604528819c84fc9acc5d5ec1b3bd0d11d13ee5d8a77d49ff678fa" -dependencies = [ - "anyhow", - "base64 0.21.7", - "dashmap", - "either", - "indexmap 2.4.0", - "jsonc-parser", - "lru 0.10.1", - "once_cell", - "parking_lot", - "pathdiff", - "regex", - "rustc-hash 1.1.0", - "serde", - "serde_json", - "sourcemap", - "swc_atoms", - "swc_cached", - "swc_common", - "swc_compiler_base", - "swc_config", - "swc_ecma_ast", - "swc_ecma_codegen", - "swc_ecma_ext_transforms", - "swc_ecma_lints", - "swc_ecma_loader", - "swc_ecma_minifier", - "swc_ecma_parser", - "swc_ecma_preset_env", - "swc_ecma_transforms", - "swc_ecma_transforms_base", - "swc_ecma_transforms_compat", - "swc_ecma_transforms_optimization", - "swc_ecma_utils", - "swc_ecma_visit", - "swc_error_reporters", - "swc_node_comments", - "swc_timer", - "swc_transform_common", - "swc_typescript", - "swc_visit", - "tracing", - "url", -] - -[[package]] -name = "swc_allocator" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc8bd3075d1c6964010333fae9ddcd91ad422a4f8eb8b3206a9b2b6afb4209e" -dependencies = [ - "bumpalo", - "hashbrown 0.14.5", - "ptr_meta", - "rustc-hash 1.1.0", - "triomphe", -] - -[[package]] -name = "swc_atoms" -version = "0.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb6567e4e67485b3e7662b486f1565bdae54bd5b9d6b16b2ba1a9babb1e42125" -dependencies = [ - "hstr", - "once_cell", - "rustc-hash 1.1.0", - "serde", -] - -[[package]] -name = "swc_cached" -version = "0.3.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83406221c501860fce9c27444f44125eafe9e598b8b81be7563d7036784cd05c" -dependencies = [ - "ahash 0.8.11", - "anyhow", - "dashmap", - "once_cell", - "regex", - "serde", -] - -[[package]] -name = "swc_common" -version = "0.37.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4d6c716bb706926e22edc992565c98a8f00c0cfa983e97f525f473f9ce2f93f" -dependencies = [ - "ahash 0.8.11", - "ast_node", - "better_scoped_tls", - "cfg-if", - "either", - "from_variant", - "new_debug_unreachable", - "num-bigint", - "once_cell", - "parking_lot", - "rustc-hash 1.1.0", - "serde", - "siphasher", - "sourcemap", - "swc_allocator", - "swc_atoms", - "swc_eq_ignore_macros", - "swc_visit", - "tracing", - "unicode-width", - "url", -] - -[[package]] -name = "swc_compiler_base" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9b47fac27c2e84e033bdea294ac12f575322c81c9d5d44f423e3f11ff239a86" -dependencies = [ - "anyhow", - "base64 0.21.7", - "once_cell", - "pathdiff", - "rustc-hash 1.1.0", - "serde", - "serde_json", - "sourcemap", - "swc_allocator", - "swc_atoms", - "swc_common", - "swc_config", - "swc_ecma_ast", - "swc_ecma_codegen", - "swc_ecma_minifier", - "swc_ecma_parser", - "swc_ecma_visit", - "swc_timer", -] - -[[package]] -name = "swc_config" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4740e53eaf68b101203c1df0937d5161a29f3c13bceed0836ddfe245b72dd000" -dependencies = [ - "anyhow", - "indexmap 2.4.0", - "serde", - "serde_json", - "sourcemap", - "swc_cached", - "swc_config_macro", -] - -[[package]] -name = "swc_config_macro" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c5f56139042c1a95b54f5ca48baa0e0172d369bcc9d3d473dad1de36bae8399" -dependencies = [ - "proc-macro2", - "quote", - "swc_macros_common", - "syn 2.0.74", -] - -[[package]] -name = "swc_ecma_ast" -version = "0.118.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed6c1b94abbaf080a4e4ae47101a83d4eedef90d733dd98e32b361356d3f5e4b" -dependencies = [ - "bitflags 2.6.0", - "is-macro", - "num-bigint", - "phf 0.11.2", - "scoped-tls", - "serde", - "string_enum", - "swc_atoms", - "swc_common", - "unicode-id-start", -] - -[[package]] -name = "swc_ecma_codegen" -version = "0.155.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "644514f303dcad13f7d1c244b50af798e0549f6eb8c53d64dd2ba9824266c868" -dependencies = [ - "memchr", - "num-bigint", - "once_cell", - "serde", - "sourcemap", - "swc_allocator", - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "swc_ecma_codegen_macros", - "tracing", -] - -[[package]] -name = "swc_ecma_codegen_macros" -version = "0.7.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859fabde36db38634f3fad548dd5e3410c1aebba1b67a3c63e67018fa57a0bca" -dependencies = [ - "proc-macro2", - "quote", - "swc_macros_common", - "syn 2.0.74", -] - -[[package]] -name = "swc_ecma_compat_bugfixes" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9f9cac39f19d6509db921f4b75934aaa64fc84b599416e5c1fcaed1c313132f" -dependencies = [ - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "swc_ecma_compat_es2015", - "swc_ecma_transforms_base", - "swc_ecma_utils", - "swc_ecma_visit", - "swc_trace_macro", - "tracing", -] - -[[package]] -name = "swc_ecma_compat_common" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9acdf402b36f8e83084b10e119d7ba9d07e5229ef39e1343f147db816c7b73e" -dependencies = [ - "swc_common", - "swc_ecma_ast", - "swc_ecma_utils", - "swc_ecma_visit", - "swc_trace_macro", -] - -[[package]] -name = "swc_ecma_compat_es2015" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd07cbb52c1ac41115c9ddd5a4d046a7388008bd950b61a48df7f7f490f19827" -dependencies = [ - "arrayvec", - "indexmap 2.4.0", - "is-macro", - "serde", - "serde_derive", - "smallvec", - "swc_atoms", - "swc_common", - "swc_config", - "swc_ecma_ast", - "swc_ecma_compat_common", - "swc_ecma_transforms_base", - "swc_ecma_transforms_classes", - "swc_ecma_transforms_macros", - "swc_ecma_utils", - "swc_ecma_visit", - "swc_trace_macro", - "tracing", -] - -[[package]] -name = "swc_ecma_compat_es2016" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "209e347cdc3fb56632a1d882f981f3448f5f529c16d8da9d770207fffda4a8f6" -dependencies = [ - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "swc_ecma_transforms_base", - "swc_ecma_transforms_macros", - "swc_ecma_utils", - "swc_ecma_visit", - "swc_trace_macro", - "tracing", -] - -[[package]] -name = "swc_ecma_compat_es2017" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a564f1b5e852a0ac656626ba689d49dd2751ba5b980903154aebc971729959d" -dependencies = [ - "serde", - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "swc_ecma_transforms_base", - "swc_ecma_transforms_macros", - "swc_ecma_utils", - "swc_ecma_visit", - "swc_trace_macro", - "tracing", -] - -[[package]] -name = "swc_ecma_compat_es2018" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f577f098e7c3738ade709caadb17c9f3bd911ea2ee6cfacca561d12addcc5761" -dependencies = [ - "serde", - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "swc_ecma_compat_common", - "swc_ecma_transforms_base", - "swc_ecma_transforms_macros", - "swc_ecma_utils", - "swc_ecma_visit", - "swc_trace_macro", - "tracing", -] - -[[package]] -name = "swc_ecma_compat_es2019" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d52253dc2f83a3fca526c387c33e4ff9a8423b68c271414c9f870e1ced3231" -dependencies = [ - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "swc_ecma_transforms_base", - "swc_ecma_utils", - "swc_ecma_visit", - "swc_trace_macro", - "tracing", -] - -[[package]] -name = "swc_ecma_compat_es2020" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed343932876fad34b1d4a13e30c55b94531e89916f45e7c04203bc49a29565b9" -dependencies = [ - "serde", - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "swc_ecma_compat_es2022", - "swc_ecma_transforms_base", - "swc_ecma_utils", - "swc_ecma_visit", - "swc_trace_macro", - "tracing", -] - -[[package]] -name = "swc_ecma_compat_es2021" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b6b28a2c109466eaa809d9b9a5b81dcbb4e269ba293a9c5c34aabc67b6427bc" -dependencies = [ - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "swc_ecma_transforms_base", - "swc_ecma_utils", - "swc_ecma_visit", - "swc_trace_macro", - "tracing", -] - -[[package]] -name = "swc_ecma_compat_es2022" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3a644e271ea2a9df88e3e456c5c204c4916ef5136b7d946f9cd25607f47ec6" -dependencies = [ - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "swc_ecma_compat_common", - "swc_ecma_transforms_base", - "swc_ecma_transforms_classes", - "swc_ecma_transforms_macros", - "swc_ecma_utils", - "swc_ecma_visit", - "swc_trace_macro", - "tracing", -] - -[[package]] -name = "swc_ecma_compat_es3" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e55ffadc12067b21524bf7b5d6938021ee918f65f18937ed27245c23544bc910" -dependencies = [ - "swc_common", - "swc_ecma_ast", - "swc_ecma_transforms_base", - "swc_ecma_utils", - "swc_ecma_visit", - "swc_trace_macro", - "tracing", -] - -[[package]] -name = "swc_ecma_ext_transforms" -version = "0.120.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad03ee53c734eb74757d03c07ec71b1a982261830c9253ef3e2e4a089f9af25d" -dependencies = [ - "phf 0.11.2", - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "swc_ecma_utils", - "swc_ecma_visit", -] - -[[package]] -name = "swc_ecma_lints" -version = "0.99.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20c11bcc9e3dc49929500c07c8b0c84a88064847d31e9ee16204b257e6bd315c" -dependencies = [ - "auto_impl", - "dashmap", - "parking_lot", - "rayon", - "regex", - "serde", - "swc_atoms", - "swc_common", - "swc_config", - "swc_ecma_ast", - "swc_ecma_utils", - "swc_ecma_visit", -] - -[[package]] -name = "swc_ecma_loader" -version = "0.49.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55fa3d55045b97894bfb04d38aff6d6302ac8a6a38e3bb3dfb0d20475c4974a9" -dependencies = [ - "anyhow", - "dashmap", - "lru 0.10.1", - "normpath 0.2.0", - "once_cell", - "parking_lot", - "path-clean 0.1.0", - "pathdiff", - "serde", - "serde_json", - "swc_atoms", - "swc_cached", - "swc_common", - "tracing", -] - -[[package]] -name = "swc_ecma_minifier" -version = "0.201.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d75a42254926bad8b7fa9390767a18ac608d99cfd5a3be675e4739c2cf7db1b" -dependencies = [ - "arrayvec", - "indexmap 2.4.0", - "num-bigint", - "num_cpus", - "once_cell", - "parking_lot", - "phf 0.11.2", - "radix_fmt", - "regex", - "rustc-hash 1.1.0", - "ryu-js", - "serde", - "serde_json", - "swc_allocator", - "swc_atoms", - "swc_common", - "swc_config", - "swc_ecma_ast", - "swc_ecma_codegen", - "swc_ecma_parser", - "swc_ecma_transforms_base", - "swc_ecma_transforms_optimization", - "swc_ecma_usage_analyzer", - "swc_ecma_utils", - "swc_ecma_visit", - "swc_timer", - "tracing", -] +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] -name = "swc_ecma_parser" -version = "0.149.0" +name = "string_cache" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c7a81df222f44212c72fec4879c0d182c6eac66fb0e180afd05e8be6d920663" +checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" dependencies = [ - "either", "new_debug_unreachable", - "num-bigint", - "num-traits", - "phf 0.11.2", - "serde", - "smallvec", - "smartstring", - "stacker", - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "tracing", - "typed-arena", -] - -[[package]] -name = "swc_ecma_preset_env" -version = "0.214.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e85162c77f8c80b55e5aed3a5e5477b74a53ce9cca05dec6e3dabec1de0f49af" -dependencies = [ - "anyhow", - "dashmap", - "indexmap 2.4.0", - "once_cell", - "preset_env_base", - "rustc-hash 1.1.0", - "semver 1.0.23", - "serde", - "serde_json", - "st-map", - "string_enum", - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "swc_ecma_transforms", - "swc_ecma_utils", - "swc_ecma_visit", -] - -[[package]] -name = "swc_ecma_transforms" -version = "0.236.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d289a83ab829d076d6c2bf2cc0beea7fbf0480ac47a415401f683181a65f4856" -dependencies = [ - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "swc_ecma_transforms_base", - "swc_ecma_transforms_compat", - "swc_ecma_transforms_module", - "swc_ecma_transforms_optimization", - "swc_ecma_transforms_proposal", - "swc_ecma_transforms_react", - "swc_ecma_transforms_typescript", - "swc_ecma_utils", - "swc_ecma_visit", -] - -[[package]] -name = "swc_ecma_transforms_base" -version = "0.144.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c0a71579d030e12fd3cfbfc8712c4ce21afc526f2a759903c77d8df61950f5e" -dependencies = [ - "better_scoped_tls", - "bitflags 2.6.0", - "indexmap 2.4.0", "once_cell", - "phf 0.11.2", - "rustc-hash 1.1.0", - "serde", - "smallvec", - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "swc_ecma_parser", - "swc_ecma_utils", - "swc_ecma_visit", - "tracing", -] - -[[package]] -name = "swc_ecma_transforms_classes" -version = "0.133.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f37ec04525798a09ce02e52dc15433acee2d86664da0b8ede55bb5cefd95384" -dependencies = [ - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "swc_ecma_transforms_base", - "swc_ecma_utils", - "swc_ecma_visit", -] - -[[package]] -name = "swc_ecma_transforms_compat" -version = "0.170.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bb500b65423646da940e289ad37e7c88332d7194248c33fc63a9e768e104fe5" -dependencies = [ - "arrayvec", - "indexmap 2.4.0", - "is-macro", - "num-bigint", + "parking_lot", + "phf_shared 0.10.0", + "precomputed-hash", "serde", - "smallvec", - "swc_atoms", - "swc_common", - "swc_config", - "swc_ecma_ast", - "swc_ecma_compat_bugfixes", - "swc_ecma_compat_common", - "swc_ecma_compat_es2015", - "swc_ecma_compat_es2016", - "swc_ecma_compat_es2017", - "swc_ecma_compat_es2018", - "swc_ecma_compat_es2019", - "swc_ecma_compat_es2020", - "swc_ecma_compat_es2021", - "swc_ecma_compat_es2022", - "swc_ecma_compat_es3", - "swc_ecma_transforms_base", - "swc_ecma_transforms_classes", - "swc_ecma_transforms_macros", - "swc_ecma_utils", - "swc_ecma_visit", - "swc_trace_macro", - "tracing", ] [[package]] -name = "swc_ecma_transforms_macros" -version = "0.5.5" +name = "string_cache_codegen" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "500a1dadad1e0e41e417d633b3d6d5de677c9e0d3159b94ba3348436cdb15aab" +checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", "proc-macro2", "quote", - "swc_macros_common", - "syn 2.0.74", -] - -[[package]] -name = "swc_ecma_transforms_module" -version = "0.187.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc022be297cdc70d5e71720587c7e810401a958577d77830f3108411e73c466d" -dependencies = [ - "Inflector", - "anyhow", - "bitflags 2.6.0", - "indexmap 2.4.0", - "is-macro", - "path-clean 1.0.1", - "pathdiff", - "regex", - "serde", - "swc_atoms", - "swc_cached", - "swc_common", - "swc_ecma_ast", - "swc_ecma_loader", - "swc_ecma_parser", - "swc_ecma_transforms_base", - "swc_ecma_utils", - "swc_ecma_visit", - "tracing", -] - -[[package]] -name = "swc_ecma_transforms_optimization" -version = "0.205.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17446e46b75654901d962251ec4d0063423ee81759a325ed82fcf073308d97ca" -dependencies = [ - "dashmap", - "indexmap 2.4.0", - "once_cell", - "petgraph", - "rustc-hash 1.1.0", - "serde_json", - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "swc_ecma_parser", - "swc_ecma_transforms_base", - "swc_ecma_transforms_macros", - "swc_ecma_utils", - "swc_ecma_visit", - "swc_fast_graph", - "tracing", -] - -[[package]] -name = "swc_ecma_transforms_proposal" -version = "0.178.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9c7ddb3aae86f19eb9e41b0c62509d8e400c1dc79c0889df98f6df1ab893f3f" -dependencies = [ - "either", - "rustc-hash 1.1.0", - "serde", - "smallvec", - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "swc_ecma_transforms_base", - "swc_ecma_transforms_classes", - "swc_ecma_transforms_macros", - "swc_ecma_utils", - "swc_ecma_visit", ] [[package]] -name = "swc_ecma_transforms_react" -version = "0.190.0" +name = "stringprep" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3e54a8c87d90812bf69b0f07931bb629111a3f24efe83b9190c3a40a5ebc25e" +checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" dependencies = [ - "base64 0.21.7", - "dashmap", - "indexmap 2.4.0", - "once_cell", - "serde", - "sha1", - "string_enum", - "swc_allocator", - "swc_atoms", - "swc_common", - "swc_config", - "swc_ecma_ast", - "swc_ecma_parser", - "swc_ecma_transforms_base", - "swc_ecma_transforms_macros", - "swc_ecma_utils", - "swc_ecma_visit", + "unicode-bidi", + "unicode-normalization", + "unicode-properties", ] [[package]] -name = "swc_ecma_transforms_typescript" -version = "0.195.1" +name = "strsim" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f814b32ec83fde097df19e7346c429825390d156d0015f321f1f6434b6a06c0c" -dependencies = [ - "ryu-js", - "serde", - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "swc_ecma_transforms_base", - "swc_ecma_transforms_react", - "swc_ecma_utils", - "swc_ecma_visit", -] +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] -name = "swc_ecma_usage_analyzer" -version = "0.30.1" +name = "strsim" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b04b74fc4525b03d1402018ca00855c10421631e4bbda4e202fc877d801acfa" -dependencies = [ - "indexmap 2.4.0", - "rustc-hash 1.1.0", - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "swc_ecma_utils", - "swc_ecma_visit", - "swc_timer", - "tracing", -] +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] -name = "swc_ecma_utils" -version = "0.134.1" +name = "strum" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde8f1ef3f7bd53340c7bd679f1ec563a45225ac8fb63f22d6de1ff4b345475d" -dependencies = [ - "indexmap 2.4.0", - "num_cpus", - "once_cell", - "rustc-hash 1.1.0", - "ryu-js", - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "swc_ecma_visit", - "tracing", - "unicode-id", -] +checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" [[package]] -name = "swc_ecma_visit" -version = "0.104.5" +name = "strum" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71f5f97db49b96208805104b381c5e117f55fad5f3d178e626c92934a4d0e36" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" dependencies = [ - "new_debug_unreachable", - "num-bigint", - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "swc_visit", - "tracing", + "strum_macros 0.26.4", ] [[package]] -name = "swc_eq_ignore_macros" -version = "0.1.4" +name = "strum_macros" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63db0adcff29d220c3d151c5b25c0eabe7e32dd936212b84cdaa1392e3130497" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ + "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.74", -] - -[[package]] -name = "swc_error_reporters" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d049e9256abf29d9fc66d3db3ea44b6815a64ad565ce31e117a74ee96478bb3" -dependencies = [ - "anyhow", - "miette", - "once_cell", - "parking_lot", - "swc_common", -] - -[[package]] -name = "swc_fast_graph" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357e2c97bb51431d65080f25b436bc4e2fc1a7f64a643bc21a8353e478dc799f" -dependencies = [ - "indexmap 2.4.0", - "petgraph", - "rustc-hash 1.1.0", - "swc_common", + "rustversion", + "syn 1.0.109", ] [[package]] -name = "swc_macros_common" -version = "0.3.13" +name = "strum_macros" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f486687bfb7b5c560868f69ed2d458b880cebc9babebcb67e49f31b55c5bf847" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ + "heck 0.5.0", "proc-macro2", "quote", + "rustversion", "syn 2.0.74", ] [[package]] -name = "swc_node_comments" -version = "0.24.0" +name = "subprocess" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d016ab18b432523b2a3c104ce3aaf7d869db46c0a41477dbfb6201ddc86c1eb0" +checksum = "0c2e86926081dda636c546d8c5e641661049d7562a68f5488be4a1f7f66f6086" dependencies = [ - "dashmap", - "swc_atoms", - "swc_common", + "libc", + "winapi", ] [[package]] -name = "swc_timer" -version = "0.25.0" +name = "subtle" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b5fb6f8b8b85512aacbb3d7140a828666e0e0b1bcc69bf84000a0cd36306bab" -dependencies = [ - "tracing", -] +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] -name = "swc_trace_macro" -version = "0.1.3" +name = "supports-color" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff9719b6085dd2824fd61938a881937be14b08f95e2d27c64c825a9f65e052ba" +checksum = "d6398cde53adc3c4557306a96ce67b302968513830a77a95b2b17305d9719a89" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.74", + "is-terminal", + "is_ci", ] [[package]] -name = "swc_transform_common" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eda3e80e1ad638d3575bc07745a914af13dcb02215098659f864731078271f2c" +name = "suspense-carousel" +version = "0.6.0-alpha.2" dependencies = [ - "better_scoped_tls", - "once_cell", - "rustc-hash 1.1.0", + "dioxus", + "gloo-timers 0.3.0", "serde", - "serde_json", -] - -[[package]] -name = "swc_typescript" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5d043347b109a8aebfe01aaeada4af322304ea0f54ae8e5721df9afcb9305ca" -dependencies = [ - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "thiserror", -] - -[[package]] -name = "swc_visit" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ceb044142ba2719ef9eb3b6b454fce61ab849eb696c34d190f04651955c613d" -dependencies = [ - "either", - "new_debug_unreachable", + "tokio", ] [[package]] @@ -11113,7 +9829,7 @@ dependencies = [ "plist", "regex", "rpm", - "semver 1.0.23", + "semver", "serde", "serde_json", "sha1", @@ -11179,7 +9895,7 @@ dependencies = [ "memchr", "phf 0.11.2", "regex", - "semver 1.0.23", + "semver", "serde", "serde-untagged", "serde_json", @@ -11257,17 +9973,6 @@ dependencies = [ "manganis", ] -[[package]] -name = "textwrap" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" -dependencies = [ - "smawk", - "unicode-linebreak", - "unicode-width", -] - [[package]] name = "thin-slice" version = "0.1.1" @@ -11740,16 +10445,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "triomphe" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6631e42e10b40c0690bf92f404ebcfe6e1fdb480391d15f17cc8e96eeed5369" -dependencies = [ - "serde", - "stable_deref_trait", -] - [[package]] name = "try-lock" version = "0.2.5" @@ -11809,12 +10504,6 @@ dependencies = [ "static_assertions", ] -[[package]] -name = "typed-arena" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" - [[package]] name = "typeid" version = "1.0.1" @@ -11915,30 +10604,12 @@ version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7eec5d1121208364f6793f7d2e222bf75a915c19557537745b195b253dd64217" -[[package]] -name = "unicode-id" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1b6def86329695390197b82c1e244a54a131ceb66c996f2088a3876e2ae083f" - -[[package]] -name = "unicode-id-start" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc3882f69607a2ac8cc4de3ee7993d8f68bb06f2974271195065b3bd07f2edea" - [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" -[[package]] -name = "unicode-linebreak" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" - [[package]] name = "unicode-normalization" version = "0.1.23" @@ -12464,7 +11135,7 @@ dependencies = [ "bitflags 2.6.0", "hashbrown 0.14.5", "indexmap 2.4.0", - "semver 1.0.23", + "semver", "serde", ] diff --git a/Cargo.toml b/Cargo.toml index f7510a6a08..2e8aab2038 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,6 @@ members = [ "packages/dioxus-lib", "packages/core", "packages/cli", - "packages/cli-config", "packages/core-types", "packages/core-macro", "packages/config-macro", @@ -66,6 +65,7 @@ members = [ "packages/manganis-test-package/test-package-dependency", "packages/manganis-test-package/test-package-nested-dependency", "packages/document", + "packages/runtime-config", ] exclude = ["examples/mobile_demo", "examples/openid_connect_demo"] @@ -97,7 +97,6 @@ dioxus-check = { path = "packages/check", version = "0.6.0-alpha.2" } dioxus-rsx = { path = "packages/rsx", version = "0.6.0-alpha.2" } rsx-rosetta = { path = "packages/rsx-rosetta", version = "0.6.0-alpha.2" } dioxus-signals = { path = "packages/signals", version = "0.6.0-alpha.2" } -dioxus-cli-config = { path = "packages/cli-config", version = "0.6.0-alpha.2", default-features = false} generational-box = { path = "packages/generational-box", version = "0.6.0-alpha.2" } dioxus-hot-reload = { path = "packages/hot-reload", version = "0.6.0-alpha.2" } dioxus-fullstack = { path = "packages/fullstack", version = "0.6.0-alpha.2" } diff --git a/examples/all_events.rs b/examples/all_events.rs index f111eccc75..112bc290ab 100644 --- a/examples/all_events.rs +++ b/examples/all_events.rs @@ -26,7 +26,7 @@ fn app() -> Element { }; rsx! { - head::Link { rel: "stylesheet", href: STYLE } + document::Link { rel: "stylesheet", href: STYLE } div { id: "container", // focusing is necessary to catch keyboard events div { id: "receiver", tabindex: 0, diff --git a/examples/calculator.rs b/examples/calculator.rs index 49e1d7d581..8f3c371d64 100644 --- a/examples/calculator.rs +++ b/examples/calculator.rs @@ -54,7 +54,7 @@ fn app() -> Element { }; rsx! { - head::Link { rel: "stylesheet", href: STYLE } + document::Link { rel: "stylesheet", href: STYLE } div { id: "wrapper", div { class: "app", div { class: "calculator", tabindex: "0", onkeydown: handle_key_down_event, diff --git a/examples/calculator_mutable.rs b/examples/calculator_mutable.rs index 049f535b33..814fd9220e 100644 --- a/examples/calculator_mutable.rs +++ b/examples/calculator_mutable.rs @@ -29,7 +29,7 @@ fn app() -> Element { let mut state = use_signal(Calculator::new); rsx! { - head::Link { rel: "stylesheet", href: asset!("/examples/assets/calculator.css") } + document::Link { rel: "stylesheet", href: asset!("/examples/assets/calculator.css") } div { id: "wrapper", div { class: "app", div { diff --git a/examples/clock.rs b/examples/clock.rs index 6f59daf37c..283722a5cc 100644 --- a/examples/clock.rs +++ b/examples/clock.rs @@ -38,7 +38,7 @@ fn app() -> Element { ); rsx! { - head::Link { rel: "stylesheet", href: STYLE } + document::Link { rel: "stylesheet", href: STYLE } div { id: "app", div { id: "title", "Carpe diem 🎉" } div { id: "clock-display", "{time}" } diff --git a/examples/control_focus.rs b/examples/control_focus.rs index 208310088c..7e8c7d914a 100644 --- a/examples/control_focus.rs +++ b/examples/control_focus.rs @@ -38,7 +38,7 @@ fn app() -> Element { }); rsx! { - head::Link { rel: "stylesheet", href: STYLE } + document::Link { rel: "stylesheet", href: STYLE } h1 { "Input Roulette" } button { onclick: move |_| running.toggle(), "Toggle roulette" } div { id: "roulette-grid", diff --git a/examples/counters.rs b/examples/counters.rs index bb9d5975a1..626a8cad29 100644 --- a/examples/counters.rs +++ b/examples/counters.rs @@ -16,7 +16,7 @@ fn app() -> Element { let sum = use_memo(move || counters.read().iter().copied().sum::()); rsx! { - head::Link { rel: "stylesheet", href: STYLE } + document::Link { rel: "stylesheet", href: STYLE } div { id: "controls", button { onclick: move |_| counters.write().push(0), "Add counter" } diff --git a/examples/crm.rs b/examples/crm.rs index b05001859c..6e5dd1e17a 100644 --- a/examples/crm.rs +++ b/examples/crm.rs @@ -20,13 +20,13 @@ fn main() { })) .launch(|| { rsx! { - head::Link { + document::Link { rel: "stylesheet", href: asset!("https://unpkg.com/purecss@2.0.6/build/pure-min.css"), integrity: "sha384-Uu6IeWbM+gzNVXJcM9XV3SohHtmWE+3VGi496jvgX1jyvDTXfdK+rfZc8C1Aehk5", crossorigin: "anonymous" } - head::Link { rel: "stylesheet", href: asset!("/examples/assets/crm.css") } + document::Link { rel: "stylesheet", href: asset!("/examples/assets/crm.css") } h1 { "Dioxus CRM Example" } Router:: {} } diff --git a/examples/custom_assets.rs b/examples/custom_assets.rs index e1c7db7ff9..3ae30355d7 100644 --- a/examples/custom_assets.rs +++ b/examples/custom_assets.rs @@ -30,15 +30,15 @@ fn app() -> Element { img { src: ASSET_PATH } // keep support for these too - // img { - // src: "/Users/jonkelley/Development/dioxus/examples/assets/logo.png" - // } - // img { - // src: "/examples/assets/logo.png" - // } - // img { - // src: "examples/assets/logo.png" - // } + img { + src: "/Users/jonkelley/Development/dioxus/examples/assets/logo.png" + } + img { + src: "/examples/assets/logo.png" + } + img { + src: "examples/assets/logo.png" + } } } } diff --git a/examples/dynamic_asset.rs b/examples/dynamic_asset.rs index db0c599cd5..b08732b3e5 100644 --- a/examples/dynamic_asset.rs +++ b/examples/dynamic_asset.rs @@ -25,7 +25,7 @@ fn app() -> Element { }); rsx! { - head::Link { rel: "stylesheet", href: STYLE } + document::Link { rel: "stylesheet", href: STYLE } h1 { "Dynamic Assets" } img { src: "custom://logos/logo.png" } } diff --git a/examples/eval.rs b/examples/eval.rs index d67365c51e..c3f0b7fc1d 100644 --- a/examples/eval.rs +++ b/examples/eval.rs @@ -18,29 +18,25 @@ fn app() -> Element { // The `eval` is available in the prelude - and simply takes a block of JS. // Dioxus' eval is interesting since it allows sending messages to and from the JS code using the `await dioxus.recv()` // builtin function. This allows you to create a two-way communication channel between Rust and JS. - let mut eval = eval( + let mut eval = document::eval( r#" dioxus.send("Hi from JS!"); - let msg = await dioxus.recv(); - console.log(msg); return "hi from JS!"; "#, ); - // Send a message to the JS code. - eval.send("Hi from Rust!".into()).unwrap(); - - // Our line on the JS side will log the message and then return "hello world". - let res = eval.recv().await.unwrap(); - // This will print "Hi from JS!" and "Hi from Rust!". - println!("{:?}", eval.await); + let res = eval.await; + + println!("hello from js! {:?}", res); res }); - match future.value().as_ref() { - Some(v) => rsx!( p { "{v}" } ), - _ => rsx!( p { "waiting.." } ), - } + todo!() + // future.read_unchecked().as_ref().map(|f| match f { + // Some(Ok(v)) => rsx!( p { "{v:?}" } ), + // Some(Err(e)) => rsx!( p { "{v:?}" } ), + // None => rsx!( p { "waiting.." } ), + // }) } diff --git a/examples/file_explorer.rs b/examples/file_explorer.rs index e6f7c789d8..a51b1c4595 100644 --- a/examples/file_explorer.rs +++ b/examples/file_explorer.rs @@ -18,12 +18,12 @@ fn app() -> Element { let mut files = use_signal(Files::new); rsx! { - head::Link { + document::Link { rel: "stylesheet", href: asset!("/examples/assets/fileexplorer.css") } div { - head::Link { href: "https://fonts.googleapis.com/icon?family=Material+Icons", rel: "stylesheet" } + document::Link { href: "https://fonts.googleapis.com/icon?family=Material+Icons", rel: "stylesheet" } header { i { class: "material-icons icon-menu", "menu" } h1 { "Files: " {files.read().current()} } diff --git a/examples/file_upload.rs b/examples/file_upload.rs index 982e8a0b06..f8fd172910 100644 --- a/examples/file_upload.rs +++ b/examples/file_upload.rs @@ -43,7 +43,7 @@ fn app() -> Element { }; rsx! { - head::Link { rel: "stylesheet", href: STYLE } + document::Link { rel: "stylesheet", href: STYLE } h1 { "File Upload Example" } p { "Drop a .txt, .rs, or .js file here to read it" } diff --git a/examples/flat_router.rs b/examples/flat_router.rs index c00e4600f4..33ca137747 100644 --- a/examples/flat_router.rs +++ b/examples/flat_router.rs @@ -14,7 +14,7 @@ const STYLE: Asset = asset!("/examples/assets/flat_router.css"); fn main() { launch(|| { rsx! { - head::Link { rel: "stylesheet", href: STYLE } + document::Link { rel: "stylesheet", href: STYLE } Router:: {} } }) diff --git a/examples/global.rs b/examples/global.rs index d0269322ec..64cdb8662a 100644 --- a/examples/global.rs +++ b/examples/global.rs @@ -18,7 +18,7 @@ fn main() { fn app() -> Element { rsx! { - head::Link { rel: "stylesheet", href: STYLE } + document::Link { rel: "stylesheet", href: STYLE } Increment {} Decrement {} Reset {} diff --git a/examples/image_generator_openai.rs b/examples/image_generator_openai.rs index 474c46de90..71c9eb598c 100644 --- a/examples/image_generator_openai.rs +++ b/examples/image_generator_openai.rs @@ -36,7 +36,7 @@ fn app() -> Element { }); rsx! { - head::Link { rel: "stylesheet", href: "https://unpkg.com/bulma@0.9.0/css/bulma.min.css" } + document::Link { rel: "stylesheet", href: "https://unpkg.com/bulma@0.9.0/css/bulma.min.css" } div { class: "container", div { class: "columns", div { class: "column", diff --git a/examples/link.rs b/examples/link.rs index 7c2b420705..59de6013c4 100644 --- a/examples/link.rs +++ b/examples/link.rs @@ -16,7 +16,7 @@ fn main() { fn app() -> Element { rsx! ( - head::Link { rel: "stylesheet", href: STYLE } + document::Link { rel: "stylesheet", href: STYLE } Router:: {} ) } diff --git a/examples/meta.rs b/examples/meta.rs index 39c2ef3d21..8b3b4a610c 100644 --- a/examples/meta.rs +++ b/examples/meta.rs @@ -11,23 +11,23 @@ fn app() -> Element { // You can use the Meta component to render a meta tag into the head of the page // Meta tags are useful to provide information about the page to search engines and social media sites // This example sets up meta tags for the open graph protocol for social media previews - Meta { + document::Meta { property: "og:title", content: "My Site", } - Meta { + document::Meta { property: "og:type", content: "website", } - Meta { + document::Meta { property: "og:url", content: "https://www.example.com", } - Meta { + document::Meta { property: "og:image", content: "https://example.com/image.jpg", } - Meta { + document::Meta { name: "description", content: "My Site is a site", } diff --git a/examples/overlay.rs b/examples/overlay.rs index e73db187e8..8c675675d5 100644 --- a/examples/overlay.rs +++ b/examples/overlay.rs @@ -20,7 +20,7 @@ fn app() -> Element { _ = use_global_shortcut("cmd+g", move || show_overlay.toggle()); rsx! { - head::Link { + document::Link { rel: "stylesheet", href: asset!("/examples/assets/overlay.css"), } diff --git a/examples/read_size.rs b/examples/read_size.rs index 076a73baac..98c4951590 100644 --- a/examples/read_size.rs +++ b/examples/read_size.rs @@ -28,7 +28,7 @@ fn app() -> Element { }; rsx!( - head::Link { rel: "stylesheet", href: asset!("/examples/assets/read_size.css") } + document::Link { rel: "stylesheet", href: asset!("/examples/assets/read_size.css") } div { width: "50%", height: "50%", diff --git a/examples/reducer.rs b/examples/reducer.rs index 407f1de83b..48e664220f 100644 --- a/examples/reducer.rs +++ b/examples/reducer.rs @@ -17,7 +17,7 @@ fn app() -> Element { let mut state = use_signal(|| PlayerState { is_playing: false }); rsx!( - head::Link { rel: "stylesheet", href: STYLE } + document::Link { rel: "stylesheet", href: STYLE } h1 {"Select an option"} // Add some cute animations if the radio is playing! diff --git a/examples/resize.rs b/examples/resize.rs index 56b4bb798f..2a88529134 100644 --- a/examples/resize.rs +++ b/examples/resize.rs @@ -15,7 +15,7 @@ fn app() -> Element { let mut dimensions = use_signal(Size2D::zero); rsx!( - head::Link { rel: "stylesheet", href: asset!("/examples/assets/read_size.css") } + document::Link { rel: "stylesheet", href: asset!("/examples/assets/read_size.css") } div { width: "50%", height: "50%", diff --git a/examples/router.rs b/examples/router.rs index beed07b518..06679679ee 100644 --- a/examples/router.rs +++ b/examples/router.rs @@ -13,7 +13,7 @@ const STYLE: Asset = asset!("/examples/assets/router.css"); fn main() { launch(|| { rsx! { - head::Link { rel: "stylesheet", href: STYLE } + document::Link { rel: "stylesheet", href: STYLE } Router:: {} } }); diff --git a/examples/tailwind/src/main.rs b/examples/tailwind/src/main.rs index 20ac467d68..3b5d52d1c8 100644 --- a/examples/tailwind/src/main.rs +++ b/examples/tailwind/src/main.rs @@ -9,7 +9,7 @@ fn main() { pub fn app() -> Element { let grey_background = true; rsx!( - Stylesheet { href: asset!("/public/tailwind.css") } + document::Stylesheet { href: asset!("/public/tailwind.css") } div { header { class: "text-gray-400 body-font", diff --git a/examples/title.rs b/examples/title.rs index 18d7bcbb64..02ea15b718 100644 --- a/examples/title.rs +++ b/examples/title.rs @@ -13,7 +13,7 @@ fn app() -> Element { div { // You can set the title of the page with the Title component // In web applications, this sets the title in the head. On desktop, it sets the window title - Title { "My Application (Count {count})" } + document::Title { "My Application (Count {count})" } button { onclick: move |_| count += 1, "Up high!" } button { onclick: move |_| count -= 1, "Down low!" } } diff --git a/examples/todomvc.rs b/examples/todomvc.rs index 6cb748dd4f..6a88e93892 100644 --- a/examples/todomvc.rs +++ b/examples/todomvc.rs @@ -65,7 +65,7 @@ fn app() -> Element { }; rsx! { - head::Link { rel: "stylesheet", href: STYLE } + document::Link { rel: "stylesheet", href: STYLE } section { class: "todoapp", TodoHeader { todos } section { class: "main", diff --git a/examples/weather_app.rs b/examples/weather_app.rs index 395fd81f19..5bc258ff77 100644 --- a/examples/weather_app.rs +++ b/examples/weather_app.rs @@ -19,7 +19,7 @@ fn app() -> Element { let current_weather = use_resource(move || async move { get_weather(&country()).await }); rsx! { - head::Link { rel: "stylesheet", href: "https://unpkg.com/tailwindcss@^2.0/dist/tailwind.min.css" } + document::Link { rel: "stylesheet", href: "https://unpkg.com/tailwindcss@^2.0/dist/tailwind.min.css" } div { class: "mx-auto p-4 bg-gray-100 h-screen flex justify-center", div { class: "flex items-center justify-center flex-row", div { class: "flex items-start justify-center flex-row", diff --git a/examples/window_event.rs b/examples/window_event.rs index cb91d2a5b2..b8ce600f64 100644 --- a/examples/window_event.rs +++ b/examples/window_event.rs @@ -26,7 +26,7 @@ fn main() { fn app() -> Element { rsx!( - head::Link { href: "https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css", rel: "stylesheet" } + document::Link { href: "https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css", rel: "stylesheet" } Header {} div { class: "container mx-auto", div { class: "grid grid-cols-5", diff --git a/packages/cli-config/.gitignore b/packages/cli-config/.gitignore deleted file mode 100644 index 6700b1332d..0000000000 --- a/packages/cli-config/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -/target -Cargo.lock -.DS_Store -.idea/ diff --git a/packages/cli-config/Cargo.toml b/packages/cli-config/Cargo.toml deleted file mode 100644 index ae2caa4573..0000000000 --- a/packages/cli-config/Cargo.toml +++ /dev/null @@ -1,43 +0,0 @@ -[package] -name = "dioxus-cli-config" -version = { workspace = true } -authors = ["Jonathan Kelley"] -edition = "2021" -description = "Configuration for the Dioxus CLI" -repository = "https://github.com/DioxusLabs/dioxus/" -license = "MIT OR Apache-2.0" -keywords = ["react", "gui", "cli", "dioxus", "wasm"] - -[dependencies] -clap = { version = "4.2", features = ["derive"], optional = true } -serde = { version = "1.0.136", features = ["derive"] } -serde_json = "1.0.79" -toml = { workspace = true, optional = true } -cargo_toml = { workspace = true, optional = true } -cargo-config2 = { workspace = true, optional = true } -once_cell = "1.18.0" -tracing = { workspace = true } - -# bundling -tauri-bundler = { workspace = true, optional = true } -tauri-utils = { workspace = true, optional = true } - -dirs = { workspace = true, optional = true } - - -[features] -default = ["read-config"] -cli = [ - "dep:tauri-bundler", - "dep:tauri-utils", - "read-from-args", - "dep:toml", - "dep:cargo_toml", - "dep:dirs", - "dep:cargo-config2", -] -read-config = [] -read-from-args = ["dep:clap"] - -[package.metadata.docs.rs] -cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"] diff --git a/packages/cli-config/README.md b/packages/cli-config/README.md deleted file mode 100644 index 8cad990e33..0000000000 --- a/packages/cli-config/README.md +++ /dev/null @@ -1,5 +0,0 @@ -
-

📦✨ Dioxus CLI Configuration

-
- -The **dioxus-cli-config** contains the configuration for the **dioxus-cli**. diff --git a/packages/cli-config/src/bundle.rs b/packages/cli-config/src/bundle.rs deleted file mode 100644 index 1bef710dbe..0000000000 --- a/packages/cli-config/src/bundle.rs +++ /dev/null @@ -1,271 +0,0 @@ -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; -use std::path::PathBuf; - -#[derive(Debug, Clone, Serialize, Deserialize, Default)] -pub struct BundleConfig { - pub identifier: Option, - pub publisher: Option, - pub icon: Option>, - pub resources: Option>, - pub copyright: Option, - pub category: Option, - pub short_description: Option, - pub long_description: Option, - pub external_bin: Option>, - pub deb: Option, - pub macos: Option, - pub windows: Option, -} - -#[cfg(feature = "cli")] -impl From for tauri_bundler::BundleSettings { - fn from(val: BundleConfig) -> Self { - tauri_bundler::BundleSettings { - identifier: val.identifier, - publisher: val.publisher, - icon: val.icon, - resources: val.resources, - copyright: val.copyright, - category: val.category.and_then(|c| c.parse().ok()), - short_description: val.short_description, - long_description: val.long_description, - external_bin: val.external_bin, - deb: val.deb.map(Into::into).unwrap_or_default(), - macos: val.macos.map(Into::into).unwrap_or_default(), - windows: val.windows.map(Into::into).unwrap_or_default(), - ..Default::default() - } - } -} - -#[derive(Debug, Clone, Serialize, Deserialize, Default)] -pub struct DebianSettings { - pub depends: Option>, - pub files: HashMap, - pub nsis: Option, -} - -#[cfg(feature = "cli")] -impl From for tauri_bundler::DebianSettings { - fn from(val: DebianSettings) -> Self { - tauri_bundler::DebianSettings { - depends: val.depends, - files: val.files, - desktop_template: None, - provides: todo!(), - conflicts: todo!(), - replaces: todo!(), - section: todo!(), - priority: todo!(), - changelog: todo!(), - pre_install_script: todo!(), - post_install_script: todo!(), - pre_remove_script: todo!(), - post_remove_script: todo!(), - } - } -} - -#[derive(Debug, Clone, Serialize, Deserialize, Default)] -pub struct WixSettings { - pub language: Vec<(String, Option)>, - pub template: Option, - pub fragment_paths: Vec, - pub component_group_refs: Vec, - pub component_refs: Vec, - pub feature_group_refs: Vec, - pub feature_refs: Vec, - pub merge_refs: Vec, - pub skip_webview_install: bool, - pub license: Option, - pub enable_elevated_update_task: bool, - pub banner_path: Option, - pub dialog_image_path: Option, - pub fips_compliant: bool, -} - -#[cfg(feature = "cli")] -impl From for tauri_bundler::WixSettings { - fn from(val: WixSettings) -> Self { - tauri_bundler::WixSettings { - language: tauri_bundler::bundle::WixLanguage({ - let mut languages: Vec<_> = val - .language - .iter() - .map(|l| { - ( - l.0.clone(), - tauri_bundler::bundle::WixLanguageConfig { - locale_path: l.1.clone(), - }, - ) - }) - .collect(); - if languages.is_empty() { - languages.push(("en-US".into(), Default::default())); - } - languages - }), - template: val.template, - fragment_paths: val.fragment_paths, - component_group_refs: val.component_group_refs, - component_refs: val.component_refs, - feature_group_refs: val.feature_group_refs, - feature_refs: val.feature_refs, - merge_refs: val.merge_refs, - enable_elevated_update_task: val.enable_elevated_update_task, - banner_path: val.banner_path, - dialog_image_path: val.dialog_image_path, - fips_compliant: val.fips_compliant, - } - } -} - -#[derive(Debug, Clone, Serialize, Deserialize, Default)] -pub struct MacOsSettings { - pub frameworks: Option>, - pub minimum_system_version: Option, - pub license: Option, - pub exception_domain: Option, - pub signing_identity: Option, - pub provider_short_name: Option, - pub entitlements: Option, - pub info_plist_path: Option, -} - -#[cfg(feature = "cli")] -impl From for tauri_bundler::MacOsSettings { - fn from(val: MacOsSettings) -> Self { - tauri_bundler::MacOsSettings { - frameworks: val.frameworks, - minimum_system_version: val.minimum_system_version, - exception_domain: val.exception_domain, - signing_identity: val.signing_identity, - provider_short_name: val.provider_short_name, - entitlements: val.entitlements, - info_plist_path: val.info_plist_path, - files: todo!(), - hardened_runtime: todo!(), - } - } -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct WindowsSettings { - pub digest_algorithm: Option, - pub certificate_thumbprint: Option, - pub timestamp_url: Option, - pub tsp: bool, - pub wix: Option, - pub icon_path: Option, - pub webview_install_mode: WebviewInstallMode, - pub webview_fixed_runtime_path: Option, - pub allow_downgrades: bool, - pub nsis: Option, -} - -#[cfg(feature = "cli")] -impl From for tauri_bundler::WindowsSettings { - fn from(val: WindowsSettings) -> Self { - tauri_bundler::WindowsSettings { - digest_algorithm: val.digest_algorithm, - certificate_thumbprint: val.certificate_thumbprint, - timestamp_url: val.timestamp_url, - tsp: val.tsp, - wix: val.wix.map(Into::into), - icon_path: val.icon_path.unwrap_or("icons/icon.ico".into()), - webview_install_mode: val.webview_install_mode.into(), - webview_fixed_runtime_path: val.webview_fixed_runtime_path, - allow_downgrades: val.allow_downgrades, - nsis: val.nsis.map(Into::into), - sign_command: todo!(), - } - } -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct NsisSettings { - pub template: Option, - pub license: Option, - pub header_image: Option, - pub sidebar_image: Option, - pub installer_icon: Option, - pub install_mode: NSISInstallerMode, - pub languages: Option>, - pub custom_language_files: Option>, - pub display_language_selector: bool, -} - -#[cfg(feature = "cli")] -impl From for tauri_bundler::NsisSettings { - fn from(val: NsisSettings) -> Self { - tauri_bundler::NsisSettings { - header_image: val.header_image, - sidebar_image: val.sidebar_image, - installer_icon: val.installer_icon, - install_mode: val.install_mode.into(), - languages: val.languages, - display_language_selector: val.display_language_selector, - custom_language_files: None, - template: None, - compression: tauri_utils::config::NsisCompression::None, - start_menu_folder: todo!(), - installer_hooks: todo!(), - } - } -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub enum NSISInstallerMode { - CurrentUser, - PerMachine, - Both, -} - -#[cfg(feature = "cli")] -impl From for tauri_utils::config::NSISInstallerMode { - fn from(val: NSISInstallerMode) -> Self { - match val { - NSISInstallerMode::CurrentUser => tauri_utils::config::NSISInstallerMode::CurrentUser, - NSISInstallerMode::PerMachine => tauri_utils::config::NSISInstallerMode::PerMachine, - NSISInstallerMode::Both => tauri_utils::config::NSISInstallerMode::Both, - } - } -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub enum WebviewInstallMode { - Skip, - DownloadBootstrapper { silent: bool }, - EmbedBootstrapper { silent: bool }, - OfflineInstaller { silent: bool }, - FixedRuntime { path: PathBuf }, -} - -#[cfg(feature = "cli")] -impl WebviewInstallMode { - fn into(self) -> tauri_utils::config::WebviewInstallMode { - match self { - Self::Skip => tauri_utils::config::WebviewInstallMode::Skip, - Self::DownloadBootstrapper { silent } => { - tauri_utils::config::WebviewInstallMode::DownloadBootstrapper { silent } - } - Self::EmbedBootstrapper { silent } => { - tauri_utils::config::WebviewInstallMode::EmbedBootstrapper { silent } - } - Self::OfflineInstaller { silent } => { - tauri_utils::config::WebviewInstallMode::OfflineInstaller { silent } - } - Self::FixedRuntime { path } => { - tauri_utils::config::WebviewInstallMode::FixedRuntime { path } - } - } - } -} - -impl Default for WebviewInstallMode { - fn default() -> Self { - Self::OfflineInstaller { silent: false } - } -} diff --git a/packages/cli-config/src/lib.rs b/packages/cli-config/src/lib.rs deleted file mode 100644 index 9fc704c3c5..0000000000 --- a/packages/cli-config/src/lib.rs +++ /dev/null @@ -1,77 +0,0 @@ -#![doc = include_str!("../README.md")] -#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/79236386")] -#![doc(html_favicon_url = "https://avatars.githubusercontent.com/u/79236386")] - -mod config; -pub use config::*; - -mod bundle; -pub use bundle::*; - -mod serve; -pub use serve::*; -/// An error that occurs when the dioxus CLI was not used to build the application. -#[derive(Debug)] -pub struct DioxusCLINotUsed; - -impl std::fmt::Display for DioxusCLINotUsed { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str("dioxus CLI was not used to build the application") - } -} - -impl std::error::Error for DioxusCLINotUsed {} - -#[cfg(feature = "read-config")] -/// The current crate's configuration. -pub static CURRENT_CONFIG: once_cell::sync::Lazy< - Result, -> = once_cell::sync::Lazy::new(|| { - // todo: somehow support wasm? - std::env::var("DIOXUS_CONFIG") - .ok() - .and_then(|config| serde_json::from_str(&config).ok()) - .ok_or_else(|| { - // let mut cli_version = crate::build_info::PKG_VERSION.to_string(); - - // if let Some(hash) = crate::build_info::GIT_COMMIT_HASH_SHORT { - // let hash = &hash.trim_start_matches('g')[..4]; - // cli_version.push_str(&format!("-{hash}")); - // } - - // tracing::warn!("Failed to parse the CLI config file. This is likely caused by a mismatch between the version of the CLI and the dioxus version.\nCLI version: {cli_version}\nDioxus version: {dioxus_version}\nSerialization error: {err}"); - // Err(DioxusCLINotUsed) - // #[doc(hidden)] - // pub mod __private { - // use crate::DioxusConfig; - - // pub(crate) const DIOXUS_CLI_VERSION: &str = "DIOXUS_CLI_VERSION"; - // pub(crate) const CONFIG_ENV: &str = "DIOXUS_CONFIG"; - // pub(crate) const CONFIG_BASE_PATH_ENV: &str = "DIOXUS_CONFIG_BASE_PATH"; - - // pub fn env_args() -> Vec<(&'static str, &'static str)> { - // vec![ - // (CONFIG_ENV, "DIOXUS_CONFIG"), - // (CONFIG_BASE_PATH_ENV, "DIOXUS_CONFIG_BASE_PATH"), - // (DIOXUS_CLI_VERSION, "DIOXUS_CLI_VERSION"), - // ] - // } - - // #[cfg(feature = "read-config")] - // /// The environment variable that stores the CLIs serve configuration. - // /// We use this to communicate between the CLI and the server for fullstack applications. - // pub const SERVE_ENV: &str = "DIOXUS_SERVE_CONFIG"; - // } - - tracing::warn!("A library is trying to access the crate's configuration, but the dioxus CLI was not used to build the application."); - DioxusCLINotUsed - }) -}); - -/// Get the "base path" from the Dioxus.toml file -/// -/// This is typically the offset of the asset from its domain -pub fn base_path() -> Option<&'static str> { - // todo!() - None -} diff --git a/packages/cli/Cargo.toml b/packages/cli/Cargo.toml index 7d1e609e25..9f7ee74405 100644 --- a/packages/cli/Cargo.toml +++ b/packages/cli/Cargo.toml @@ -16,7 +16,6 @@ thiserror = { workspace = true } wasm-bindgen-cli-support = "0.2" wasm-bindgen-shared = "0.2" colored = "2.0.0" -dioxus-cli-config = { workspace = true, features = ["cli"], default-features = false } # features log = "0.4.14" diff --git a/packages/cli/src/builder/cargo.rs b/packages/cli/src/builder/cargo.rs index d2f627b471..308942d299 100644 --- a/packages/cli/src/builder/cargo.rs +++ b/packages/cli/src/builder/cargo.rs @@ -10,10 +10,10 @@ use crate::builder::progress::CargoBuildResult; use crate::builder::progress::Stage; use crate::builder::progress::UpdateBuildProgress; use crate::builder::progress::UpdateStage; +use crate::config::Platform; use crate::link::LinkCommand; use crate::Result; use anyhow::Context; -use dioxus_cli_config::Platform; use futures_channel::mpsc::UnboundedSender; use manganis_cli_support::AssetManifest; use std::fs::create_dir_all; diff --git a/packages/cli/src/builder/mod.rs b/packages/cli/src/builder/mod.rs index 21a851058d..010f03c5e8 100644 --- a/packages/cli/src/builder/mod.rs +++ b/packages/cli/src/builder/mod.rs @@ -1,8 +1,7 @@ -use crate::cli::serve::ServeArguments; use crate::dioxus_crate::DioxusCrate; use crate::Result; use crate::{build::Build, config}; -use dioxus_cli_config::{Platform, RuntimeCLIArguments}; +use crate::{cli::serve::ServeArguments, config::Platform}; use futures_util::stream::select_all; use futures_util::StreamExt; use std::net::SocketAddr; @@ -160,20 +159,21 @@ impl BuildResult { tracing::trace!("Proxying fullstack server from port {fullstack_address:?}"); } - let arguments = RuntimeCLIArguments::new(serve.address.address(), fullstack_address); - let executable = self.executable.canonicalize()?; - let mut cmd = Command::new(executable); - cmd - // When building the fullstack server, we need to forward the serve arguments (like port) to the fullstack server through env vars - // .env( - // dioxus_cli_config::__private::SERVE_ENV, - // serde_json::to_string(&arguments).unwrap(), - // ) - .env("CARGO_MANIFEST_DIR", config.crate_dir()) - .stderr(Stdio::piped()) - .stdout(Stdio::piped()) - .kill_on_drop(true) - .current_dir(workspace); - Ok(Some(cmd.spawn()?)) + todo!("set runtime env vars for the fullstack server") + // let arguments = RuntimeCLIArguments::new(serve.address.address(), fullstack_address); + // let executable = self.executable.canonicalize()?; + // let mut cmd = Command::new(executable); + // cmd + // // When building the fullstack server, we need to forward the serve arguments (like port) to the fullstack server through env vars + // // .env( + // // dioxus_cli_config::__private::SERVE_ENV, + // // serde_json::to_string(&arguments).unwrap(), + // // ) + // .env("CARGO_MANIFEST_DIR", config.crate_dir()) + // .stderr(Stdio::piped()) + // .stdout(Stdio::piped()) + // .kill_on_drop(true) + // .current_dir(workspace); + // Ok(Some(cmd.spawn()?)) } } diff --git a/packages/cli/src/builder/prepare_html.rs b/packages/cli/src/builder/prepare_html.rs index 0b2dcaa6dc..b2277dfe30 100644 --- a/packages/cli/src/builder/prepare_html.rs +++ b/packages/cli/src/builder/prepare_html.rs @@ -145,7 +145,7 @@ impl BuildRequest { paths: Vec, variant: ResourceType, ) { - const RESOURCE_DEPRECATION_MESSAGE: &str = r#"The `web.resource` config has been deprecated in favor of head components and will be removed in a future release. Instead of including assets in the config, you can include assets with the `asset!` macro and add them to the head with `head::Link` and `Script` components."#; + const RESOURCE_DEPRECATION_MESSAGE: &str = r#"The `web.resource` config has been deprecated in favor of head components and will be removed in a future release. Instead of including assets in the config, you can include assets with the `asset!` macro and add them to the head with `document::Link` and `Script` components."#; let replacement_components = paths .iter() @@ -168,7 +168,7 @@ impl BuildRequest { }; match variant { ResourceType::Style => format!( - " head::Link {{ rel: \"stylesheet\", href: asset!(css(\"{}\")) }}", + " document::Link {{ rel: \"stylesheet\", href: asset!(css(\"{}\")) }}", path.display() ), ResourceType::Script => { diff --git a/packages/cli/src/bundle_utils.rs b/packages/cli/src/bundle_utils.rs new file mode 100644 index 0000000000..34016098cf --- /dev/null +++ b/packages/cli/src/bundle_utils.rs @@ -0,0 +1,165 @@ +// use dioxus_cli_config::BundleConfig; + +use crate::config::BundleConfig; + +pub fn make_tauri_bundler_settings(bundle_config: BundleConfig) -> tauri_bundler::BundleSettings { + todo!() +} + +// impl From for tauri_bundler::NsisSettings { +// fn from(val: NsisSettings) -> Self { +// tauri_bundler::NsisSettings { +// header_image: val.header_image, +// sidebar_image: val.sidebar_image, +// installer_icon: val.installer_icon, +// install_mode: val.install_mode.into(), +// languages: val.languages, +// display_language_selector: val.display_language_selector, +// custom_language_files: None, +// template: None, +// compression: tauri_utils::config::NsisCompression::None, +// start_menu_folder: todo!(), +// installer_hooks: todo!(), +// } +// } +// } + +// impl From for tauri_bundler::BundleSettings { +// fn from(val: BundleConfig) -> Self { +// tauri_bundler::BundleSettings { +// identifier: val.identifier, +// publisher: val.publisher, +// icon: val.icon, +// resources: val.resources, +// copyright: val.copyright, +// category: val.category.and_then(|c| c.parse().ok()), +// short_description: val.short_description, +// long_description: val.long_description, +// external_bin: val.external_bin, +// deb: val.deb.map(Into::into).unwrap_or_default(), +// macos: val.macos.map(Into::into).unwrap_or_default(), +// windows: val.windows.map(Into::into).unwrap_or_default(), +// ..Default::default() +// } +// } +// } + +// impl From for tauri_bundler::DebianSettings { +// fn from(val: DebianSettings) -> Self { +// tauri_bundler::DebianSettings { +// depends: val.depends, +// files: val.files, +// desktop_template: None, +// provides: todo!(), +// conflicts: todo!(), +// replaces: todo!(), +// section: todo!(), +// priority: todo!(), +// changelog: todo!(), +// pre_install_script: todo!(), +// post_install_script: todo!(), +// pre_remove_script: todo!(), +// post_remove_script: todo!(), +// } +// } +// } + +// impl From for tauri_bundler::WixSettings { +// fn from(val: WixSettings) -> Self { +// tauri_bundler::WixSettings { +// language: tauri_bundler::bundle::WixLanguage({ +// let mut languages: Vec<_> = val +// .language +// .iter() +// .map(|l| { +// ( +// l.0.clone(), +// tauri_bundler::bundle::WixLanguageConfig { +// locale_path: l.1.clone(), +// }, +// ) +// }) +// .collect(); +// if languages.is_empty() { +// languages.push(("en-US".into(), Default::default())); +// } +// languages +// }), +// template: val.template, +// fragment_paths: val.fragment_paths, +// component_group_refs: val.component_group_refs, +// component_refs: val.component_refs, +// feature_group_refs: val.feature_group_refs, +// feature_refs: val.feature_refs, +// merge_refs: val.merge_refs, +// enable_elevated_update_task: val.enable_elevated_update_task, +// banner_path: val.banner_path, +// dialog_image_path: val.dialog_image_path, +// fips_compliant: val.fips_compliant, +// } +// } +// } + +// impl From for tauri_bundler::MacOsSettings { +// fn from(val: MacOsSettings) -> Self { +// tauri_bundler::MacOsSettings { +// frameworks: val.frameworks, +// minimum_system_version: val.minimum_system_version, +// exception_domain: val.exception_domain, +// signing_identity: val.signing_identity, +// provider_short_name: val.provider_short_name, +// entitlements: val.entitlements, +// info_plist_path: val.info_plist_path, +// files: todo!(), +// hardened_runtime: todo!(), +// } +// } +// } + +// impl From for tauri_bundler::WindowsSettings { +// fn from(val: WindowsSettings) -> Self { +// tauri_bundler::WindowsSettings { +// digest_algorithm: val.digest_algorithm, +// certificate_thumbprint: val.certificate_thumbprint, +// timestamp_url: val.timestamp_url, +// tsp: val.tsp, +// wix: val.wix.map(Into::into), +// icon_path: val.icon_path.unwrap_or("icons/icon.ico".into()), +// webview_install_mode: val.webview_install_mode.into(), +// webview_fixed_runtime_path: val.webview_fixed_runtime_path, +// allow_downgrades: val.allow_downgrades, +// nsis: val.nsis.map(Into::into), +// sign_command: todo!(), +// } +// } +// } + +// impl From for tauri_utils::config::NSISInstallerMode { +// fn from(val: NSISInstallerMode) -> Self { +// match val { +// NSISInstallerMode::CurrentUser => tauri_utils::config::NSISInstallerMode::CurrentUser, +// NSISInstallerMode::PerMachine => tauri_utils::config::NSISInstallerMode::PerMachine, +// NSISInstallerMode::Both => tauri_utils::config::NSISInstallerMode::Both, +// } +// } +// } + +// impl WebviewInstallMode { +// fn into(self) -> tauri_utils::config::WebviewInstallMode { +// match self { +// Self::Skip => tauri_utils::config::WebviewInstallMode::Skip, +// Self::DownloadBootstrapper { silent } => { +// tauri_utils::config::WebviewInstallMode::DownloadBootstrapper { silent } +// } +// Self::EmbedBootstrapper { silent } => { +// tauri_utils::config::WebviewInstallMode::EmbedBootstrapper { silent } +// } +// Self::OfflineInstaller { silent } => { +// tauri_utils::config::WebviewInstallMode::OfflineInstaller { silent } +// } +// Self::FixedRuntime { path } => { +// tauri_utils::config::WebviewInstallMode::FixedRuntime { path } +// } +// } +// } +// } diff --git a/packages/cli/src/cli/build.rs b/packages/cli/src/cli/build.rs index e7c653faad..5ae69825df 100644 --- a/packages/cli/src/cli/build.rs +++ b/packages/cli/src/cli/build.rs @@ -1,7 +1,6 @@ -use anyhow::Context; -use dioxus_cli_config::Platform; - +use crate::config::Platform; use crate::{builder::BuildRequest, dioxus_crate::DioxusCrate}; +use anyhow::Context; use super::*; diff --git a/packages/cli/src/cli/bundle.rs b/packages/cli/src/cli/bundle.rs index 9b4597a8b9..abdbbaf651 100644 --- a/packages/cli/src/cli/bundle.rs +++ b/packages/cli/src/cli/bundle.rs @@ -1,5 +1,5 @@ -use crate::build::Build; use crate::DioxusCrate; +use crate::{build::Build, bundle_utils::make_tauri_bundler_settings}; use anyhow::Context; use std::env::current_dir; use std::fs::create_dir_all; @@ -96,7 +96,9 @@ impl Bundle { .set_src_path(Some(dioxus_crate.workspace_dir().display().to_string())), ]; - let mut bundle_settings: BundleSettings = dioxus_crate.dioxus_config.bundle.clone().into(); + let bundle_config = dioxus_crate.dioxus_config.bundle.clone(); + let mut bundle_settings = make_tauri_bundler_settings(bundle_config); + if cfg!(windows) { let windows_icon_override = dioxus_crate .dioxus_config diff --git a/packages/cli/src/cli/serve.rs b/packages/cli/src/cli/serve.rs index f0ac6a8290..381f5f44bd 100644 --- a/packages/cli/src/cli/serve.rs +++ b/packages/cli/src/cli/serve.rs @@ -1,3 +1,4 @@ +use crate::config::AddressArguments; use crate::{ settings::{self}, tracer::CLILogControl, @@ -5,7 +6,6 @@ use crate::{ }; use anyhow::Context; use build::Build; -use dioxus_cli_config::AddressArguments; use std::ops::Deref; use super::*; diff --git a/packages/cli/src/config.rs b/packages/cli/src/config.rs new file mode 100644 index 0000000000..c569fd5412 --- /dev/null +++ b/packages/cli/src/config.rs @@ -0,0 +1,17 @@ +mod app; +mod bundle; +mod desktop; +mod dioxus_config; +// mod fullstack; +// mod liveview; +mod platform; +mod serve; +// mod static_generation; +mod web; + +pub use app::*; +pub use bundle::*; +pub use dioxus_config::*; +pub use platform::*; +pub use serve::*; +pub use web::*; diff --git a/packages/cli-config/src/config.rs b/packages/cli/src/config/app.rs similarity index 53% rename from packages/cli-config/src/config.rs rename to packages/cli/src/config/app.rs index 5ef83a6304..be2d9b9b28 100644 --- a/packages/cli-config/src/config.rs +++ b/packages/cli/src/config/app.rs @@ -1,153 +1,6 @@ -use crate::BundleConfig; +use super::Platform; use serde::{Deserialize, Serialize}; -use std::fmt::Display; use std::path::PathBuf; -use std::str::FromStr; - -#[derive( - Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Debug, Default, -)] -#[cfg_attr(feature = "cli", derive(clap::ValueEnum))] -#[non_exhaustive] -pub enum Platform { - /// Targeting the web platform using WASM - #[cfg_attr(feature = "cli", clap(name = "web"))] - #[serde(rename = "web")] - #[default] - Web, - - /// Targeting the desktop platform using Tao/Wry-based webview - #[cfg_attr(feature = "cli", clap(name = "desktop"))] - #[serde(rename = "desktop")] - Desktop, - - /// Targeting the server platform using Axum and Dioxus-Fullstack - #[cfg_attr(feature = "cli", clap(name = "fullstack"))] - #[serde(rename = "fullstack")] - Fullstack, - - /// Targeting the static generation platform using SSR and Dioxus-Fullstack - #[cfg_attr(feature = "cli", clap(name = "static-generation"))] - #[serde(rename = "static-generation")] - StaticGeneration, - - /// Targeting the static generation platform using SSR and Dioxus-Fullstack - #[cfg_attr(feature = "cli", clap(name = "liveview"))] - #[serde(rename = "liveview")] - Liveview, -} - -/// An error that occurs when a platform is not recognized -pub struct UnknownPlatformError; - -impl std::fmt::Display for UnknownPlatformError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Unknown platform") - } -} - -impl FromStr for Platform { - type Err = UnknownPlatformError; - - fn from_str(s: &str) -> Result { - match s { - "web" => Ok(Self::Web), - "desktop" => Ok(Self::Desktop), - "fullstack" => Ok(Self::Fullstack), - "static-generation" => Ok(Self::StaticGeneration), - "liveview" => Ok(Self::Liveview), - _ => Err(UnknownPlatformError), - } - } -} - -impl Display for Platform { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let feature = self.feature_name(); - f.write_str(feature) - } -} - -impl Platform { - /// All platforms the dioxus CLI supports - pub const ALL: &'static [Self] = &[ - Platform::Web, - Platform::Desktop, - Platform::Fullstack, - Platform::StaticGeneration, - ]; - - /// Get the feature name for the platform in the dioxus crate - pub fn feature_name(&self) -> &str { - match self { - Platform::Web => "web", - Platform::Desktop => "desktop", - Platform::Fullstack => "fullstack", - Platform::StaticGeneration => "static-generation", - Platform::Liveview => "liveview", - } - } -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct DioxusConfig { - pub application: ApplicationConfig, - - #[serde(default)] - pub web: WebConfig, - - #[serde(default)] - pub desktop: DesktopConfig, - - #[serde(default)] - pub bundle: BundleConfig, -} - -impl Default for DioxusConfig { - fn default() -> Self { - let name = default_name(); - Self { - application: ApplicationConfig { - name: name.clone(), - default_platform: default_platform(), - out_dir: out_dir_default(), - asset_dir: asset_dir_default(), - - sub_package: None, - }, - web: WebConfig { - app: WebAppConfig { - title: default_title(), - base_path: None, - }, - proxy: vec![], - watcher: Default::default(), - resource: WebResourceConfig { - dev: WebDevResourceConfig { - style: vec![], - script: vec![], - }, - style: Some(vec![]), - script: Some(vec![]), - }, - https: WebHttpsConfig { - enabled: None, - mkcert: None, - key_path: None, - cert_path: None, - }, - pre_compress: true, - wasm_opt: Default::default(), - }, - desktop: DesktopConfig::default(), - bundle: BundleConfig { - identifier: Some(format!("io.github.{name}")), - publisher: Some(name), - ..Default::default() - }, - } - } -} #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ApplicationConfig { @@ -167,19 +20,19 @@ pub struct ApplicationConfig { pub sub_package: Option, } -fn default_name() -> String { +pub fn default_name() -> String { "my-cool-project".into() } -fn default_platform() -> Platform { +pub fn default_platform() -> Platform { Platform::Web } -fn asset_dir_default() -> PathBuf { +pub fn asset_dir_default() -> PathBuf { PathBuf::from("public") } -fn out_dir_default() -> PathBuf { +pub fn out_dir_default() -> PathBuf { PathBuf::from("dist") } @@ -187,17 +40,23 @@ fn out_dir_default() -> PathBuf { pub struct WebConfig { #[serde(default)] pub app: WebAppConfig, + #[serde(default)] pub proxy: Vec, + #[serde(default)] pub watcher: WebWatcherConfig, + #[serde(default)] pub resource: WebResourceConfig, + #[serde(default)] pub https: WebHttpsConfig, + /// Whether to enable pre-compression of assets and wasm during a web build in release mode #[serde(default = "true_bool")] pub pre_compress: bool, + /// The wasm-opt configuration #[serde(default)] pub wasm_opt: WasmOptConfig, @@ -305,7 +164,7 @@ impl Default for WebAppConfig { } } -fn default_title() -> String { +pub fn default_title() -> String { "dioxus | ⛺".into() } diff --git a/packages/cli/src/config/bundle.rs b/packages/cli/src/config/bundle.rs new file mode 100644 index 0000000000..2c2aea5068 --- /dev/null +++ b/packages/cli/src/config/bundle.rs @@ -0,0 +1,105 @@ +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use std::path::PathBuf; + +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct BundleConfig { + pub identifier: Option, + pub publisher: Option, + pub icon: Option>, + pub resources: Option>, + pub copyright: Option, + pub category: Option, + pub short_description: Option, + pub long_description: Option, + pub external_bin: Option>, + pub deb: Option, + pub macos: Option, + pub windows: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct DebianSettings { + pub depends: Option>, + pub files: HashMap, + pub nsis: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct WixSettings { + pub language: Vec<(String, Option)>, + pub template: Option, + pub fragment_paths: Vec, + pub component_group_refs: Vec, + pub component_refs: Vec, + pub feature_group_refs: Vec, + pub feature_refs: Vec, + pub merge_refs: Vec, + pub skip_webview_install: bool, + pub license: Option, + pub enable_elevated_update_task: bool, + pub banner_path: Option, + pub dialog_image_path: Option, + pub fips_compliant: bool, +} + +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct MacOsSettings { + pub frameworks: Option>, + pub minimum_system_version: Option, + pub license: Option, + pub exception_domain: Option, + pub signing_identity: Option, + pub provider_short_name: Option, + pub entitlements: Option, + pub info_plist_path: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WindowsSettings { + pub digest_algorithm: Option, + pub certificate_thumbprint: Option, + pub timestamp_url: Option, + pub tsp: bool, + pub wix: Option, + pub icon_path: Option, + pub webview_install_mode: WebviewInstallMode, + pub webview_fixed_runtime_path: Option, + pub allow_downgrades: bool, + pub nsis: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct NsisSettings { + pub template: Option, + pub license: Option, + pub header_image: Option, + pub sidebar_image: Option, + pub installer_icon: Option, + pub install_mode: NSISInstallerMode, + pub languages: Option>, + pub custom_language_files: Option>, + pub display_language_selector: bool, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum NSISInstallerMode { + CurrentUser, + PerMachine, + Both, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum WebviewInstallMode { + Skip, + DownloadBootstrapper { silent: bool }, + EmbedBootstrapper { silent: bool }, + OfflineInstaller { silent: bool }, + FixedRuntime { path: PathBuf }, +} + +impl Default for WebviewInstallMode { + fn default() -> Self { + Self::OfflineInstaller { silent: false } + } +} diff --git a/packages/cli/src/config/desktop.rs b/packages/cli/src/config/desktop.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/cli/src/config/dioxus_config.rs b/packages/cli/src/config/dioxus_config.rs new file mode 100644 index 0000000000..ecc9d52caa --- /dev/null +++ b/packages/cli/src/config/dioxus_config.rs @@ -0,0 +1,63 @@ +use super::*; +use serde::{Deserialize, Serialize}; +use std::path::PathBuf; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct DioxusConfig { + pub application: ApplicationConfig, + + #[serde(default)] + pub web: WebConfig, + + #[serde(default)] + pub desktop: DesktopConfig, + + #[serde(default)] + pub bundle: BundleConfig, +} + +impl Default for DioxusConfig { + fn default() -> Self { + let name = default_name(); + Self { + application: ApplicationConfig { + name: name.clone(), + default_platform: default_platform(), + out_dir: out_dir_default(), + asset_dir: asset_dir_default(), + + sub_package: None, + }, + web: WebConfig { + app: WebAppConfig { + title: default_title(), + base_path: None, + }, + proxy: vec![], + watcher: Default::default(), + resource: WebResourceConfig { + dev: WebDevResourceConfig { + style: vec![], + script: vec![], + }, + style: Some(vec![]), + script: Some(vec![]), + }, + https: WebHttpsConfig { + enabled: None, + mkcert: None, + key_path: None, + cert_path: None, + }, + pre_compress: true, + wasm_opt: Default::default(), + }, + desktop: DesktopConfig::default(), + bundle: BundleConfig { + identifier: Some(format!("io.github.{name}")), + publisher: Some(name), + ..Default::default() + }, + } + } +} diff --git a/packages/cli/src/config/platform.rs b/packages/cli/src/config/platform.rs new file mode 100644 index 0000000000..2b04be2b82 --- /dev/null +++ b/packages/cli/src/config/platform.rs @@ -0,0 +1,99 @@ +use serde::{Deserialize, Serialize}; +use std::fmt::Display; +use std::path::PathBuf; +use std::str::FromStr; + +#[derive( + Copy, + Clone, + Hash, + PartialEq, + Eq, + PartialOrd, + Ord, + Serialize, + Deserialize, + Debug, + Default, + clap::ValueEnum, +)] +#[non_exhaustive] +pub enum Platform { + /// Targeting the web platform using WASM + #[clap(name = "web")] + #[serde(rename = "web")] + #[default] + Web, + + /// Targeting the desktop platform using Tao/Wry-based webview + #[clap(name = "desktop")] + #[serde(rename = "desktop")] + Desktop, + + /// Targeting the server platform using Axum and Dioxus-Fullstack + #[clap(name = "fullstack")] + #[serde(rename = "fullstack")] + Fullstack, + + /// Targeting the static generation platform using SSR and Dioxus-Fullstack + #[clap(name = "static-generation")] + #[serde(rename = "static-generation")] + StaticGeneration, + + /// Targeting the static generation platform using SSR and Dioxus-Fullstack + #[clap(name = "liveview")] + #[serde(rename = "liveview")] + Liveview, +} + +/// An error that occurs when a platform is not recognized +pub struct UnknownPlatformError; + +impl std::fmt::Display for UnknownPlatformError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Unknown platform") + } +} + +impl FromStr for Platform { + type Err = UnknownPlatformError; + + fn from_str(s: &str) -> Result { + match s { + "web" => Ok(Self::Web), + "desktop" => Ok(Self::Desktop), + "fullstack" => Ok(Self::Fullstack), + "static-generation" => Ok(Self::StaticGeneration), + "liveview" => Ok(Self::Liveview), + _ => Err(UnknownPlatformError), + } + } +} + +impl Display for Platform { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let feature = self.feature_name(); + f.write_str(feature) + } +} + +impl Platform { + /// All platforms the dioxus CLI supports + pub const ALL: &'static [Self] = &[ + Platform::Web, + Platform::Desktop, + Platform::Fullstack, + Platform::StaticGeneration, + ]; + + /// Get the feature name for the platform in the dioxus crate + pub fn feature_name(&self) -> &str { + match self { + Platform::Web => "web", + Platform::Desktop => "desktop", + Platform::Fullstack => "fullstack", + Platform::StaticGeneration => "static-generation", + Platform::Liveview => "liveview", + } + } +} diff --git a/packages/cli-config/src/serve.rs b/packages/cli/src/config/serve.rs similarity index 74% rename from packages/cli-config/src/serve.rs rename to packages/cli/src/config/serve.rs index 6a29d45c56..57671b15f8 100644 --- a/packages/cli-config/src/serve.rs +++ b/packages/cli/src/config/serve.rs @@ -1,42 +1,7 @@ #![allow(unused)] // lots of configs... -use std::net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4}; - -#[cfg(feature = "read-from-args")] use clap::Parser; - -/// The arguments for the address the server will run on - -#[cfg(feature = "read-from-args")] -#[derive(Clone, Debug, Parser)] -pub struct AddressArguments { - /// The port the server will run on - #[clap(long)] - #[clap(default_value_t = default_port())] - pub port: u16, - - /// The address the server will run on - #[clap(long, default_value_t = default_address())] - pub addr: std::net::IpAddr, -} - -#[cfg(feature = "read-from-args")] -impl Default for AddressArguments { - fn default() -> Self { - Self { - port: default_port(), - addr: default_address(), - } - } -} - -#[cfg(feature = "read-from-args")] -impl AddressArguments { - /// Get the address the server should run on - pub fn address(&self) -> SocketAddr { - SocketAddr::new(self.addr, self.port) - } -} +use std::net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4}; #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] pub struct RuntimeCLIArguments { @@ -73,27 +38,55 @@ impl RuntimeCLIArguments { pub fn cli_address(&self) -> SocketAddr { self.cli_address } +} + +// /// Get the address the proxied fullstack server should run on +// #[cfg(feature = "read-from-args")] +// pub fn fullstack_address(&self) -> AddressArguments { +// let socket = self.server_socket.unwrap_or_else(|| { +// SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, default_port())) +// }); - /// Get the address the proxied fullstack server should run on - #[cfg(feature = "read-from-args")] - pub fn fullstack_address(&self) -> AddressArguments { - let socket = self.server_socket.unwrap_or_else(|| { - SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, default_port())) - }); +// AddressArguments { +// port: socket.port(), +// addr: socket.ip(), +// } +// } - AddressArguments { - port: socket.port(), - addr: socket.ip(), +// /// The arguments for the address the server will run on + +#[derive(Clone, Debug, Parser)] +pub struct AddressArguments { + /// The port the server will run on + #[clap(long)] + #[clap(default_value_t = default_port())] + pub port: u16, + + /// The address the server will run on + #[clap(long, default_value_t = default_address())] + pub addr: std::net::IpAddr, +} + +impl Default for AddressArguments { + fn default() -> Self { + Self { + port: default_port(), + addr: default_address(), } } } -#[cfg(feature = "read-from-args")] +impl AddressArguments { + /// Get the address the server should run on + pub fn address(&self) -> SocketAddr { + SocketAddr::new(self.addr, self.port) + } +} + fn default_port() -> u16 { 8080 } -#[cfg(feature = "read-from-args")] fn default_address() -> IpAddr { IpAddr::V4(std::net::Ipv4Addr::new(127, 0, 0, 1)) } diff --git a/packages/cli/src/config/web.rs b/packages/cli/src/config/web.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/cli/src/dioxus_crate.rs b/packages/cli/src/dioxus_crate.rs index 8e63efe6fe..3fe424fb73 100644 --- a/packages/cli/src/dioxus_crate.rs +++ b/packages/cli/src/dioxus_crate.rs @@ -1,5 +1,7 @@ -use crate::build::TargetArgs; -use dioxus_cli_config::{DioxusConfig, Platform}; +use crate::{ + build::TargetArgs, + config::{DioxusConfig, Platform}, +}; use krates::cm::Target; use krates::{cm::TargetKind, Cmd, Krates, NodeId}; use serde::{Deserialize, Serialize}; diff --git a/packages/cli/src/main.rs b/packages/cli/src/main.rs index 9954364899..1befa2d32a 100644 --- a/packages/cli/src/main.rs +++ b/packages/cli/src/main.rs @@ -3,6 +3,8 @@ #![doc(html_favicon_url = "https://avatars.githubusercontent.com/u/79236386")] pub mod assets; +pub mod bundle_utils; +pub mod config; pub mod dx_build_info; pub mod serve; pub mod tools; diff --git a/packages/cli/src/serve/output.rs b/packages/cli/src/serve/output.rs index d28a0fbf06..1d0e6d1408 100644 --- a/packages/cli/src/serve/output.rs +++ b/packages/cli/src/serve/output.rs @@ -1,3 +1,4 @@ +use crate::config::{AddressArguments, Platform}; use crate::{ builder::{ BuildMessage, MessageSource, MessageType, Stage, TargetPlatform, UpdateBuildProgress, @@ -17,7 +18,6 @@ use crossterm::{ tty::IsTty, ExecutableCommand, }; -use dioxus_cli_config::{AddressArguments, Platform}; use dioxus_hot_reload::ClientMsg; use futures_util::{future::select_all, Future, FutureExt, StreamExt}; use ratatui::{prelude::*, widgets::*, TerminalOptions, Viewport}; @@ -633,7 +633,7 @@ impl Output { // frame.render_widget(Block::default().borders(Borders::BOTTOM), body[0]); // Render the metadata - let mut spans = vec![ + let mut spans: Vec = vec![ Span::from(if self.is_cli_release { "dx" } else { "dx-dev" }).green(), Span::from(" ").green(), Span::from("serve").green(), diff --git a/packages/cli/src/serve/proxy.rs b/packages/cli/src/serve/proxy.rs index b5a04717fb..279de3b70f 100644 --- a/packages/cli/src/serve/proxy.rs +++ b/packages/cli/src/serve/proxy.rs @@ -1,5 +1,5 @@ +use crate::config::WebProxyConfig; use crate::{Error, Result}; -use dioxus_cli_config::WebProxyConfig; use anyhow::{anyhow, Context}; use axum::body::Body as MyBody; diff --git a/packages/cli/src/serve/server.rs b/packages/cli/src/serve/server.rs index 8a5de277cb..bac6a7535e 100644 --- a/packages/cli/src/serve/server.rs +++ b/packages/cli/src/serve/server.rs @@ -1,3 +1,4 @@ +use crate::config::{Platform, WebHttpsConfig}; use crate::dioxus_crate::DioxusCrate; use crate::serve::{next_or_pending, Serve}; use crate::{Error, Result}; @@ -18,7 +19,6 @@ use axum::{ Router, }; use axum_server::tls_rustls::RustlsConfig; -use dioxus_cli_config::{Platform, WebHttpsConfig}; use dioxus_hot_reload::{DevserverMsg, HotReloadMsg}; use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender}; use futures_util::stream; diff --git a/packages/core-macro/src/props/mod.rs b/packages/core-macro/src/props/mod.rs index 865f86b7ce..14a0ddcc15 100644 --- a/packages/core-macro/src/props/mod.rs +++ b/packages/core-macro/src/props/mod.rs @@ -1015,11 +1015,11 @@ Finally, call `.build()` to create the instance of `{name}`. Ok(quote! { #[allow(dead_code, non_camel_case_types, missing_docs)] impl #impl_generics dioxus_core::prelude::HasAttributes for #builder_name < #( #ty_generics ),* > #where_clause { - fn push_attribute( + fn push_attribute( mut self, name: &'static str, ns: Option<&'static str>, - attr: impl dioxus_core::prelude::IntoAttributeValue, + attr: impl dioxus_core::prelude::IntoAttributeValue, volatile: bool ) -> Self { let ( #(#descructuring,)* ) = self.fields; diff --git a/packages/core-types/src/formatter.rs b/packages/core-types/src/formatter.rs new file mode 100644 index 0000000000..a4df243729 --- /dev/null +++ b/packages/core-types/src/formatter.rs @@ -0,0 +1,14 @@ +use std::borrow::Cow; + +/// Take this type and format it into a Cow<'static, str> +/// +/// This trait exists so libraries like manganis can implement this type for asssets without depending +/// on dioxus-core, which can be heavy in proc macros and build scripts. +/// +/// We don't want a blanket impl for T: Display because that might conflict for the other integral data +/// types of AttributeValue +/// +/// Todo: we might be able to specialize without this just with Display. +pub trait DioxusFormattable { + fn format(&self) -> Cow<'static, str>; +} diff --git a/packages/core-types/src/lib.rs b/packages/core-types/src/lib.rs index 5ecf2a4cb1..432d49e695 100644 --- a/packages/core-types/src/lib.rs +++ b/packages/core-types/src/lib.rs @@ -1,11 +1,13 @@ pub mod bubbles; mod core; mod event; +pub mod formatter; mod hotreload; pub mod hr_context; pub use bubbles::*; pub use core::*; pub use event::*; +pub use formatter::*; pub use hotreload::*; pub use hr_context::*; diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index bd82081ce7..bfafec4773 100644 --- a/packages/core/src/nodes.rs +++ b/packages/core/src/nodes.rs @@ -1,3 +1,5 @@ +use dioxus_core_types::DioxusFormattable; + use crate::innerlude::VProps; use crate::prelude::RenderError; use crate::{any_props::BoxedAnyProps, innerlude::ScopeState}; @@ -722,9 +724,9 @@ impl Attribute { /// /// "Volatile" refers to whether or not Dioxus should always override the value. This helps prevent the UI in /// some renderers stay in sync with the VirtualDom's understanding of the world - pub fn new( + pub fn new( name: &'static str, - value: impl IntoAttributeValue, + value: impl IntoAttributeValue, namespace: Option<&'static str>, volatile: bool, ) -> Attribute { @@ -1030,7 +1032,7 @@ impl IntoVNode for &Option { } /// A value that can be converted into an attribute value -pub trait IntoAttributeValue { +pub trait IntoAttributeValue { /// Convert into an attribute value fn into_value(self) -> AttributeValue; } @@ -1108,28 +1110,21 @@ impl IntoAttributeValue for Option { } } -// #[cfg(feature = "manganis")] -// impl IntoAttributeValue for manganis::ImageAsset { -// fn into_value(self) -> AttributeValue { -// AttributeValue::Text(self.to_string()) -// } -// } - -// #[cfg(feature = "manganis")] -// impl IntoAttributeValue for manganis::Asset { -// fn into_value(self) -> AttributeValue { -// AttributeValue::Text(self.to_string()) -// } -// } +pub struct DioxusFormattableMarker; +impl IntoAttributeValue for T { + fn into_value(self) -> AttributeValue { + AttributeValue::Text(self.format().into_owned()) + } +} /// A trait for anything that has a dynamic list of attributes pub trait HasAttributes { /// Push an attribute onto the list of attributes - fn push_attribute( + fn push_attribute( self, name: &'static str, ns: Option<&'static str>, - attr: impl IntoAttributeValue, + attr: impl IntoAttributeValue, volatile: bool, ) -> Self; } diff --git a/packages/core/tests/suspense.rs b/packages/core/tests/suspense.rs index f2775ab70d..cfabe841fe 100644 --- a/packages/core/tests/suspense.rs +++ b/packages/core/tests/suspense.rs @@ -517,7 +517,7 @@ fn nested_suspense_resolves_client() { let title = use_resource(move || async_content(0)).suspend()?(); rsx! { - Title { "{title.title}" } + document::Title { "{title.title}" } } } diff --git a/packages/desktop/headless_tests/eval.rs b/packages/desktop/headless_tests/eval.rs index 4e9c9df795..7fc230750c 100644 --- a/packages/desktop/headless_tests/eval.rs +++ b/packages/desktop/headless_tests/eval.rs @@ -16,23 +16,24 @@ static EVALS_RETURNED: GlobalSignal = Signal::global(|| 0); fn app() -> Element { // Double 100 values in the value use_future(|| async { - let mut eval = eval( + let mut eval = document::eval( r#"for (let i = 0; i < 100; i++) { let value = await dioxus.recv(); dioxus.send(value*2); }"#, ); - for i in 0..100 { - eval.send(serde_json::Value::from(i)).unwrap(); - let value = eval.recv().await.unwrap(); - assert_eq!(value, serde_json::Value::from(i * 2)); - EVALS_RECEIVED.with_mut(|x| *x += 1); - } + todo!("Fix eval tests") + // for i in 0..100 { + // eval.send(serde_json::Value::from(i)).unwrap(); + // let value = eval.recv().await.unwrap(); + // assert_eq!(value, serde_json::Value::from(i * 2)); + // EVALS_RECEIVED.with_mut(|x| *x += 1); + // } }); // Make sure returning no value resolves the future use_future(|| async { - let eval = eval(r#"return;"#); + let eval = document::eval(r#"return;"#); eval.await.unwrap(); EVALS_RETURNED.with_mut(|x| *x += 1); @@ -40,7 +41,7 @@ fn app() -> Element { // Return a value from the future use_future(|| async { - let eval = eval( + let eval = document::eval( r#" return [1, 2, 3]; "#, diff --git a/packages/desktop/headless_tests/rendering.rs b/packages/desktop/headless_tests/rendering.rs index ace615a020..cb67e7bec6 100644 --- a/packages/desktop/headless_tests/rendering.rs +++ b/packages/desktop/headless_tests/rendering.rs @@ -16,7 +16,7 @@ fn use_inner_html(id: &'static str) -> Option { spawn(async move { tokio::time::sleep(std::time::Duration::from_millis(500)).await; - let res = eval(&format!( + let res = document::eval(&format!( r#"let element = document.getElementById('{}'); return element.innerHTML"#, id diff --git a/packages/desktop/headless_tests/utils.rs b/packages/desktop/headless_tests/utils.rs index 1f15cdd536..11b28f2068 100644 --- a/packages/desktop/headless_tests/utils.rs +++ b/packages/desktop/headless_tests/utils.rs @@ -50,7 +50,7 @@ pub fn mock_event_with_extra(id: &'static str, value: &'static str, extra: &'sta "# ); - eval(&js).await.unwrap(); + document::eval(&js).await.unwrap(); }); }) } diff --git a/packages/desktop/src/webview.rs b/packages/desktop/src/webview.rs index 4fc4e29fa6..db25eadfb2 100644 --- a/packages/desktop/src/webview.rs +++ b/packages/desktop/src/webview.rs @@ -147,9 +147,15 @@ impl WebviewInstance { ) -> WebviewInstance { let mut window = cfg.window.clone(); - // tao makes small windows for some reason, make them bigger - if cfg.window.window.inner_size.is_none() { - window = window.with_inner_size(tao::dpi::LogicalSize::new(800.0, 600.0)); + // tao makes small windows for some reason, make them bigger on desktop + // + // on mobile, we want them to be `None` so tao makes them the size of the screen. Otherwise we + // get a window that is not the size of the screen and weird black bars. + #[cfg(not(any(target_os = "ios", target_os = "android")))] + { + if cfg.window.window.inner_size.is_none() { + window = window.with_inner_size(tao::dpi::LogicalSize::new(800.0, 600.0)); + } } // We assume that if the icon is None in cfg, then the user just didnt set it diff --git a/packages/dioxus/Cargo.toml b/packages/dioxus/Cargo.toml index b82eedb72a..e461f25172 100644 --- a/packages/dioxus/Cargo.toml +++ b/packages/dioxus/Cargo.toml @@ -12,6 +12,8 @@ rust-version = "1.79.0" [dependencies] dioxus-core = { workspace = true } +dioxus-core-types = { workspace = true } +dioxus-document = { workspace = true } dioxus-html = { workspace = true, default-features = false, optional = true } dioxus-core-macro = { workspace = true, optional = true } dioxus-config-macro = { workspace = true, optional = true } diff --git a/packages/dioxus/src/lib.rs b/packages/dioxus/src/lib.rs index 35eb4dbf6f..0532530704 100644 --- a/packages/dioxus/src/lib.rs +++ b/packages/dioxus/src/lib.rs @@ -126,6 +126,10 @@ pub mod prelude { #[cfg(feature = "asset")] #[cfg_attr(docsrs, doc(cfg(feature = "asset")))] pub use manganis::{self, self as assets, asset, classes, Asset, ImageAsset, ImageType}; + + #[cfg(feature = "document")] + #[cfg_attr(docsrs, doc(cfg(feature = "document")))] + pub use dioxus_document as document; } #[cfg(feature = "web")] diff --git a/packages/document/Cargo.toml b/packages/document/Cargo.toml index 530c95efef..8c424bcce6 100644 --- a/packages/document/Cargo.toml +++ b/packages/document/Cargo.toml @@ -8,3 +8,5 @@ dioxus-core = { workspace = true } dioxus-core-types = { workspace = true } dioxus-core-macro = { workspace = true } tracing = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } diff --git a/packages/document/src/eval.rs b/packages/document/src/eval.rs index 75434c8c05..e238cd87c0 100644 --- a/packages/document/src/eval.rs +++ b/packages/document/src/eval.rs @@ -81,14 +81,15 @@ pub struct UseEval { // } // } -// impl IntoFuture for UseEval { -// type Output = Result; -// type IntoFuture = Pin>>; +impl IntoFuture for UseEval { + type Output = Result; + type IntoFuture = Pin>>; -// fn into_future(self) -> Self::IntoFuture { -// Box::pin(self.join()) -// } -// } + fn into_future(self) -> Self::IntoFuture { + todo!() + // Box::pin(self.join()) + } +} /// Represents an error when evaluating JavaScript #[derive(Debug)] diff --git a/packages/document/src/head.rs b/packages/document/src/head.rs index eb15655ea4..7a599371ee 100644 --- a/packages/document/src/head.rs +++ b/packages/document/src/head.rs @@ -434,7 +434,7 @@ impl LinkProps { /// rsx! { /// // You can use the meta component to render a meta tag into the head of the page /// // This meta tag will redirect the user to the dioxuslabs homepage in 10 seconds -/// head::Link { +/// document::Link { /// href: asset!("/assets/style.css"), /// rel: "stylesheet", /// } diff --git a/packages/fullstack/Cargo.toml b/packages/fullstack/Cargo.toml index f4629f3a62..650e14d77e 100644 --- a/packages/fullstack/Cargo.toml +++ b/packages/fullstack/Cargo.toml @@ -62,7 +62,7 @@ tower-layer = { version = "0.3.2", optional = true } parking_lot = { version = "0.12.1", features = ["send_guard"], optional = true } web-sys = { version = "0.3.61", optional = true, features = ["Window", "Document", "Element", "HtmlDocument", "Storage", "console"] } -dioxus-cli-config = { workspace = true, features = ["read-config"], optional = true } +# dioxus-cli-config = { workspace = true, features = ["read-config"], optional = true } clap = { version = "4.5.7", optional = true, features = ["derive"] } aws-lc-rs = { version = "1.8.1", optional = true } @@ -105,12 +105,12 @@ server = [ "dep:tracing-futures", "dep:pin-project", "dep:thiserror", - "dep:dioxus-cli-config", + # "dep:dioxus-cli-config", "dep:async-trait", "dep:parking_lot", "dioxus-interpreter-js", "dep:clap", - "dioxus-cli-config/read-from-args", + # "dioxus-cli-config/read-from-args", ] aws-lc-rs = ["dep:aws-lc-rs"] diff --git a/packages/fullstack/examples/hackernews/src/main.rs b/packages/fullstack/examples/hackernews/src/main.rs index 4ea769dd15..828cb119a4 100644 --- a/packages/fullstack/examples/hackernews/src/main.rs +++ b/packages/fullstack/examples/hackernews/src/main.rs @@ -36,7 +36,7 @@ pub fn App() -> Element { #[component] fn Homepage(story: ReadOnlySignal) -> Element { rsx! { - head::Link { rel: "stylesheet", href: asset!("/assets/hackernews.css") } + document::Stylesheet { href: asset!("/assets/hackernews.css") } div { display: "flex", flex_direction: "row", width: "100%", div { width: "50%", diff --git a/packages/fullstack/src/document/server.rs b/packages/fullstack/src/document/server.rs index 265cb2a510..0c560a89a1 100644 --- a/packages/fullstack/src/document/server.rs +++ b/packages/fullstack/src/document/server.rs @@ -115,7 +115,7 @@ impl Document for ServerDocument { }); } - fn create_link(&self, props: head::LinkProps) { + fn create_link(&self, props: document::LinkProps) { self.warn_if_streaming(); self.serialize_for_hydration(); self.0.borrow_mut().link.push(rsx! { diff --git a/packages/hot-reload/Cargo.toml b/packages/hot-reload/Cargo.toml index 14b3310380..168c9f03cc 100644 --- a/packages/hot-reload/Cargo.toml +++ b/packages/hot-reload/Cargo.toml @@ -14,7 +14,7 @@ dioxus-rsx = { workspace = true } dioxus-core = { workspace = true, features = ["serialize"] } dioxus-html = { workspace = true, optional = true } dioxus-signals = { workspace = true, optional = true } -dioxus-cli-config = { workspace = true, optional = true, features = ["read-config"] } +# dioxus-cli-config = { workspace = true, optional = true, features = ["read-config"] } serde = { version = "1", features = ["derive"] } @@ -44,7 +44,7 @@ serde_json = "1.0.91" [features] default = [] client = ["dep:dioxus-signals"] -serve = ["dep:dioxus-cli-config"] +serve = [] # serve = ["dep:tokio-stream", "dep:futures-util", "dep:tokio", "dep:tokio-tungstenite", "dep:dioxus-cli-config"] [package.metadata.docs.rs] diff --git a/packages/hot-reload/src/client.rs b/packages/hot-reload/src/client.rs index ca825e5173..b8d4ccde7c 100644 --- a/packages/hot-reload/src/client.rs +++ b/packages/hot-reload/src/client.rs @@ -6,7 +6,7 @@ use warnings::Warning; /// Applies template and literal changes to the VirtualDom /// /// Assets need to be handled by the renderer. -pub fn apply_changes(dom: &mut VirtualDom, msg: &HotReloadMsg) { +pub fn apply_changes(dom: &VirtualDom, msg: &HotReloadMsg) { dom.runtime().on_scope(ScopeId::ROOT, || { let ctx = dioxus_signals::get_global_context(); diff --git a/packages/liveview/Cargo.toml b/packages/liveview/Cargo.toml index e2e81e1472..2aee072008 100644 --- a/packages/liveview/Cargo.toml +++ b/packages/liveview/Cargo.toml @@ -25,9 +25,11 @@ serde_json = "1.0.91" dioxus-html = { workspace = true, features = ["serialize", "document", "mounted"] } rustc-hash = { workspace = true } dioxus-core = { workspace = true, features = ["serialize"] } +dioxus-document = { workspace = true } +dioxus-core-types = { workspace = true } dioxus-interpreter-js = { workspace = true, features = ["binary-protocol"] } dioxus-hot-reload = { workspace = true, optional = true, features = ["serve", "client"] } -dioxus-cli-config = { workspace = true, features = ["read-config", "read-from-args"] } +# dioxus-cli-config = { workspace = true, features = ["read-config", "read-from-args"] } generational-box = { workspace = true } # axum diff --git a/packages/liveview/src/config.rs b/packages/liveview/src/config.rs index 49194d6924..66e6642002 100644 --- a/packages/liveview/src/config.rs +++ b/packages/liveview/src/config.rs @@ -1,13 +1,13 @@ -use dioxus_cli_config::{RuntimeCLIArguments, CURRENT_CONFIG}; use dioxus_core::VirtualDom; use crate::LiveviewRouter; pub(crate) fn app_title() -> String { - CURRENT_CONFIG - .as_ref() - .map(|c| c.web.app.title.clone()) - .unwrap_or_else(|_| "Dioxus Liveview App".to_string()) + todo!() + // CURRENT_CONFIG + // .as_ref() + // .map(|c| c.web.app.title.clone()) + // .unwrap_or_else(|_| "Dioxus Liveview App".to_string()) } /// A configuration for the LiveView server. @@ -19,12 +19,14 @@ pub struct Config { impl Default for Config { fn default() -> Self { - let address = RuntimeCLIArguments::from_cli() - .map(|args| args.fullstack_address().address()) - .unwrap_or(std::net::SocketAddr::V4(std::net::SocketAddrV4::new( - std::net::Ipv4Addr::new(127, 0, 0, 1), - 8080, - ))); + let address = todo!(); + todo!(); + // let address = RuntimeCLIArguments::from_cli() + // .map(|args| args.fullstack_address().address()) + // .unwrap_or(std::net::SocketAddr::V4(std::net::SocketAddrV4::new( + // std::net::Ipv4Addr::new(127, 0, 0, 1), + // 8080, + // ))); Self { router: R::create_default_liveview_router(), address, diff --git a/packages/liveview/src/eval.rs b/packages/liveview/src/eval.rs index 6821d6502f..11064bb1cb 100644 --- a/packages/liveview/src/eval.rs +++ b/packages/liveview/src/eval.rs @@ -1,5 +1,5 @@ use dioxus_core::ScopeId; -use dioxus_html::document::{Document, EvalError, Evaluator}; +use dioxus_document::{Document, EvalError}; use generational_box::{AnyStorage, GenerationalBox, UnsyncStorage}; use std::rc::Rc; @@ -18,10 +18,6 @@ pub struct LiveviewDocument { } impl Document for LiveviewDocument { - fn new_evaluator(&self, js: String) -> GenerationalBox> { - LiveviewEvaluator::create(self.query.clone(), js) - } - fn as_any(&self) -> &dyn std::any::Any { self } @@ -32,50 +28,50 @@ pub(crate) struct LiveviewEvaluator { query: Query, } -impl LiveviewEvaluator { - /// Creates a new evaluator for liveview-based targets. - pub fn create(query_engine: QueryEngine, js: String) -> GenerationalBox> { - let query = query_engine.new_query(&js); - // We create a generational box that is owned by the query slot so that when we drop the query slot, the generational box is also dropped. - let owner = UnsyncStorage::owner(); - let query_id = query.id; - let query = owner.insert(Box::new(LiveviewEvaluator { query }) as Box); - query_engine.active_requests.slab.borrow_mut()[query_id].owner = Some(owner); +// impl LiveviewEvaluator { +// /// Creates a new evaluator for liveview-based targets. +// pub fn create(query_engine: QueryEngine, js: String) -> GenerationalBox> { +// let query = query_engine.new_query(&js); +// // We create a generational box that is owned by the query slot so that when we drop the query slot, the generational box is also dropped. +// let owner = UnsyncStorage::owner(); +// let query_id = query.id; +// let query = owner.insert(Box::new(LiveviewEvaluator { query }) as Box); +// query_engine.active_requests.slab.borrow_mut()[query_id].owner = Some(owner); - query - } -} +// query +// } +// } -impl Evaluator for LiveviewEvaluator { - /// # Panics - /// This will panic if the query is currently being awaited. - fn poll_join( - &mut self, - context: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - self.query - .poll_result(context) - .map_err(|e| EvalError::Communication(e.to_string())) - } +// impl Evaluator for LiveviewEvaluator { +// /// # Panics +// /// This will panic if the query is currently being awaited. +// fn poll_join( +// &mut self, +// context: &mut std::task::Context<'_>, +// ) -> std::task::Poll> { +// self.query +// .poll_result(context) +// .map_err(|e| EvalError::Communication(e.to_string())) +// } - /// Sends a message to the evaluated JavaScript. - fn send(&self, data: serde_json::Value) -> Result<(), EvalError> { - if let Err(e) = self.query.send(data) { - return Err(EvalError::Communication(e.to_string())); - } - Ok(()) - } +// /// Sends a message to the evaluated JavaScript. +// fn send(&self, data: serde_json::Value) -> Result<(), EvalError> { +// if let Err(e) = self.query.send(data) { +// return Err(EvalError::Communication(e.to_string())); +// } +// Ok(()) +// } - /// Gets an UnboundedReceiver to receive messages from the evaluated JavaScript. - /// - /// # Panics - /// This will panic if the query is currently being awaited. - fn poll_recv( - &mut self, - context: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - self.query - .poll_recv(context) - .map_err(|e| EvalError::Communication(e.to_string())) - } -} +// /// Gets an UnboundedReceiver to receive messages from the evaluated JavaScript. +// /// +// /// # Panics +// /// This will panic if the query is currently being awaited. +// fn poll_recv( +// &mut self, +// context: &mut std::task::Context<'_>, +// ) -> std::task::Poll> { +// self.query +// .poll_recv(context) +// .map_err(|e| EvalError::Communication(e.to_string())) +// } +// } diff --git a/packages/manganis-cli-support/Cargo.toml b/packages/manganis-cli-support/Cargo.toml index 912f8ec5e7..34f6475717 100644 --- a/packages/manganis-cli-support/Cargo.toml +++ b/packages/manganis-cli-support/Cargo.toml @@ -36,9 +36,9 @@ ravif = { version = "0.11", default-features = false } # CSS Minification lightningcss = "1.0.0-alpha.44" -# Js minification -swc = "=0.283.0" -swc_common = "=0.37.1" +# # Js minification +# swc = "=0.283.0" +# swc_common = "=0.37.1" # Remote assets url = { version = "2.4.0", features = ["serde"] } diff --git a/packages/manganis-cli-support/src/file.rs b/packages/manganis-cli-support/src/file.rs index d097dfaad2..1f21eaca37 100644 --- a/packages/manganis-cli-support/src/file.rs +++ b/packages/manganis-cli-support/src/file.rs @@ -9,9 +9,9 @@ use std::{ path::Path, sync::Arc, }; -use swc::{config::JsMinifyOptions, try_with_handler, BoolOrDataConfig}; -use swc_common::{sync::Lrc, FileName}; -use swc_common::{SourceMap, GLOBALS}; +// use swc::{config::JsMinifyOptions, try_with_handler, BoolOrDataConfig}; +// use swc_common::{sync::Lrc, FileName}; +// use swc_common::{SourceMap, GLOBALS}; pub trait Process { fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()>; @@ -189,43 +189,44 @@ pub(crate) fn minify_css(css: &str) -> String { } pub(crate) fn minify_js(source: &ResourceAsset) -> anyhow::Result { - let cm = Arc::::default(); - - let js = source.read_to_string()?; - let c = swc::Compiler::new(cm.clone()); - let output = GLOBALS - .set(&Default::default(), || { - try_with_handler(cm.clone(), Default::default(), |handler| { - // let filename = Lrc::new(match source { - // manganis_common::ResourceAsset::Local(path) => { - // FileName::Real(path.canonicalized.clone()) - // } - // manganis_common::ResourceAsset::Remote(url) => FileName::Url(url.clone()), - // }); - let filename = todo!(); - let fm = cm.new_source_file(filename, js.to_string()); - - c.minify( - fm, - handler, - &JsMinifyOptions { - compress: BoolOrDataConfig::from_bool(true), - mangle: BoolOrDataConfig::from_bool(true), - ..Default::default() - }, - ) - .context("failed to minify javascript") - }) - }) - .map(|output| output.code); - - match output { - Ok(output) => Ok(output), - Err(err) => { - tracing::error!("Failed to minify javascript: {}", err); - Ok(js) - } - } + todo!("disabled swc due to semver issues") + // let cm = Arc::::default(); + + // let js = source.read_to_string()?; + // let c = swc::Compiler::new(cm.clone()); + // let output = GLOBALS + // .set(&Default::default(), || { + // try_with_handler(cm.clone(), Default::default(), |handler| { + // // let filename = Lrc::new(match source { + // // manganis_common::ResourceAsset::Local(path) => { + // // FileName::Real(path.canonicalized.clone()) + // // } + // // manganis_common::ResourceAsset::Remote(url) => FileName::Url(url.clone()), + // // }); + // let filename = todo!(); + // let fm = cm.new_source_file(filename, js.to_string()); + + // c.minify( + // fm, + // handler, + // &JsMinifyOptions { + // compress: BoolOrDataConfig::from_bool(true), + // mangle: BoolOrDataConfig::from_bool(true), + // ..Default::default() + // }, + // ) + // .context("failed to minify javascript") + // }) + // }) + // .map(|output| output.code); + + // match output { + // Ok(output) => Ok(output), + // Err(err) => { + // tracing::error!("Failed to minify javascript: {}", err); + // Ok(js) + // } + // } } impl Process for JsOptions { diff --git a/packages/manganis/Cargo.toml b/packages/manganis/Cargo.toml index fafdf4c158..39ee52b512 100644 --- a/packages/manganis/Cargo.toml +++ b/packages/manganis/Cargo.toml @@ -19,6 +19,7 @@ once_cell = "1.19.0" # infer = { workspace = true } # manganis-common = { workspace = true } dunce = "1.0.2" +dioxus-core-types = { workspace = true } # [target.'cfg(target_os = "macos")'.dependencies] # core-foundation = "0.9.3" diff --git a/packages/manganis/src/builder.rs b/packages/manganis/src/builder.rs index cff27b4771..f0123c7a5d 100644 --- a/packages/manganis/src/builder.rs +++ b/packages/manganis/src/builder.rs @@ -1,5 +1,7 @@ use std::path::{Path, PathBuf}; +use dioxus_core_types::DioxusFormattable; + /// Asset #[derive(Debug, PartialEq, Clone, Copy, Hash)] pub struct Asset { @@ -276,6 +278,17 @@ pub struct ImageAsset { src: AssetSource, } +impl DioxusFormattable for ImageAsset { + fn format(&self) -> std::borrow::Cow<'static, str> { + std::borrow::Cow::Owned(self.to_string()) + } +} +impl DioxusFormattable for Asset { + fn format(&self) -> std::borrow::Cow<'static, str> { + std::borrow::Cow::Owned(self.to_string()) + } +} + impl std::fmt::Display for ImageAsset { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.src.resolve().display()) diff --git a/packages/playwright-tests/fullstack/src/main.rs b/packages/playwright-tests/fullstack/src/main.rs index 59edd973ec..8cd8960882 100644 --- a/packages/playwright-tests/fullstack/src/main.rs +++ b/packages/playwright-tests/fullstack/src/main.rs @@ -16,8 +16,8 @@ fn app() -> Element { let mut text = use_signal(|| "...".to_string()); rsx! { + document::Title { "hello axum! {count}" } h1 { "hello axum! {count}" } - Title { "hello axum! {count}" } button { class: "increment-button", onclick: move |_| count += 1, "Increment" } button { class: "server-button", diff --git a/packages/playwright-tests/nested-suspense/src/main.rs b/packages/playwright-tests/nested-suspense/src/main.rs index c8e275ff96..c60efbdbd9 100644 --- a/packages/playwright-tests/nested-suspense/src/main.rs +++ b/packages/playwright-tests/nested-suspense/src/main.rs @@ -44,7 +44,7 @@ fn LoadTitle() -> Element { .unwrap(); rsx! { - Title { "{title.title}" } + document::Title { "{title.title}" } } } diff --git a/packages/playwright-tests/web/src/main.rs b/packages/playwright-tests/web/src/main.rs index 126f729e4c..bbcb9bb57d 100644 --- a/packages/playwright-tests/web/src/main.rs +++ b/packages/playwright-tests/web/src/main.rs @@ -8,8 +8,8 @@ fn app() -> Element { rsx! { div { + document::Title { "hello axum! {num}" } "hello axum! {num}" - Title { "hello axum! {num}" } button { class: "increment-button", onclick: move |_| num += 1, "Increment" } } svg { circle { cx: 50, cy: 50, r: 40, stroke: "green", fill: "yellow" } } @@ -24,7 +24,7 @@ fn app() -> Element { button { class: "eval-button", onclick: move |_| async move { - let mut eval = eval( + let mut eval = document::eval( r#" window.document.title = 'Hello from Dioxus Eval!'; // Receive and multiply 10 numbers @@ -36,17 +36,18 @@ fn app() -> Element { "#, ); - // Send 10 numbers - for i in 0..10 { - eval.send(serde_json::Value::from(i)).unwrap(); - let value = eval.recv().await.unwrap(); - assert_eq!(value, serde_json::Value::from(i * 2)); - } + todo!("no more dioxus evaluator thing anymore - just a plain-old evail") + // // Send 10 numbers + // for i in 0..10 { + // eval.send(serde_json::Value::from(i)).unwrap(); + // let value = eval.recv().await.unwrap(); + // assert_eq!(value, serde_json::Value::from(i * 2)); + // } - let result = eval.recv().await; - if let Ok(serde_json::Value::String(string)) = result { - eval_result.set(string); - } + // let result = eval.recv().await; + // if let Ok(serde_json::Value::String(string)) = result { + // eval_result.set(string); + // } }, "Eval" } diff --git a/packages/router/Cargo.toml b/packages/router/Cargo.toml index 89fca32bfe..f1d76345b2 100644 --- a/packages/router/Cargo.toml +++ b/packages/router/Cargo.toml @@ -29,7 +29,7 @@ dioxus-ssr = { workspace = true, optional = true } http = { workspace = true, optional = true } dioxus-fullstack = { workspace = true, optional = true } tokio = { workspace = true, features = ["full"], optional = true } -dioxus-cli-config = { workspace = true, features = ["read-config"] } +# dioxus-cli-config = { workspace = true, features = ["read-config"] } rustversion = "1.0.17" # you need to comment this out when publishing since cargo workspaces is not smart enough to wipe this when dropping diff --git a/packages/router/src/history/web.rs b/packages/router/src/history/web.rs index a60dfe7e64..0554160c6a 100644 --- a/packages/router/src/history/web.rs +++ b/packages/router/src/history/web.rs @@ -15,11 +15,12 @@ use super::{ #[allow(dead_code)] fn base_path() -> Option<&'static str> { - tracing::trace!( - "Using base_path from Dioxus.toml: {:?}", - dioxus_cli_config::base_path() - ); - dioxus_cli_config::base_path() + todo!("set basepath not through compile-time env vars!") + // tracing::trace!( + // "Using base_path from Dioxus.toml: {:?}", + // dioxus_cli_config::base_path() + // ); + // dioxus_cli_config::base_path() } #[allow(clippy::extra_unused_type_parameters)] diff --git a/packages/rsx/tests/hotreload_pattern.rs b/packages/rsx/tests/hotreload_pattern.rs index c4b32a4af0..65dc3dce6a 100644 --- a/packages/rsx/tests/hotreload_pattern.rs +++ b/packages/rsx/tests/hotreload_pattern.rs @@ -10,9 +10,10 @@ use dioxus_core::{ prelude::{Template, TemplateNode}, TemplateAttribute, VNode, }; +use dioxus_core_types::HotReloadingContext; use dioxus_rsx::{ hot_reload::{self, diff_rsx, ChangedRsx, HotReloadResult}, - CallBody, HotReloadingContext, + CallBody, }; use proc_macro2::TokenStream; use quote::{quote, ToTokens}; diff --git a/packages/runtime-config/Cargo.toml b/packages/runtime-config/Cargo.toml new file mode 100644 index 0000000000..f899820e81 --- /dev/null +++ b/packages/runtime-config/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "dioxus-runtime-config" +edition = "2021" +version.workspace = true + +[dependencies] \ No newline at end of file diff --git a/packages/runtime-config/README.md b/packages/runtime-config/README.md new file mode 100644 index 0000000000..dfa2f77135 --- /dev/null +++ b/packages/runtime-config/README.md @@ -0,0 +1,7 @@ +# dioxus-runtime-config + +A crate that provides key/value names and types for configuring Dioxus applications at runtime. + +This crate exists for us to very cleanly define the exact fields we want to pass down to Dioxus applications at runtime but without exposing the entire config object. + +This leads to faster compile times, smaller binaries, and a clearer distinction between the config and the application. diff --git a/packages/runtime-config/src/lib.rs b/packages/runtime-config/src/lib.rs new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/packages/runtime-config/src/lib.rs @@ -0,0 +1 @@ + diff --git a/packages/ssr/Cargo.toml b/packages/ssr/Cargo.toml index b694b0d822..e313fd7285 100644 --- a/packages/ssr/Cargo.toml +++ b/packages/ssr/Cargo.toml @@ -12,7 +12,7 @@ keywords = ["dom", "ui", "gui", "react", "ssr"] dioxus-core = { workspace = true, features = ["serialize"] } dioxus-html = { workspace = true, features = ["document"]} dioxus-core-types = { workspace = true } -dioxus-cli-config = { workspace = true, features = ["read-config"], optional = true } +# dioxus-cli-config = { workspace = true, features = ["read-config"], optional = true } dioxus-interpreter-js = { workspace = true } generational-box = { workspace = true } askama_escape = "0.10.3" @@ -46,7 +46,7 @@ fs_extra = "1.2.0" [features] default = [] -incremental = ["dep:tokio", "dep:chrono", "dep:dioxus-cli-config"] +incremental = ["dep:tokio", "dep:chrono"] [package.metadata.docs.rs] cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"] diff --git a/packages/static-generation/Cargo.toml b/packages/static-generation/Cargo.toml index 0b3f546623..cd3b79ad07 100644 --- a/packages/static-generation/Cargo.toml +++ b/packages/static-generation/Cargo.toml @@ -18,7 +18,7 @@ dioxus-ssr = { workspace = true, features = ["incremental"], optional = true } axum = { workspace = true, features = ["ws", "macros"], optional = true } tower-http = { workspace = true, features = ["fs"], optional = true } dioxus-hot-reload = { workspace = true, features = ["serve"], optional = true } -dioxus-cli-config = { workspace = true, features = ["read-config"], optional = true } +# dioxus-cli-config = { workspace = true, features = ["read-config"], optional = true } dioxus-web = { workspace = true, features = ["hydrate"], optional = true } tokio = { workspace = true, optional = true } http = { workspace = true, optional = true } @@ -31,7 +31,8 @@ criterion = { workspace = true } [features] default = [] -server = ["dioxus-fullstack/server", "dioxus-router/ssr", "dep:dioxus-ssr", "dep:tokio", "dep:http", "dep:axum", "dep:tower-http", "dep:dioxus-hot-reload", "dep:dioxus-cli-config", "dep:tower"] +server = ["dioxus-fullstack/server", "dioxus-router/ssr", "dep:dioxus-ssr", "dep:tokio", "dep:http", "dep:axum", "dep:tower-http", "dep:dioxus-hot-reload", "dep:tower"] +# server = ["dioxus-fullstack/server", "dioxus-router/ssr", "dep:dioxus-ssr", "dep:tokio", "dep:http", "dep:axum", "dep:tower-http", "dep:dioxus-hot-reload", "dep:dioxus-cli-config", "dep:tower"] web = ["dioxus-fullstack/web", "dioxus-router/web", "dep:dioxus-web"] # [[bench]] From deb0df33257a2ab5270ceeb680839f1dbdf34f2b Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 21 Aug 2024 20:58:58 -0700 Subject: [PATCH 025/139] switch link to stylesheet for clarity in examples --- examples/all_events.rs | 2 +- examples/calculator.rs | 2 +- examples/calculator_mutable.rs | 2 +- examples/clock.rs | 2 +- examples/control_focus.rs | 2 +- examples/counters.rs | 2 +- examples/crm.rs | 2 +- examples/dynamic_asset.rs | 2 +- examples/file_upload.rs | 2 +- examples/flat_router.rs | 2 +- examples/global.rs | 2 +- examples/image_generator_openai.rs | 2 +- examples/link.rs | 2 +- examples/read_size.rs | 2 +- examples/reducer.rs | 2 +- examples/resize.rs | 2 +- examples/router.rs | 2 +- examples/todomvc.rs | 2 +- examples/weather_app.rs | 2 +- 19 files changed, 19 insertions(+), 19 deletions(-) diff --git a/examples/all_events.rs b/examples/all_events.rs index 112bc290ab..b04b00aff1 100644 --- a/examples/all_events.rs +++ b/examples/all_events.rs @@ -26,7 +26,7 @@ fn app() -> Element { }; rsx! { - document::Link { rel: "stylesheet", href: STYLE } + document::Stylesheet { href: STYLE } div { id: "container", // focusing is necessary to catch keyboard events div { id: "receiver", tabindex: 0, diff --git a/examples/calculator.rs b/examples/calculator.rs index 8f3c371d64..e26dd47280 100644 --- a/examples/calculator.rs +++ b/examples/calculator.rs @@ -54,7 +54,7 @@ fn app() -> Element { }; rsx! { - document::Link { rel: "stylesheet", href: STYLE } + document::Stylesheet { href: STYLE } div { id: "wrapper", div { class: "app", div { class: "calculator", tabindex: "0", onkeydown: handle_key_down_event, diff --git a/examples/calculator_mutable.rs b/examples/calculator_mutable.rs index 814fd9220e..13a289bfef 100644 --- a/examples/calculator_mutable.rs +++ b/examples/calculator_mutable.rs @@ -29,7 +29,7 @@ fn app() -> Element { let mut state = use_signal(Calculator::new); rsx! { - document::Link { rel: "stylesheet", href: asset!("/examples/assets/calculator.css") } + document::Stylesheet { href: asset!("/examples/assets/calculator.css") } div { id: "wrapper", div { class: "app", div { diff --git a/examples/clock.rs b/examples/clock.rs index 283722a5cc..6005c87100 100644 --- a/examples/clock.rs +++ b/examples/clock.rs @@ -38,7 +38,7 @@ fn app() -> Element { ); rsx! { - document::Link { rel: "stylesheet", href: STYLE } + document::Stylesheet { href: STYLE } div { id: "app", div { id: "title", "Carpe diem 🎉" } div { id: "clock-display", "{time}" } diff --git a/examples/control_focus.rs b/examples/control_focus.rs index 7e8c7d914a..f0aa57672b 100644 --- a/examples/control_focus.rs +++ b/examples/control_focus.rs @@ -38,7 +38,7 @@ fn app() -> Element { }); rsx! { - document::Link { rel: "stylesheet", href: STYLE } + document::Stylesheet { href: STYLE } h1 { "Input Roulette" } button { onclick: move |_| running.toggle(), "Toggle roulette" } div { id: "roulette-grid", diff --git a/examples/counters.rs b/examples/counters.rs index 626a8cad29..1ed6b60693 100644 --- a/examples/counters.rs +++ b/examples/counters.rs @@ -16,7 +16,7 @@ fn app() -> Element { let sum = use_memo(move || counters.read().iter().copied().sum::()); rsx! { - document::Link { rel: "stylesheet", href: STYLE } + document::Stylesheet { href: STYLE } div { id: "controls", button { onclick: move |_| counters.write().push(0), "Add counter" } diff --git a/examples/crm.rs b/examples/crm.rs index 6e5dd1e17a..4299a962d0 100644 --- a/examples/crm.rs +++ b/examples/crm.rs @@ -26,7 +26,7 @@ fn main() { integrity: "sha384-Uu6IeWbM+gzNVXJcM9XV3SohHtmWE+3VGi496jvgX1jyvDTXfdK+rfZc8C1Aehk5", crossorigin: "anonymous" } - document::Link { rel: "stylesheet", href: asset!("/examples/assets/crm.css") } + document::Stylesheet { href: asset!("/examples/assets/crm.css") } h1 { "Dioxus CRM Example" } Router:: {} } diff --git a/examples/dynamic_asset.rs b/examples/dynamic_asset.rs index b08732b3e5..f97456ec24 100644 --- a/examples/dynamic_asset.rs +++ b/examples/dynamic_asset.rs @@ -25,7 +25,7 @@ fn app() -> Element { }); rsx! { - document::Link { rel: "stylesheet", href: STYLE } + document::Stylesheet { href: STYLE } h1 { "Dynamic Assets" } img { src: "custom://logos/logo.png" } } diff --git a/examples/file_upload.rs b/examples/file_upload.rs index f8fd172910..ca8ea02dd0 100644 --- a/examples/file_upload.rs +++ b/examples/file_upload.rs @@ -43,7 +43,7 @@ fn app() -> Element { }; rsx! { - document::Link { rel: "stylesheet", href: STYLE } + document::Stylesheet { href: STYLE } h1 { "File Upload Example" } p { "Drop a .txt, .rs, or .js file here to read it" } diff --git a/examples/flat_router.rs b/examples/flat_router.rs index 33ca137747..87a120f28c 100644 --- a/examples/flat_router.rs +++ b/examples/flat_router.rs @@ -14,7 +14,7 @@ const STYLE: Asset = asset!("/examples/assets/flat_router.css"); fn main() { launch(|| { rsx! { - document::Link { rel: "stylesheet", href: STYLE } + document::Stylesheet { href: STYLE } Router:: {} } }) diff --git a/examples/global.rs b/examples/global.rs index 64cdb8662a..5b5829ea32 100644 --- a/examples/global.rs +++ b/examples/global.rs @@ -18,7 +18,7 @@ fn main() { fn app() -> Element { rsx! { - document::Link { rel: "stylesheet", href: STYLE } + document::Stylesheet { href: STYLE } Increment {} Decrement {} Reset {} diff --git a/examples/image_generator_openai.rs b/examples/image_generator_openai.rs index 71c9eb598c..c8cecf6fd4 100644 --- a/examples/image_generator_openai.rs +++ b/examples/image_generator_openai.rs @@ -36,7 +36,7 @@ fn app() -> Element { }); rsx! { - document::Link { rel: "stylesheet", href: "https://unpkg.com/bulma@0.9.0/css/bulma.min.css" } + document::Stylesheet { href: "https://unpkg.com/bulma@0.9.0/css/bulma.min.css" } div { class: "container", div { class: "columns", div { class: "column", diff --git a/examples/link.rs b/examples/link.rs index 59de6013c4..bd5274c163 100644 --- a/examples/link.rs +++ b/examples/link.rs @@ -16,7 +16,7 @@ fn main() { fn app() -> Element { rsx! ( - document::Link { rel: "stylesheet", href: STYLE } + document::Stylesheet { href: STYLE } Router:: {} ) } diff --git a/examples/read_size.rs b/examples/read_size.rs index 98c4951590..350b397a19 100644 --- a/examples/read_size.rs +++ b/examples/read_size.rs @@ -28,7 +28,7 @@ fn app() -> Element { }; rsx!( - document::Link { rel: "stylesheet", href: asset!("/examples/assets/read_size.css") } + document::Stylesheet { href: asset!("/examples/assets/read_size.css") } div { width: "50%", height: "50%", diff --git a/examples/reducer.rs b/examples/reducer.rs index 48e664220f..954448944c 100644 --- a/examples/reducer.rs +++ b/examples/reducer.rs @@ -17,7 +17,7 @@ fn app() -> Element { let mut state = use_signal(|| PlayerState { is_playing: false }); rsx!( - document::Link { rel: "stylesheet", href: STYLE } + document::Stylesheet { href: STYLE } h1 {"Select an option"} // Add some cute animations if the radio is playing! diff --git a/examples/resize.rs b/examples/resize.rs index 2a88529134..73dfd1912d 100644 --- a/examples/resize.rs +++ b/examples/resize.rs @@ -15,7 +15,7 @@ fn app() -> Element { let mut dimensions = use_signal(Size2D::zero); rsx!( - document::Link { rel: "stylesheet", href: asset!("/examples/assets/read_size.css") } + document::Stylesheet { href: asset!("/examples/assets/read_size.css") } div { width: "50%", height: "50%", diff --git a/examples/router.rs b/examples/router.rs index 06679679ee..591286d541 100644 --- a/examples/router.rs +++ b/examples/router.rs @@ -13,7 +13,7 @@ const STYLE: Asset = asset!("/examples/assets/router.css"); fn main() { launch(|| { rsx! { - document::Link { rel: "stylesheet", href: STYLE } + document::Stylesheet { href: STYLE } Router:: {} } }); diff --git a/examples/todomvc.rs b/examples/todomvc.rs index 6a88e93892..66db053466 100644 --- a/examples/todomvc.rs +++ b/examples/todomvc.rs @@ -65,7 +65,7 @@ fn app() -> Element { }; rsx! { - document::Link { rel: "stylesheet", href: STYLE } + document::Stylesheet { href: STYLE } section { class: "todoapp", TodoHeader { todos } section { class: "main", diff --git a/examples/weather_app.rs b/examples/weather_app.rs index 5bc258ff77..328f719568 100644 --- a/examples/weather_app.rs +++ b/examples/weather_app.rs @@ -19,7 +19,7 @@ fn app() -> Element { let current_weather = use_resource(move || async move { get_weather(&country()).await }); rsx! { - document::Link { rel: "stylesheet", href: "https://unpkg.com/tailwindcss@^2.0/dist/tailwind.min.css" } + document::Stylesheet { href: "https://unpkg.com/tailwindcss@^2.0/dist/tailwind.min.css" } div { class: "mx-auto p-4 bg-gray-100 h-screen flex justify-center", div { class: "flex items-center justify-center flex-row", div { class: "flex items-start justify-center flex-row", From 6829d9076b72db9cc4fbbcdbf2c21173c36b7a88 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 21 Aug 2024 21:09:02 -0700 Subject: [PATCH 026/139] untangle another serialize struct --- packages/core/Cargo.toml | 1 - packages/desktop/Cargo.toml | 6 --- packages/desktop/build.rs | 3 +- packages/desktop/src/app.rs | 7 +-- packages/desktop/src/webview.rs | 18 +++++++- packages/dioxus/build.rs | 46 ------------------- packages/fullstack/Cargo.toml | 3 -- packages/hot-reload/Cargo.toml | 17 ------- .../interpreter/src/write_native_mutations.rs | 20 -------- 9 files changed, 19 insertions(+), 102 deletions(-) delete mode 100644 packages/dioxus/build.rs diff --git a/packages/core/Cargo.toml b/packages/core/Cargo.toml index 1a2fa16710..04d1648e41 100644 --- a/packages/core/Cargo.toml +++ b/packages/core/Cargo.toml @@ -48,7 +48,6 @@ features = [ [features] serialize = ["dep:serde"] -# manganis = ["dep:manganis"] [package.metadata.docs.rs] cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"] diff --git a/packages/desktop/Cargo.toml b/packages/desktop/Cargo.toml index 77cc7116ec..fe3acefce7 100644 --- a/packages/desktop/Cargo.toml +++ b/packages/desktop/Cargo.toml @@ -62,13 +62,7 @@ global-hotkey = { workspace = true } rfd = { workspace = true, default-features = false, features = ["xdg-portal", "tokio"] } muda = { workspace = true } -# # use rustls on android -# [target.'cfg(target_os = "android")'.dependencies] -# tokio-tungstenite = { workspace = true, optional = true, features = ["rustls"]} -# # use native tls on other platforms -# [target.'cfg(not(target_os = "android"))'.dependencies] -# tokio-tungstenite = { workspace = true, optional = true, features = ["native-tls"]} [target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies] cocoa = { workspace = true } diff --git a/packages/desktop/build.rs b/packages/desktop/build.rs index 0ee33fa4cc..4b4fa4bb45 100644 --- a/packages/desktop/build.rs +++ b/packages/desktop/build.rs @@ -1,6 +1,5 @@ use std::{io::Write as _, path::PathBuf}; - fn main() { check_gnu(); @@ -17,6 +16,7 @@ fn check_gnu() { } } +// todo: maybe we don't want to do this in this build script fn maybe_copy_doc_scrope() { // To prepare for a release, we add extra examples to desktop for doc scraping and copy assets from the workspace to make those examples compile if option_env!("DIOXUS_RELEASE").is_some() { @@ -45,7 +45,6 @@ fn maybe_copy_doc_scrope() { } } - const EXAMPLES_TOML: &str = r#" # Most of the examples live in the workspace. We include some here so that docs.rs can scrape our examples for better inline docs [[example]] diff --git a/packages/desktop/src/app.rs b/packages/desktop/src/app.rs index e050010f0e..d3105cb954 100644 --- a/packages/desktop/src/app.rs +++ b/packages/desktop/src/app.rs @@ -266,12 +266,7 @@ impl App { view.desktop_context.query.send(result); } - #[cfg(all( - feature = "hot-reload", - debug_assertions, - // not(target_os = "android"), - // not(target_os = "ios") - ))] + #[cfg(all(feature = "hot-reload", debug_assertions,))] pub fn handle_hot_reload_msg(&mut self, msg: dioxus_hot_reload::DevserverMsg) { use dioxus_hot_reload::DevserverMsg; diff --git a/packages/desktop/src/webview.rs b/packages/desktop/src/webview.rs index db25eadfb2..3eee89a9bf 100644 --- a/packages/desktop/src/webview.rs +++ b/packages/desktop/src/webview.rs @@ -11,7 +11,6 @@ use dioxus_core::{Runtime, ScopeId, VirtualDom}; use dioxus_document::Document; use dioxus_hooks::to_owned; use dioxus_html::{HasFileData, HtmlEvent, PlatformEventData}; -use dioxus_interpreter_js::SynchronousEventResponse; use futures_util::{pin_mut, FutureExt}; use std::cell::OnceCell; use std::sync::Arc; @@ -151,6 +150,8 @@ impl WebviewInstance { // // on mobile, we want them to be `None` so tao makes them the size of the screen. Otherwise we // get a window that is not the size of the screen and weird black bars. + // + // todo: move this to our launch function that's different for desktop and mobile #[cfg(not(any(target_os = "ios", target_os = "android")))] { if cfg.window.window.inner_size.is_none() { @@ -393,3 +394,18 @@ impl WebviewInstance { .evaluate_script("window.interpreter.kickAllStylesheetsOnPage()"); } } + +/// A synchronous response to a browser event which may prevent the default browser's action +#[derive(serde::Serialize, Default)] +pub struct SynchronousEventResponse { + #[serde(rename = "preventDefault")] + prevent_default: bool, +} + +impl SynchronousEventResponse { + /// Create a new SynchronousEventResponse + #[allow(unused)] + pub fn new(prevent_default: bool) -> Self { + Self { prevent_default } + } +} diff --git a/packages/dioxus/build.rs b/packages/dioxus/build.rs deleted file mode 100644 index 59c348665c..0000000000 --- a/packages/dioxus/build.rs +++ /dev/null @@ -1,46 +0,0 @@ -fn main() { - // Warn the user if they enabled the launch feature without any renderers - // if feature_enabled("launch") { - // if feature_enabled("third-party-renderer") { - // return; - // } - - // let liveview_renderers = ["liveview", "server"]; - // let fullstack_renderers = ["server"]; - // let client_renderers = ["desktop", "mobile", "web"]; - // let client_renderer_selected = client_renderers - // .iter() - // .any(|renderer| feature_enabled(renderer)); - // if feature_enabled("fullstack") { - // let server_fullstack_enabled = fullstack_renderers - // .iter() - // .any(|renderer| feature_enabled(renderer)); - // if !server_fullstack_enabled && !client_renderer_selected { - // println!("cargo:warning=You have enabled the launch and fullstack features, but have not enabled any renderers. The application will not be able to launch. Try enabling one of the following renderers: {} for the server or one of the following renderers: {} for the client.", fullstack_renderers.join(", "), client_renderers.join(", ")); - // } - // } - - // if feature_enabled("liveview") { - // let server_selected = liveview_renderers - // .iter() - // .any(|renderer| feature_enabled(renderer)); - // if !server_selected { - // println!("cargo:warning=You have enabled the launch and liveview features, but have not enabled any liveview renderers. The application will not be able to launch. Try enabling one of the following renderers: {}", liveview_renderers.join(", ")); - // } - // } - - // if !client_renderer_selected { - // println!("cargo:warning=You have enabled the launch feature, but have not enabled any client renderers. The application will not be able to launch. Try enabling one of the following renderers: {}, fullstack or liveview", client_renderers.join(", ")); - // } - // } - - // if feature_enabled("axum") { - // println!("cargo:warning=The axum feature has been renamed to server and will be removed in a future release. Please update your code to use server feature instead."); - // } -} - -// fn feature_enabled(feature: &str) -> bool { -// let feature = "CARGO_FEATURE_".to_owned() + &feature.to_uppercase().replace('-', "_"); -// println!("cargo:rerun-if-env-changed={}", feature); -// std::env::var(feature).is_ok() -// } diff --git a/packages/fullstack/Cargo.toml b/packages/fullstack/Cargo.toml index 650e14d77e..e44eb15c5e 100644 --- a/packages/fullstack/Cargo.toml +++ b/packages/fullstack/Cargo.toml @@ -62,7 +62,6 @@ tower-layer = { version = "0.3.2", optional = true } parking_lot = { version = "0.12.1", features = ["send_guard"], optional = true } web-sys = { version = "0.3.61", optional = true, features = ["Window", "Document", "Element", "HtmlDocument", "Storage", "console"] } -# dioxus-cli-config = { workspace = true, features = ["read-config"], optional = true } clap = { version = "4.5.7", optional = true, features = ["derive"] } aws-lc-rs = { version = "1.8.1", optional = true } @@ -105,12 +104,10 @@ server = [ "dep:tracing-futures", "dep:pin-project", "dep:thiserror", - # "dep:dioxus-cli-config", "dep:async-trait", "dep:parking_lot", "dioxus-interpreter-js", "dep:clap", - # "dioxus-cli-config/read-from-args", ] aws-lc-rs = ["dep:aws-lc-rs"] diff --git a/packages/hot-reload/Cargo.toml b/packages/hot-reload/Cargo.toml index 168c9f03cc..6b805cc581 100644 --- a/packages/hot-reload/Cargo.toml +++ b/packages/hot-reload/Cargo.toml @@ -14,7 +14,6 @@ dioxus-rsx = { workspace = true } dioxus-core = { workspace = true, features = ["serialize"] } dioxus-html = { workspace = true, optional = true } dioxus-signals = { workspace = true, optional = true } -# dioxus-cli-config = { workspace = true, optional = true, features = ["read-config"] } serde = { version = "1", features = ["derive"] } @@ -22,21 +21,6 @@ serde = { version = "1", features = ["derive"] } tracing = { workspace = true } warnings.workspace = true -# tokio-stream = { version = "0.1.12", features = ["sync"], optional = true } -# notify = { workspace = true, optional = true } -# chrono = { version = "0.4.24", default-features = false, features = ["clock"], optional = true } -# execute = { version = "0.2.11", optional = true } -# once_cell = { version = "1.17.0", optional = true } -# ignore = { version = "0.4.19", optional = true } - -# # use rustls on android -# [target.'cfg(target_os = "android")'.dependencies] -# tokio-tungstenite = { workspace = true, optional = true, features = ["rustls"] } - -# # use native tls on other platforms -# [target.'cfg(not(target_os = "android"))'.dependencies] -# tokio-tungstenite = { workspace = true, optional = true, features = ["native-tls"] } - [dev-dependencies] tokio = { workspace = true, features = ["full"] } serde_json = "1.0.91" @@ -45,7 +29,6 @@ serde_json = "1.0.91" default = [] client = ["dep:dioxus-signals"] serve = [] -# serve = ["dep:tokio-stream", "dep:futures-util", "dep:tokio", "dep:tokio-tungstenite", "dep:dioxus-cli-config"] [package.metadata.docs.rs] cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"] diff --git a/packages/interpreter/src/write_native_mutations.rs b/packages/interpreter/src/write_native_mutations.rs index f193cd3bdd..f7b0631a3b 100644 --- a/packages/interpreter/src/write_native_mutations.rs +++ b/packages/interpreter/src/write_native_mutations.rs @@ -192,23 +192,3 @@ impl WriteMutations for MutationState { self.channel.push_root(id.0 as _); } } - -/// A synchronous response to a browser event which may prevent the default browser's action -#[cfg_attr(feature = "serialize", derive(serde::Serialize))] -#[derive(Default)] -pub struct SynchronousEventResponse { - #[cfg(feature = "serialize")] - #[serde(rename = "preventDefault")] - prevent_default: bool, -} - -impl SynchronousEventResponse { - /// Create a new SynchronousEventResponse - #[allow(unused)] - pub fn new(prevent_default: bool) -> Self { - Self { - #[cfg(feature = "serialize")] - prevent_default, - } - } -} From 56b03c351d168437e02cc0a642cc3e25437061c9 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 21 Aug 2024 21:11:22 -0700 Subject: [PATCH 027/139] move manganis workspace example to examples folder --- Cargo.toml | 6 +++--- .../manganis-test-package/Cargo.toml | 0 .../manganis-test-package/assets/asset.txt | 0 .../manganis-test-package/assets/data.json | 0 .../manganis-test-package/assets/script.js | 0 .../manganis-test-package/assets/test.mp4 | 0 .../manganis-test-package/src/main.rs | 0 .../test-package-dependency/Cargo.toml | 0 .../test-package-dependency/src/asset.txt | 0 .../test-package-dependency/src/file.rs | 0 .../test-package-dependency/src/lib.rs | 0 .../test-package-nested-dependency/Cargo.toml | 0 .../all_the_assets/data.json | 0 .../all_the_assets/rustacean-flat-gesture.png | Bin .../all_the_assets/script.js | 0 .../all_the_assets/style.css | 0 .../test-package-nested-dependency/src/file.rs | 0 .../test-package-nested-dependency/src/folder.rs | 0 .../test-package-nested-dependency/src/font.rs | 0 .../test-package-nested-dependency/src/js.rs | 0 .../test-package-nested-dependency/src/lib.rs | 0 packages/liveview/Cargo.toml | 6 +++--- 22 files changed, 6 insertions(+), 6 deletions(-) rename {packages => examples}/manganis-test-package/Cargo.toml (100%) rename {packages => examples}/manganis-test-package/assets/asset.txt (100%) rename {packages => examples}/manganis-test-package/assets/data.json (100%) rename {packages => examples}/manganis-test-package/assets/script.js (100%) rename {packages => examples}/manganis-test-package/assets/test.mp4 (100%) rename {packages => examples}/manganis-test-package/src/main.rs (100%) rename {packages => examples}/manganis-test-package/test-package-dependency/Cargo.toml (100%) rename {packages => examples}/manganis-test-package/test-package-dependency/src/asset.txt (100%) rename {packages => examples}/manganis-test-package/test-package-dependency/src/file.rs (100%) rename {packages => examples}/manganis-test-package/test-package-dependency/src/lib.rs (100%) rename {packages => examples}/manganis-test-package/test-package-nested-dependency/Cargo.toml (100%) rename {packages => examples}/manganis-test-package/test-package-nested-dependency/all_the_assets/data.json (100%) rename {packages => examples}/manganis-test-package/test-package-nested-dependency/all_the_assets/rustacean-flat-gesture.png (100%) rename {packages => examples}/manganis-test-package/test-package-nested-dependency/all_the_assets/script.js (100%) rename {packages => examples}/manganis-test-package/test-package-nested-dependency/all_the_assets/style.css (100%) rename {packages => examples}/manganis-test-package/test-package-nested-dependency/src/file.rs (100%) rename {packages => examples}/manganis-test-package/test-package-nested-dependency/src/folder.rs (100%) rename {packages => examples}/manganis-test-package/test-package-nested-dependency/src/font.rs (100%) rename {packages => examples}/manganis-test-package/test-package-nested-dependency/src/js.rs (100%) rename {packages => examples}/manganis-test-package/test-package-nested-dependency/src/lib.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index 2e8aab2038..edcbbe85a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,9 +61,9 @@ members = [ "packages/manganis-macro", "packages/manganis-common", "packages/manganis-cli-support", - "packages/manganis-test-package", - "packages/manganis-test-package/test-package-dependency", - "packages/manganis-test-package/test-package-nested-dependency", + "examples/manganis-test-package", + "examples/manganis-test-package/test-package-dependency", + "examples/manganis-test-package/test-package-nested-dependency", "packages/document", "packages/runtime-config", ] diff --git a/packages/manganis-test-package/Cargo.toml b/examples/manganis-test-package/Cargo.toml similarity index 100% rename from packages/manganis-test-package/Cargo.toml rename to examples/manganis-test-package/Cargo.toml diff --git a/packages/manganis-test-package/assets/asset.txt b/examples/manganis-test-package/assets/asset.txt similarity index 100% rename from packages/manganis-test-package/assets/asset.txt rename to examples/manganis-test-package/assets/asset.txt diff --git a/packages/manganis-test-package/assets/data.json b/examples/manganis-test-package/assets/data.json similarity index 100% rename from packages/manganis-test-package/assets/data.json rename to examples/manganis-test-package/assets/data.json diff --git a/packages/manganis-test-package/assets/script.js b/examples/manganis-test-package/assets/script.js similarity index 100% rename from packages/manganis-test-package/assets/script.js rename to examples/manganis-test-package/assets/script.js diff --git a/packages/manganis-test-package/assets/test.mp4 b/examples/manganis-test-package/assets/test.mp4 similarity index 100% rename from packages/manganis-test-package/assets/test.mp4 rename to examples/manganis-test-package/assets/test.mp4 diff --git a/packages/manganis-test-package/src/main.rs b/examples/manganis-test-package/src/main.rs similarity index 100% rename from packages/manganis-test-package/src/main.rs rename to examples/manganis-test-package/src/main.rs diff --git a/packages/manganis-test-package/test-package-dependency/Cargo.toml b/examples/manganis-test-package/test-package-dependency/Cargo.toml similarity index 100% rename from packages/manganis-test-package/test-package-dependency/Cargo.toml rename to examples/manganis-test-package/test-package-dependency/Cargo.toml diff --git a/packages/manganis-test-package/test-package-dependency/src/asset.txt b/examples/manganis-test-package/test-package-dependency/src/asset.txt similarity index 100% rename from packages/manganis-test-package/test-package-dependency/src/asset.txt rename to examples/manganis-test-package/test-package-dependency/src/asset.txt diff --git a/packages/manganis-test-package/test-package-dependency/src/file.rs b/examples/manganis-test-package/test-package-dependency/src/file.rs similarity index 100% rename from packages/manganis-test-package/test-package-dependency/src/file.rs rename to examples/manganis-test-package/test-package-dependency/src/file.rs diff --git a/packages/manganis-test-package/test-package-dependency/src/lib.rs b/examples/manganis-test-package/test-package-dependency/src/lib.rs similarity index 100% rename from packages/manganis-test-package/test-package-dependency/src/lib.rs rename to examples/manganis-test-package/test-package-dependency/src/lib.rs diff --git a/packages/manganis-test-package/test-package-nested-dependency/Cargo.toml b/examples/manganis-test-package/test-package-nested-dependency/Cargo.toml similarity index 100% rename from packages/manganis-test-package/test-package-nested-dependency/Cargo.toml rename to examples/manganis-test-package/test-package-nested-dependency/Cargo.toml diff --git a/packages/manganis-test-package/test-package-nested-dependency/all_the_assets/data.json b/examples/manganis-test-package/test-package-nested-dependency/all_the_assets/data.json similarity index 100% rename from packages/manganis-test-package/test-package-nested-dependency/all_the_assets/data.json rename to examples/manganis-test-package/test-package-nested-dependency/all_the_assets/data.json diff --git a/packages/manganis-test-package/test-package-nested-dependency/all_the_assets/rustacean-flat-gesture.png b/examples/manganis-test-package/test-package-nested-dependency/all_the_assets/rustacean-flat-gesture.png similarity index 100% rename from packages/manganis-test-package/test-package-nested-dependency/all_the_assets/rustacean-flat-gesture.png rename to examples/manganis-test-package/test-package-nested-dependency/all_the_assets/rustacean-flat-gesture.png diff --git a/packages/manganis-test-package/test-package-nested-dependency/all_the_assets/script.js b/examples/manganis-test-package/test-package-nested-dependency/all_the_assets/script.js similarity index 100% rename from packages/manganis-test-package/test-package-nested-dependency/all_the_assets/script.js rename to examples/manganis-test-package/test-package-nested-dependency/all_the_assets/script.js diff --git a/packages/manganis-test-package/test-package-nested-dependency/all_the_assets/style.css b/examples/manganis-test-package/test-package-nested-dependency/all_the_assets/style.css similarity index 100% rename from packages/manganis-test-package/test-package-nested-dependency/all_the_assets/style.css rename to examples/manganis-test-package/test-package-nested-dependency/all_the_assets/style.css diff --git a/packages/manganis-test-package/test-package-nested-dependency/src/file.rs b/examples/manganis-test-package/test-package-nested-dependency/src/file.rs similarity index 100% rename from packages/manganis-test-package/test-package-nested-dependency/src/file.rs rename to examples/manganis-test-package/test-package-nested-dependency/src/file.rs diff --git a/packages/manganis-test-package/test-package-nested-dependency/src/folder.rs b/examples/manganis-test-package/test-package-nested-dependency/src/folder.rs similarity index 100% rename from packages/manganis-test-package/test-package-nested-dependency/src/folder.rs rename to examples/manganis-test-package/test-package-nested-dependency/src/folder.rs diff --git a/packages/manganis-test-package/test-package-nested-dependency/src/font.rs b/examples/manganis-test-package/test-package-nested-dependency/src/font.rs similarity index 100% rename from packages/manganis-test-package/test-package-nested-dependency/src/font.rs rename to examples/manganis-test-package/test-package-nested-dependency/src/font.rs diff --git a/packages/manganis-test-package/test-package-nested-dependency/src/js.rs b/examples/manganis-test-package/test-package-nested-dependency/src/js.rs similarity index 100% rename from packages/manganis-test-package/test-package-nested-dependency/src/js.rs rename to examples/manganis-test-package/test-package-nested-dependency/src/js.rs diff --git a/packages/manganis-test-package/test-package-nested-dependency/src/lib.rs b/examples/manganis-test-package/test-package-nested-dependency/src/lib.rs similarity index 100% rename from packages/manganis-test-package/test-package-nested-dependency/src/lib.rs rename to examples/manganis-test-package/test-package-nested-dependency/src/lib.rs diff --git a/packages/liveview/Cargo.toml b/packages/liveview/Cargo.toml index 2aee072008..31aa8b41f8 100644 --- a/packages/liveview/Cargo.toml +++ b/packages/liveview/Cargo.toml @@ -24,12 +24,12 @@ serde = { version = "1.0.151", features = ["derive"] } serde_json = "1.0.91" dioxus-html = { workspace = true, features = ["serialize", "document", "mounted"] } rustc-hash = { workspace = true } + dioxus-core = { workspace = true, features = ["serialize"] } -dioxus-document = { workspace = true } dioxus-core-types = { workspace = true } -dioxus-interpreter-js = { workspace = true, features = ["binary-protocol"] } +dioxus-document = { workspace = true } dioxus-hot-reload = { workspace = true, optional = true, features = ["serve", "client"] } -# dioxus-cli-config = { workspace = true, features = ["read-config", "read-from-args"] } +dioxus-interpreter-js = { workspace = true, features = ["binary-protocol"] } generational-box = { workspace = true } # axum From 37642f78fd300eeecfbdef76c160a317d8fd1cee Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 21 Aug 2024 21:39:24 -0700 Subject: [PATCH 028/139] decompose manganis-cli-support --- Cargo.lock | 591 +----------------- Cargo.toml | 20 +- packages/cli/Cargo.toml | 56 +- packages/cli/src/assets.rs | 14 +- .../src => cli/src/assets}/file.rs | 0 .../src => cli/src/assets}/folder.rs | 0 .../src/assets}/linker_intercept.rs | 0 .../src => cli/src/assets}/manifest.rs | 0 .../src/lib.rs => cli/src/assets/mod.rs} | 2 + packages/cli/src/builder/cargo.rs | 67 +- packages/cli/src/tracer.rs | 2 +- packages/core-macro/src/lib.rs | 20 +- packages/manganis-cli-support/Cargo.toml | 62 -- packages/manganis-cli-support/README.md | 58 -- packages/manganis-cli-support/examples/cli.rs | 74 --- .../tests/collects_assets.rs | 93 --- packages/manganis-common/Cargo.toml | 30 - packages/manganis-macro/Cargo.toml | 8 - packages/manganis/Cargo.toml | 18 +- .../src => manganis/src/common}/asset.rs | 0 .../src/common}/asset/error.rs | 0 .../src => manganis/src/common}/asset/file.rs | 0 .../src/common}/asset/folder.rs | 0 .../src => manganis/src/common}/asset/meta.rs | 0 .../src/common}/asset/resource.rs | 0 .../src/common}/asset/tailwind.rs | 0 .../src => manganis/src/common}/built.rs | 0 .../src => manganis/src/common}/config.rs | 0 .../src => manganis/src/common}/file.rs | 0 .../src => manganis/src/common}/lib.rs | 0 .../src => manganis/src/common}/linker.rs | 0 packages/router/Cargo.toml | 1 - 32 files changed, 136 insertions(+), 980 deletions(-) rename packages/{manganis-cli-support/src => cli/src/assets}/file.rs (100%) rename packages/{manganis-cli-support/src => cli/src/assets}/folder.rs (100%) rename packages/{manganis-cli-support/src => cli/src/assets}/linker_intercept.rs (100%) rename packages/{manganis-cli-support/src => cli/src/assets}/manifest.rs (100%) rename packages/{manganis-cli-support/src/lib.rs => cli/src/assets/mod.rs} (90%) delete mode 100644 packages/manganis-cli-support/Cargo.toml delete mode 100644 packages/manganis-cli-support/README.md delete mode 100644 packages/manganis-cli-support/examples/cli.rs delete mode 100644 packages/manganis-cli-support/tests/collects_assets.rs delete mode 100644 packages/manganis-common/Cargo.toml rename packages/{manganis-common/src => manganis/src/common}/asset.rs (100%) rename packages/{manganis-common/src => manganis/src/common}/asset/error.rs (100%) rename packages/{manganis-common/src => manganis/src/common}/asset/file.rs (100%) rename packages/{manganis-common/src => manganis/src/common}/asset/folder.rs (100%) rename packages/{manganis-common/src => manganis/src/common}/asset/meta.rs (100%) rename packages/{manganis-common/src => manganis/src/common}/asset/resource.rs (100%) rename packages/{manganis-common/src => manganis/src/common}/asset/tailwind.rs (100%) rename packages/{manganis-common/src => manganis/src/common}/built.rs (100%) rename packages/{manganis-common/src => manganis/src/common}/config.rs (100%) rename packages/{manganis-common/src => manganis/src/common}/file.rs (100%) rename packages/{manganis-common/src => manganis/src/common}/lib.rs (100%) rename packages/{manganis-common/src => manganis/src/common}/linker.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 7e689793e1..2437742607 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -95,12 +95,6 @@ dependencies = [ "as-slice", ] -[[package]] -name = "aligned-vec" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" - [[package]] name = "alloc-no-stdlib" version = "2.0.4" @@ -233,29 +227,12 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d67af77d68a931ecd5cbd8a3b5987d63a1d1d1278f7f6a60ae33db485cdebb69" -[[package]] -name = "arbitrary" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" - [[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.74", -] - [[package]] name = "argh" version = "0.1.12" @@ -549,29 +526,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" -[[package]] -name = "av1-grain" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6678909d8c5d46a42abcf571271e15fdbc0a225e3646cf23762cd415046c78bf" -dependencies = [ - "anyhow", - "arrayvec", - "log", - "nom", - "num-rational", - "v_frame", -] - -[[package]] -name = "avif-serialize" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "876c75a42f6364451a033496a14c44bffe41f5f4a8236f697391f11024e596d2" -dependencies = [ - "arrayvec", -] - [[package]] name = "aws-lc-rs" version = "1.8.1" @@ -853,15 +807,6 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" -[[package]] -name = "base64-simd" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "781dd20c3aff0bd194fe7d2a977dd92f21c173891f3a03b677359e5fa457e5d5" -dependencies = [ - "simd-abstraction", -] - [[package]] name = "base64ct" version = "1.6.0" @@ -966,12 +911,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "bitstream-io" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dcde5f311c85b8ca30c2e4198d4326bc342c76541590106f5fa4a50946ea499" - [[package]] name = "bitvec" version = "1.0.1" @@ -1154,12 +1093,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" -[[package]] -name = "byteorder-lite" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" - [[package]] name = "bytes" version = "1.7.1" @@ -1734,26 +1667,6 @@ dependencies = [ "tiny-keccak", ] -[[package]] -name = "const-str" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21077772762a1002bb421c3af42ac1725fa56066bfc53d9a55bb79905df2aaf3" -dependencies = [ - "const-str-proc-macro", -] - -[[package]] -name = "const-str-proc-macro" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e1e0fdd2e5d3041e530e1b21158aeeef8b5d0e306bc5c1e3d6cf0930d10e25a" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "const_format" version = "0.2.32" @@ -2058,28 +1971,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "cssparser" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9be934d936a0fbed5bcdc01042b770de1398bf79d0e192f49fa7faea0e99281e" -dependencies = [ - "cssparser-macros", - "dtoa-short", - "itoa 1.0.11", - "phf 0.11.2", - "smallvec", -] - -[[package]] -name = "cssparser-color" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "556c099a61d85989d7af52b692e35a8d68a57e7df8c6d07563dc0778b3960c9f" -dependencies = [ - "cssparser 0.33.0", -] - [[package]] name = "cssparser-macros" version = "0.6.1" @@ -2288,15 +2179,6 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" -[[package]] -name = "data-url" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a30bfce702bcfa94e906ef82421f2c0e61c076ad76030c16ee5d2e9a32fe193" -dependencies = [ - "matches", -] - [[package]] name = "der" version = "0.7.9" @@ -2507,7 +2389,6 @@ dependencies = [ "ignore", "krates", "log", - "manganis-cli-support", "notify", "once_cell", "open", @@ -5368,58 +5249,6 @@ dependencies = [ "tiff", ] -[[package]] -name = "image" -version = "0.25.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99314c8a2152b8ddb211f924cdae532d8c5e4c8bb54728e12fff1b0cd5963a10" -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.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f79afb8cbee2ef20f59ccd477a218c12a93943d075b492015ecb1bb81f8ee904" -dependencies = [ - "byteorder-lite", - "quick-error", -] - -[[package]] -name = "imagequant" -version = "4.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fecc99538c9061ee4d88476f6cd704c9f06575a34f0083affdaa1337a331aa7" -dependencies = [ - "arrayvec", - "once_cell", - "rayon", - "rgb", - "thread_local", -] - -[[package]] -name = "imgref" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44feda355f4159a7c757171a77de25daf6411e217b4cabd03bd6650690468126" - [[package]] name = "indexmap" version = "1.9.3" @@ -5527,17 +5356,6 @@ dependencies = [ "parking_lot", ] -[[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.74", -] - [[package]] name = "inventory" version = "0.3.15" @@ -5823,7 +5641,7 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f29e4755b7b995046f510a7520c42b2fed58b77bd94d5a87a8eb43d2fd126da8" dependencies = [ - "cssparser 0.27.2", + "cssparser", "html5ever", "indexmap 1.9.3", "matches", @@ -5867,17 +5685,6 @@ version = "0.2.156" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5f43f184355eefb8d17fc948dbecf6c13be3c141f20d834ae842193a448c72a" -[[package]] -name = "libfuzzer-sys" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7" -dependencies = [ - "arbitrary", - "cc", - "once_cell", -] - [[package]] name = "libgit2-sys" version = "0.16.2+1.7.2" @@ -5975,50 +5782,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "lightningcss" -version = "1.0.0-alpha.58" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec380ca49dc7f6a1cafbdd2de5e587958eac0f67ab26b1e56727fcc60a0c3d4d" -dependencies = [ - "ahash 0.8.11", - "bitflags 2.6.0", - "const-str", - "cssparser 0.33.0", - "cssparser-color", - "dashmap", - "data-encoding", - "getrandom 0.2.15", - "itertools 0.10.5", - "lazy_static", - "lightningcss-derive", - "parcel_selectors", - "parcel_sourcemap", - "paste", - "pathdiff", - "rayon", - "serde", - "smallvec", -] - -[[package]] -name = "lightningcss-derive" -version = "1.0.0-alpha.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c12744d1279367caed41739ef094c325d53fb0ffcd4f9b84a368796f870252" -dependencies = [ - "convert_case 0.6.0", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "line-col" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e69cdf6b85b5c8dce514f694089a2cf8b1a702f6cd28607bcb3cf296c9778db" - [[package]] name = "link-cplusplus" version = "1.0.9" @@ -6113,15 +5876,6 @@ 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 = "lru" version = "0.12.4" @@ -6171,43 +5925,12 @@ dependencies = [ name = "manganis" version = "0.6.0-alpha.2" dependencies = [ + "anyhow", + "base64 0.22.1", "dioxus-core-types", "dunce", - "manganis-common", "manganis-macro", "once_cell", -] - -[[package]] -name = "manganis-cli-support" -version = "0.6.0-alpha.2" -dependencies = [ - "anyhow", - "image 0.25.2", - "imagequant", - "lightningcss", - "manganis-common", - "mozjpeg", - "object", - "png", - "railwind", - "ravif", - "rayon", - "reqwest", - "rustc-hash 1.1.0", - "serde", - "serde_json", - "tracing", - "tracing-subscriber", - "url", -] - -[[package]] -name = "manganis-common" -version = "0.6.0-alpha.2" -dependencies = [ - "anyhow", - "base64 0.22.1", "serde", ] @@ -6257,15 +5980,6 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" -[[package]] -name = "maybe-rayon" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" -dependencies = [ - "cfg-if", -] - [[package]] name = "md-5" version = "0.10.6" @@ -6378,31 +6092,6 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9be0862c1b3f26a88803c4a49de6889c10e608b3ee9344e6ef5b45fb37ad3d1" -[[package]] -name = "mozjpeg" -version = "0.10.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c1e1f1b1de2ee6ef673e8c76666b93794120fa6ec1cb4f535c129ea6f32731d" -dependencies = [ - "arrayvec", - "bytemuck", - "libc", - "mozjpeg-sys", - "rgb", -] - -[[package]] -name = "mozjpeg-sys" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27e31c0171e0b1158c0dfb7386dbdf999f4a9afaa83fd68de39c7929f4d5c16f" -dependencies = [ - "cc", - "dunce", - "libc", - "nasm-rs 0.3.0", -] - [[package]] name = "muda" version = "0.14.0" @@ -6448,24 +6137,6 @@ dependencies = [ "rand 0.8.5", ] -[[package]] -name = "nasm-rs" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4d98d0065f4b1daf164b3eafb11974c94662e5e2396cf03f32d0bb5c17da51" -dependencies = [ - "rayon", -] - -[[package]] -name = "nasm-rs" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12fcfa1bd49e0342ec1d07ed2be83b59963e7acbeb9310e1bb2c07b69dadd959" -dependencies = [ - "jobserver", -] - [[package]] name = "native-tls" version = "0.2.12" @@ -6576,12 +6247,6 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9e591e719385e6ebaeb5ce5d3887f7d5676fceca6411d1925ccc95745f3d6f7" -[[package]] -name = "noop_proc_macro" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" - [[package]] name = "normpath" version = "1.3.0" @@ -6845,10 +6510,7 @@ version = "0.36.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9" dependencies = [ - "flate2", "memchr", - "ruzstd", - "wasmparser 0.215.0", ] [[package]] @@ -6960,12 +6622,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "outref" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f222829ae9293e33a9f5e9f440c6760a3d450a64affe1846486b140db81c1f4" - [[package]] name = "overload" version = "0.1.1" @@ -7030,36 +6686,6 @@ dependencies = [ "system-deps", ] -[[package]] -name = "parcel_selectors" -version = "0.26.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512215cb1d3814e276ace4ec2dbc2cac16726ea3fcac20c22ae1197e16fdd72d" -dependencies = [ - "bitflags 2.6.0", - "cssparser 0.33.0", - "fxhash", - "log", - "phf 0.10.1", - "phf_codegen 0.10.0", - "precomputed-hash", - "smallvec", -] - -[[package]] -name = "parcel_sourcemap" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "485b74d7218068b2b7c0e3ff12fbc61ae11d57cb5d8224f525bd304c6be05bbb" -dependencies = [ - "base64-simd", - "data-url", - "rkyv", - "serde", - "serde_json", - "vlq", -] - [[package]] name = "parking" version = "2.2.0" @@ -7730,25 +7356,6 @@ version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "744a264d26b88a6a7e37cbad97953fa233b94d585236310bcbc88474b4092d79" -[[package]] -name = "profiling" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43d84d1d7a6ac92673717f9f6d1518374ef257669c24ebc5ac25d5033828be58" -dependencies = [ - "profiling-procmacros", -] - -[[package]] -name = "profiling-procmacros" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8021cf59c8ec9c432cfc2526ac6b8aa508ecaf29cd415f271b8406c1b851c3fd" -dependencies = [ - "quote", - "syn 2.0.74", -] - [[package]] name = "prost" version = "0.12.6" @@ -7810,12 +7417,6 @@ 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.32.0" @@ -7888,22 +7489,6 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" -[[package]] -name = "railwind" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bc2b6ca634162c78a6e8d399e12e22b42bcbf2eb60f2b7a2f99aa2379d41800" -dependencies = [ - "hex", - "indexmap 1.9.3", - "lazy_static", - "line-col", - "regex", - "ron", - "serde", - "serde_regex", -] - [[package]] name = "rand" version = "0.7.3" @@ -8006,57 +7591,6 @@ dependencies = [ "unicode-width", ] -[[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", - "cc", - "cfg-if", - "interpolate_name", - "itertools 0.12.1", - "libc", - "libfuzzer-sys", - "log", - "maybe-rayon", - "nasm-rs 0.2.5", - "new_debug_unreachable", - "noop_proc_macro", - "num-derive", - "num-traits", - "once_cell", - "paste", - "profiling", - "rand 0.8.5", - "rand_chacha 0.3.1", - "simd_helpers", - "system-deps", - "thiserror", - "v_frame", - "wasm-bindgen", -] - -[[package]] -name = "ravif" -version = "0.11.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f0bfd976333248de2078d350bfdf182ff96e168a24d23d2436cef320dd4bdd" -dependencies = [ - "avif-serialize", - "imgref", - "loop9", - "quick-error", - "rav1e", - "rgb", -] - [[package]] name = "raw-window-handle" version = "0.5.2" @@ -8270,15 +7804,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "rgb" -version = "0.8.48" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f86ae463694029097b846d8f99fd5536740602ae00022c0c50c5600720b2f71" -dependencies = [ - "bytemuck", -] - [[package]] name = "rhai" version = "1.18.0" @@ -8360,19 +7885,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "ron" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" -dependencies = [ - "base64 0.21.7", - "bitflags 2.6.0", - "indexmap 2.4.0", - "serde", - "serde_derive", -] - [[package]] name = "router-static-generation" version = "0.1.0" @@ -8595,16 +8107,6 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" -[[package]] -name = "ruzstd" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5022b253619b1ba797f243056276bed8ed1a73b0f5a7ce7225d524067644bf8f" -dependencies = [ - "byteorder", - "twox-hash", -] - [[package]] name = "ryu" version = "1.0.18" @@ -8717,7 +8219,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe" dependencies = [ "bitflags 1.3.2", - "cssparser 0.27.2", + "cssparser", "derive_more", "fxhash", "log", @@ -8829,16 +8331,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "serde_regex" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8136f1a4ea815d7eac4101cfd0b16dc0cb5e1fe1b8609dfd728058656b7badf" -dependencies = [ - "regex", - "serde", -] - [[package]] name = "serde_repr" version = "0.1.19" @@ -9069,30 +8561,12 @@ dependencies = [ "rand_core 0.6.4", ] -[[package]] -name = "simd-abstraction" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cadb29c57caadc51ff8346233b5cec1d240b68ce55cf1afc764818791876987" -dependencies = [ - "outref", -] - [[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 = "simdutf8" version = "0.1.4" @@ -9822,7 +9296,7 @@ dependencies = [ "handlebars", "heck 0.5.0", "hex", - "image 0.24.9", + "image", "log", "md5", "os_pipe", @@ -10494,16 +9968,6 @@ dependencies = [ "cipher", ] -[[package]] -name = "twox-hash" -version = "1.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" -dependencies = [ - "cfg-if", - "static_assertions", -] - [[package]] name = "typeid" version = "1.0.1" @@ -10752,17 +10216,6 @@ dependencies = [ "sha1_smol", ] -[[package]] -name = "v_frame" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6f32aaa24bacd11e488aa9ba66369c7cd514885742c9fe08cfe85884db3e92b" -dependencies = [ - "aligned-vec", - "num-traits", - "wasm-bindgen", -] - [[package]] name = "valuable" version = "0.1.0" @@ -10787,12 +10240,6 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" -[[package]] -name = "vlq" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65dd7eed29412da847b0f78bcec0ac98588165988a8cfe41d4ea1d429f8ccfff" - [[package]] name = "walkdir" version = "2.5.0" @@ -10816,7 +10263,7 @@ dependencies = [ "log", "walrus-macro", "wasm-encoder", - "wasmparser 0.212.0", + "wasmparser", ] [[package]] @@ -11037,7 +10484,7 @@ dependencies = [ "leb128", "log", "walrus", - "wasmparser 0.212.0", + "wasmparser", ] [[package]] @@ -11139,15 +10586,6 @@ dependencies = [ "serde", ] -[[package]] -name = "wasmparser" -version = "0.215.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fbde0881f24199b81cf49b6ff8f9c145ac8eb1b7fc439adb5c099734f7d90e" -dependencies = [ - "bitflags 2.6.0", -] - [[package]] name = "web-sys" version = "0.3.70" @@ -11981,12 +11419,6 @@ dependencies = [ "pkg-config", ] -[[package]] -name = "zune-core" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" - [[package]] name = "zune-inflate" version = "0.2.54" @@ -11996,15 +11428,6 @@ dependencies = [ "simd-adler32", ] -[[package]] -name = "zune-jpeg" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16099418600b4d8f028622f73ff6e3deaabdff330fb9a2a131dea781ee8b0768" -dependencies = [ - "zune-core", -] - [[package]] name = "zvariant" version = "4.2.0" diff --git a/Cargo.toml b/Cargo.toml index edcbbe85a9..e6896c684a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,8 @@ members = [ "packages/server-macro", "packages/static-generation", "packages/lazy-js-bundle", + "packages/document", + "packages/runtime-config", # Fullstack examples "packages/fullstack/examples/hello-world", @@ -44,10 +46,11 @@ members = [ "packages/static-generation/examples/simple", "packages/static-generation/examples/router", "packages/static-generation/examples/github-pages", + # Full project examples "examples/tailwind", "examples/PWA-example", - # "examples/openid_connect_demo", + # Playwright tests "packages/playwright-tests/liveview", "packages/playwright-tests/web", @@ -59,13 +62,10 @@ members = [ # manganis "packages/manganis", "packages/manganis-macro", - "packages/manganis-common", - "packages/manganis-cli-support", "examples/manganis-test-package", "examples/manganis-test-package/test-package-dependency", "examples/manganis-test-package/test-package-nested-dependency", - "packages/document", - "packages/runtime-config", + ] exclude = ["examples/mobile_demo", "examples/openid_connect_demo"] @@ -105,19 +105,10 @@ dioxus_server_macro = { path = "packages/server-macro", version = "0.6.0-alpha.2 lazy-js-bundle = { path = "packages/lazy-js-bundle", version = "0.6.0-alpha.2" } manganis = { path = "packages/manganis", version = "0.6.0-alpha.2" } -manganis-common = { path = "packages/manganis-common", version = "0.6.0-alpha.2" } -manganis-cli-support = { path = "packages/manganis-cli-support", version = "0.6.0-alpha.2" } manganis-macro = { path = "packages/manganis-macro", version = "0.6.0-alpha.2" } warnings = { version = "0.2.0" } -# manganis-cli-support = { version = "0.3.0-alpha.0", features = ["html"], path = "../ecosystem-dioxus/manganis/packages/cli-support" } -# manganis = { version = "0.3.0-alpha.0", default-features = false, features = ["macro"], path = "../ecosystem-dioxus/manganis/packages/manganis" } -# manganis-cli-support = { version = "0.3.0-alpha.0", features = ["html"] } -# manganis = { version = "0.3.0-alpha.0", default-features = false } -# manganis-cli-support = { version = "0.3.0-alpha.1", features = ["html"] } -# manganis = { version = "0.3.0-alpha.1", default-features = false, features = ["html", "macro"]} - # a fork of pretty please for tests prettier-please = { version = "0.3.0", features = ["verbatim"]} @@ -267,7 +258,6 @@ fullstack = ["dioxus/fullstack"] axum = ["dioxus/axum"] server = ["dioxus/axum"] web = ["dioxus/web"] -# collect-assets = ["dep:manganis"] http = ["dep:reqwest", "dep:http-range"] [[example]] diff --git a/packages/cli/Cargo.toml b/packages/cli/Cargo.toml index 9f7ee74405..a89fe71025 100644 --- a/packages/cli/Cargo.toml +++ b/packages/cli/Cargo.toml @@ -83,7 +83,6 @@ tauri-bundler = { workspace = true } prettyplease = { workspace = true } # Assets -manganis-cli-support = { workspace = true, features = ["html"] } brotli = "6.0.0" dioxus-autofmt = { workspace = true } @@ -106,6 +105,61 @@ crossterm = { version = "0.27.0", features = ["event-stream"] } ansi-to-tui = "=5.0.0-rc.1" ansi-to-html = "0.2.1" + + + + +# # just use the manganis crate directly since it has all the types we need +# manganis = { workspace = true } +# serde = { version = "1.0.183", features = ["derive"] } +# serde_json = {version="1.0.116"} +# anyhow = "1" +# rayon = "1.7.0" +# rustc-hash = "1.1.0" + +# # Tailwind +# railwind = "0.1.5" + +# # Image compression/conversion +# # JPEG +# mozjpeg = { version = "0.10.7", default-features = false, features = ["parallel"] } +# # PNG +# imagequant = "4.2.0" +# png = "0.17.9" +# # Conversion +# image = { version = "0.25" } +# ravif = { version = "0.11", default-features = false } + +# # CSS Minification +# lightningcss = "1.0.0-alpha.44" + +# # # Js minification +# # swc = "=0.283.0" +# # swc_common = "=0.37.1" + +# # Remote assets +# url = { version = "2.4.0", features = ["serde"] } +# reqwest = { version = "0.12.5", features = ["blocking"] } +# tracing = "0.1.37" + +# # Extracting data from an executable +# object = {version="0.36.0", features=["wasm"]} + +# [dev-dependencies] +# tracing-subscriber = "0.3.18" + +# [features] +# default = ["html"] +# html = [] +# # html = ["manganis-common/html"] + +# asm = ["ravif/asm", "mozjpeg/nasm_simd"] + +# # Note: this feature now enables nothing and should be removed in the next major version +# webp = [] +# avif = [] + + # on macos, we need to specify the vendored feature on ssl when cross compiling # [target.'cfg(target_os = "macos")'.dependencies] # openssl = { version = "0.10", features = ["vendored"] } diff --git a/packages/cli/src/assets.rs b/packages/cli/src/assets.rs index 076c0443cb..2cb4489162 100644 --- a/packages/cli/src/assets.rs +++ b/packages/cli/src/assets.rs @@ -5,7 +5,7 @@ use crate::Result; use anyhow::Context; use brotli::enc::BrotliEncoderParams; use futures_channel::mpsc::UnboundedSender; -use manganis_cli_support::{process_file, AssetManifest, AssetManifestExt, AssetType}; +// use manganis_cli_support::{process_file, AssetManifest, AssetManifestExt, AssetType}; use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use std::fs; use std::path::Path; @@ -16,6 +16,18 @@ use std::{fs::File, io::Write}; use tracing::Level; use walkdir::WalkDir; +mod file; +mod folder; +mod linker_intercept; +mod manifest; + +pub use file::process_file; +pub use folder::process_folder; +pub use linker_intercept::*; +pub use manifest::*; + +pub struct AssetManifest {} + /// The temp file name for passing manganis json from linker to current exec. pub const MG_JSON_OUT: &str = "mg-out"; diff --git a/packages/manganis-cli-support/src/file.rs b/packages/cli/src/assets/file.rs similarity index 100% rename from packages/manganis-cli-support/src/file.rs rename to packages/cli/src/assets/file.rs diff --git a/packages/manganis-cli-support/src/folder.rs b/packages/cli/src/assets/folder.rs similarity index 100% rename from packages/manganis-cli-support/src/folder.rs rename to packages/cli/src/assets/folder.rs diff --git a/packages/manganis-cli-support/src/linker_intercept.rs b/packages/cli/src/assets/linker_intercept.rs similarity index 100% rename from packages/manganis-cli-support/src/linker_intercept.rs rename to packages/cli/src/assets/linker_intercept.rs diff --git a/packages/manganis-cli-support/src/manifest.rs b/packages/cli/src/assets/manifest.rs similarity index 100% rename from packages/manganis-cli-support/src/manifest.rs rename to packages/cli/src/assets/manifest.rs diff --git a/packages/manganis-cli-support/src/lib.rs b/packages/cli/src/assets/mod.rs similarity index 90% rename from packages/manganis-cli-support/src/lib.rs rename to packages/cli/src/assets/mod.rs index ef3b85e52c..d67fdf9f3d 100644 --- a/packages/manganis-cli-support/src/lib.rs +++ b/packages/cli/src/assets/mod.rs @@ -12,3 +12,5 @@ pub use folder::process_folder; pub use linker_intercept::*; pub use manganis_common::*; pub use manifest::*; + +pub struct AssetManifest {} diff --git a/packages/cli/src/builder/cargo.rs b/packages/cli/src/builder/cargo.rs index 308942d299..c760c09aae 100644 --- a/packages/cli/src/builder/cargo.rs +++ b/packages/cli/src/builder/cargo.rs @@ -15,7 +15,7 @@ use crate::link::LinkCommand; use crate::Result; use anyhow::Context; use futures_channel::mpsc::UnboundedSender; -use manganis_cli_support::AssetManifest; +// use manganis_cli_support::AssetManifest; use std::fs::create_dir_all; use std::path::PathBuf; @@ -196,38 +196,39 @@ impl BuildRequest { cargo_args: Vec, progress: &mut UnboundedSender, ) -> anyhow::Result> { - // If this is the server build, the client build already copied any assets we need - if self.target_platform == TargetPlatform::Server { - return Ok(None); - } - // If assets are skipped, we don't need to collect them - if self.build_arguments.skip_assets { - return Ok(None); - } - - // Start Manganis linker intercept. - let linker_args = vec![format!("{}", self.target_out_dir().display())]; - - // Don't block the main thread - manganis should not be running its own std process but it's - // fine to wrap it here at the top - let build = self.clone(); - let mut progress = progress.clone(); - tokio::task::spawn_blocking(move || { - manganis_cli_support::start_linker_intercept( - &LinkCommand::command_name(), - cargo_args, - Some(linker_args), - )?; - let assets = asset_manifest(&build); - // Collect assets from the asset manifest the linker intercept created - process_assets(&build, &assets, &mut progress)?; - // Create the __assets_head.html file for bundling - create_assets_head(&build, &assets)?; - - Ok(Some(assets)) - }) - .await - .unwrap() + todo!("collect assets is disabled currently") + // // If this is the server build, the client build already copied any assets we need + // if self.target_platform == TargetPlatform::Server { + // return Ok(None); + // } + // // If assets are skipped, we don't need to collect them + // if self.build_arguments.skip_assets { + // return Ok(None); + // } + + // // Start Manganis linker intercept. + // let linker_args = vec![format!("{}", self.target_out_dir().display())]; + + // // Don't block the main thread - manganis should not be running its own std process but it's + // // fine to wrap it here at the top + // let build = self.clone(); + // let mut progress = progress.clone(); + // tokio::task::spawn_blocking(move || { + // manganis_cli_support::start_linker_intercept( + // &LinkCommand::command_name(), + // cargo_args, + // Some(linker_args), + // )?; + // let assets = asset_manifest(&build); + // // Collect assets from the asset manifest the linker intercept created + // process_assets(&build, &assets, &mut progress)?; + // // Create the __assets_head.html file for bundling + // create_assets_head(&build, &assets)?; + + // Ok(Some(assets)) + // }) + // .await + // .unwrap() } pub fn copy_assets_dir(&self) -> anyhow::Result<()> { diff --git a/packages/cli/src/tracer.rs b/packages/cli/src/tracer.rs index afffd42a3c..40216c3511 100644 --- a/packages/cli/src/tracer.rs +++ b/packages/cli/src/tracer.rs @@ -14,7 +14,7 @@ const LOG_ENV: &str = "DIOXUS_LOG"; pub fn build_tracing() -> CLILogControl { // If {LOG_ENV} is set, default to env, otherwise filter to cli // and manganis warnings and errors from other crates - let mut filter = EnvFilter::new("error,dx=info,dioxus-cli=info,manganis-cli-support=info"); + let mut filter = EnvFilter::new("error,dx=info,dioxus-cli=info"); if env::var(LOG_ENV).is_ok() { filter = EnvFilter::from_env(LOG_ENV); } diff --git a/packages/core-macro/src/lib.rs b/packages/core-macro/src/lib.rs index 3ff19d9be2..e02f3a2d94 100644 --- a/packages/core-macro/src/lib.rs +++ b/packages/core-macro/src/lib.rs @@ -13,16 +13,6 @@ mod utils; use dioxus_rsx as rsx; -#[doc = include_str!("../docs/props.md")] -#[proc_macro_derive(Props, attributes(props))] -pub fn derive_props(input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as syn::DeriveInput); - match props::impl_my_derive(&input) { - Ok(output) => output.into(), - Err(error) => error.to_compile_error().into(), - } -} - #[doc = include_str!("../docs/rsx.md")] #[proc_macro] pub fn rsx(tokens: TokenStream) -> TokenStream { @@ -39,3 +29,13 @@ pub fn component(_args: TokenStream, input: TokenStream) -> TokenStream { .into_token_stream() .into() } + +#[doc = include_str!("../docs/props.md")] +#[proc_macro_derive(Props, attributes(props))] +pub fn derive_props(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as syn::DeriveInput); + match props::impl_my_derive(&input) { + Ok(output) => output.into(), + Err(error) => error.to_compile_error().into(), + } +} diff --git a/packages/manganis-cli-support/Cargo.toml b/packages/manganis-cli-support/Cargo.toml deleted file mode 100644 index 34f6475717..0000000000 --- a/packages/manganis-cli-support/Cargo.toml +++ /dev/null @@ -1,62 +0,0 @@ -[package] -name = "manganis-cli-support" -version.workspace = true -edition = "2021" -authors = ["Evan Almloff"] -description = "Ergonomic, automatic, cross crate asset collection and optimization" -license = "MIT OR Apache-2.0" -repository = "https://github.com/DioxusLabs/manganis/" -homepage = "https://dioxuslabs.com" -keywords = ["assets"] - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -manganis-common = { workspace = true } - -serde = { version = "1.0.183", features = ["derive"] } -serde_json = {version="1.0.116"} -anyhow = "1" -rayon = "1.7.0" -rustc-hash = "1.1.0" - -# Tailwind -railwind = "0.1.5" - -# Image compression/conversion -# JPEG -mozjpeg = { version = "0.10.7", default-features = false, features = ["parallel"] } -# PNG -imagequant = "4.2.0" -png = "0.17.9" -# Conversion -image = { version = "0.25" } -ravif = { version = "0.11", default-features = false } - -# CSS Minification -lightningcss = "1.0.0-alpha.44" - -# # Js minification -# swc = "=0.283.0" -# swc_common = "=0.37.1" - -# Remote assets -url = { version = "2.4.0", features = ["serde"] } -reqwest = { version = "0.12.5", features = ["blocking"] } -tracing = "0.1.37" - -# Extracting data from an executable -object = {version="0.36.0", features=["wasm"]} - -[dev-dependencies] -tracing-subscriber = "0.3.18" - -[features] -default = ["html"] -html = ["manganis-common/html"] - -asm = ["ravif/asm", "mozjpeg/nasm_simd"] - -# Note: this feature now enables nothing and should be removed in the next major version -webp = [] -avif = [] diff --git a/packages/manganis-cli-support/README.md b/packages/manganis-cli-support/README.md deleted file mode 100644 index 5ad9e5d3ea..0000000000 --- a/packages/manganis-cli-support/README.md +++ /dev/null @@ -1,58 +0,0 @@ -# Manganis CLI Support - -This crate provides utilities to collect assets that integrate with the Manganis macro. It makes it easy to integrate an asset collection and optimization system into a build tool. - -```rust, no_run -use manganis_cli_support::{AssetManifestExt, ManganisSupportGuard}; -use manganis_common::{AssetManifest, Config}; -use std::process::Command; - -// This is the location where the assets will be copied to in the filesystem -let assets_file_location = "./assets"; -// This is the location where the assets will be served from -let assets_serve_location = "/assets"; - -// First set any settings you need for the build. -Config::default() - .with_assets_serve_location(assets_serve_location) - .save(); - -// Tell manganis that you support assets -let _guard = ManganisSupportGuard::default(); - -// Determine if Rust is trying to link: -if let Some((_working_dir, object_files)) = manganis_cli_support::linker_intercept(std::env::args()) { - // If it is, collect the assets. - let manifest = AssetManifest::load(object_files); - - // Remove the old assets - let _ = std::fs::remove_dir_all(assets_file_location); - - // And copy the static assets to the public directory - manifest - .copy_static_assets_to(assets_file_location) - .unwrap(); - - // Then collect the tailwind CSS - let css = manifest.collect_tailwind_css(true, &mut Vec::new()); - - // And write the CSS to the public directory - std::fs::write(format!("{}/tailwind.css", assets_file_location), css).unwrap(); - -} else { - // If it isn't, build your app and initiate the helper function `start_linker_intercept()` - - // Put any cargo args in a slice that should also be passed - // to manganis toreproduce the same build. e.g. the `--release` flag - let args: Vec<&str> = vec![]; - Command::new("cargo") - .arg("build") - .args(args.clone()) - .spawn() - .unwrap() - .wait() - .unwrap(); - - manganis_cli_support::start_linker_intercept(None, args).unwrap(); -} -``` diff --git a/packages/manganis-cli-support/examples/cli.rs b/packages/manganis-cli-support/examples/cli.rs deleted file mode 100644 index 5a5599e710..0000000000 --- a/packages/manganis-cli-support/examples/cli.rs +++ /dev/null @@ -1,74 +0,0 @@ -use manganis_cli_support::AssetManifestExt; -use std::{path::PathBuf, process::Command}; - -// This is the location where the assets will be copied to in the filesystem -const ASSETS_FILE_LOCATION: &str = "./assets"; - -// This is the location where the assets will be served from -const ASSETS_SERVE_LOCATION: &str = "./assets/"; - -fn main() { - tracing_subscriber::fmt::init(); - - // Handle the commands. - let args: Vec = std::env::args().collect(); - - if let Some(arg) = args.get(1) { - if arg == "link" { - link(); - return; - } else if arg == "build" { - println!("Building!"); - build(); - return; - } - } - - println!("Unknown Command"); -} - -fn build() { - // Build your application - let current_dir = std::env::current_dir().unwrap(); - - let args = ["--release"]; - Command::new("cargo") - .current_dir(¤t_dir) - .arg("build") - .args(args) - .env("MG_BASEPATH", "/blah/") - .spawn() - .unwrap() - .wait() - .unwrap(); - - // Call the helper function to intercept the Rust linker. - // We will pass the current working directory as it may get lost. - let work_dir = std::env::current_dir().unwrap(); - let link_args = vec![format!("{}", work_dir.display())]; - manganis_cli_support::start_linker_intercept("link", args, Some(link_args)).unwrap(); -} - -fn link() { - let (link_args, object_files) = - manganis_cli_support::linker_intercept(std::env::args()).unwrap(); - - // // Extract the assets - // let assets = AssetManifest::load_from_objects(object_files); - - // let working_dir = PathBuf::from(link_args.first().unwrap()); - // let assets_dir = working_dir.join(working_dir.join(ASSETS_FILE_LOCATION)); - - // // Remove the old assets - // let _ = std::fs::remove_dir_all(&assets_dir); - - // // And copy the static assets to the public directory - // assets.copy_static_assets_to(&assets_dir).unwrap(); - - // // Then collect the tailwind CSS - // let css = assets.collect_tailwind_css(true, &mut Vec::new()); - - // // And write the CSS to the public directory - // let tailwind_path = assets_dir.join("tailwind.css"); - // std::fs::write(tailwind_path, css).unwrap(); -} diff --git a/packages/manganis-cli-support/tests/collects_assets.rs b/packages/manganis-cli-support/tests/collects_assets.rs deleted file mode 100644 index 9cffd29eda..0000000000 --- a/packages/manganis-cli-support/tests/collects_assets.rs +++ /dev/null @@ -1,93 +0,0 @@ -use manganis_cli_support::AssetManifestExt; -use manganis_common::AssetType; -use std::path::PathBuf; -use std::process::{Command, Stdio}; - -// #[test] -// fn collects_assets() { -// tracing_subscriber::fmt::init(); - -// // Get args and default to "build" -// let args: Vec = std::env::args().collect(); -// let command = match args.get(1) { -// Some(a) => a.clone(), -// None => "build".to_string(), -// }; - -// // Check if rustc is trying to link -// if command == "link" { -// link(); -// } else { -// build(); -// } -// } - -// fn build() { -// // Find the test package directory which is up one directory from this package -// let mut test_package_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")) -// .parent() -// .unwrap() -// .to_path_buf(); -// test_package_dir.push("test-package"); - -// println!("running the CLI from {test_package_dir:?}"); - -// // Then build your application -// let args = ["--target", "wasm32-unknown-unknown", "--release"]; -// Command::new("cargo") -// .arg("build") -// .args(args) -// .current_dir(&test_package_dir) -// .stdout(Stdio::piped()) -// .spawn() -// .unwrap() -// .wait() -// .unwrap(); - -// println!("Collecting Assets"); - -// // Call the helper function to intercept the Rust linker. -// // We will pass the current working directory as it may get lost. -// let link_args = vec![format!("{}", test_package_dir.display())]; -// manganis_cli_support::start_linker_intercept("link", args, Some(link_args)).unwrap(); -// } - -// fn link() { -// let (link_args, object_files) = -// manganis_cli_support::linker_intercept(std::env::args()).unwrap(); - -// // Recover the working directory from the link args. -// let working_dir = PathBuf::from(link_args.first().unwrap()); - -// // Then collect the assets -// let assets = AssetManifest::load_from_objects(object_files); - -// let all_assets = assets.assets(); -// println!("{:#?}", all_assets); - -// let locations = all_assets -// .iter() -// .filter_map(|a| match a { -// AssetType::Resource(f) => Some(f.location()), -// _ => None, -// }) -// .collect::>(); - -// // Make sure the right number of assets were collected -// assert_eq!(locations.len(), 16); - -// // Then copy the assets to a temporary directory and run the application -// let assets_dir = PathBuf::from("./assets"); -// assets.copy_static_assets_to(assets_dir).unwrap(); - -// // Then run the application -// let status = Command::new("cargo") -// .arg("run") -// .arg("--release") -// .current_dir(&working_dir) -// .status() -// .unwrap(); - -// // Make sure the application exited successfully -// assert!(status.success()); -// } diff --git a/packages/manganis-common/Cargo.toml b/packages/manganis-common/Cargo.toml deleted file mode 100644 index ea22084afa..0000000000 --- a/packages/manganis-common/Cargo.toml +++ /dev/null @@ -1,30 +0,0 @@ -[package] -name = "manganis-common" -version.workspace = true -edition = "2021" -authors = ["Evan Almloff"] -description = "Ergonomic, automatic, cross crate asset collection and optimization" -license = "MIT OR Apache-2.0" -repository = "https://github.com/DioxusLabs/manganis/" -homepage = "https://dioxuslabs.com" -keywords = ["assets"] - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -serde = { version = "1.0.183", features = ["derive"] } -anyhow = "1" -base64 = { workspace = true } -# infer = { workspace = true } -# fluent-uri = { version = "0.2.0", features = ["serde"] } - -# tracing = "0.1.40" - -# Remote assets -# reqwest = { version = "0.12.5", features = ["blocking"] } -# http = "1.1.0" -# http-serde = "2.1.1" - -[features] -default = ["html"] -html = [] diff --git a/packages/manganis-macro/Cargo.toml b/packages/manganis-macro/Cargo.toml index 0695cc83dd..702adde198 100644 --- a/packages/manganis-macro/Cargo.toml +++ b/packages/manganis-macro/Cargo.toml @@ -18,14 +18,6 @@ quote = "1.0" serde_json = "1.0" syn = { version = "2.0", features = ["full", "extra-traits"] } serde = { workspace = true, features = ["derive"] } -# base64 = { workspace = true, optional = true } - -# manganis-common = { workspace = true } -# manganis-cli-support = { workspace = true, optional = true } -# manganis-common = { path = "../common", version = "0.3.0-alpha.1" } -# manganis-cli-support = { path = "../cli-support", version = "0.3.0-alpha.1", optional = true } -# base64 = { version = "0.21.5", optional = true } [features] default = [] -# url-encoding = ["base64"] diff --git a/packages/manganis/Cargo.toml b/packages/manganis/Cargo.toml index 39ee52b512..0011c123bc 100644 --- a/packages/manganis/Cargo.toml +++ b/packages/manganis/Cargo.toml @@ -15,22 +15,22 @@ keywords = ["assets"] [dependencies] manganis-macro = { workspace = true, optional = true } once_cell = "1.19.0" -# dirs = "5.0.1" -# infer = { workspace = true } -# manganis-common = { workspace = true } dunce = "1.0.2" dioxus-core-types = { workspace = true } +# dunce = "1.0.2" +# [target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies] +# core-foundation = "0.10.0" # [target.'cfg(target_os = "macos")'.dependencies] # core-foundation = "0.9.3" - # [target.'cfg(target_os ="macos")'.dependencies] # infer = { workspace = true } -manganis-common = { workspace = true } -# dunce = "1.0.2" - -# [target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies] -# core-foundation = "0.10.0" +# dirs = "5.0.1" +# infer = { workspace = true } +# manganis-common = { workspace = true } +serde = { version = "1.0.183", features = ["derive"] } +anyhow = "1" +base64 = { workspace = true } [features] diff --git a/packages/manganis-common/src/asset.rs b/packages/manganis/src/common/asset.rs similarity index 100% rename from packages/manganis-common/src/asset.rs rename to packages/manganis/src/common/asset.rs diff --git a/packages/manganis-common/src/asset/error.rs b/packages/manganis/src/common/asset/error.rs similarity index 100% rename from packages/manganis-common/src/asset/error.rs rename to packages/manganis/src/common/asset/error.rs diff --git a/packages/manganis-common/src/asset/file.rs b/packages/manganis/src/common/asset/file.rs similarity index 100% rename from packages/manganis-common/src/asset/file.rs rename to packages/manganis/src/common/asset/file.rs diff --git a/packages/manganis-common/src/asset/folder.rs b/packages/manganis/src/common/asset/folder.rs similarity index 100% rename from packages/manganis-common/src/asset/folder.rs rename to packages/manganis/src/common/asset/folder.rs diff --git a/packages/manganis-common/src/asset/meta.rs b/packages/manganis/src/common/asset/meta.rs similarity index 100% rename from packages/manganis-common/src/asset/meta.rs rename to packages/manganis/src/common/asset/meta.rs diff --git a/packages/manganis-common/src/asset/resource.rs b/packages/manganis/src/common/asset/resource.rs similarity index 100% rename from packages/manganis-common/src/asset/resource.rs rename to packages/manganis/src/common/asset/resource.rs diff --git a/packages/manganis-common/src/asset/tailwind.rs b/packages/manganis/src/common/asset/tailwind.rs similarity index 100% rename from packages/manganis-common/src/asset/tailwind.rs rename to packages/manganis/src/common/asset/tailwind.rs diff --git a/packages/manganis-common/src/built.rs b/packages/manganis/src/common/built.rs similarity index 100% rename from packages/manganis-common/src/built.rs rename to packages/manganis/src/common/built.rs diff --git a/packages/manganis-common/src/config.rs b/packages/manganis/src/common/config.rs similarity index 100% rename from packages/manganis-common/src/config.rs rename to packages/manganis/src/common/config.rs diff --git a/packages/manganis-common/src/file.rs b/packages/manganis/src/common/file.rs similarity index 100% rename from packages/manganis-common/src/file.rs rename to packages/manganis/src/common/file.rs diff --git a/packages/manganis-common/src/lib.rs b/packages/manganis/src/common/lib.rs similarity index 100% rename from packages/manganis-common/src/lib.rs rename to packages/manganis/src/common/lib.rs diff --git a/packages/manganis-common/src/linker.rs b/packages/manganis/src/common/linker.rs similarity index 100% rename from packages/manganis-common/src/linker.rs rename to packages/manganis/src/common/linker.rs diff --git a/packages/router/Cargo.toml b/packages/router/Cargo.toml index f1d76345b2..6693619a2f 100644 --- a/packages/router/Cargo.toml +++ b/packages/router/Cargo.toml @@ -29,7 +29,6 @@ dioxus-ssr = { workspace = true, optional = true } http = { workspace = true, optional = true } dioxus-fullstack = { workspace = true, optional = true } tokio = { workspace = true, features = ["full"], optional = true } -# dioxus-cli-config = { workspace = true, features = ["read-config"] } rustversion = "1.0.17" # you need to comment this out when publishing since cargo workspaces is not smart enough to wipe this when dropping From c3896da4d39fd9d9299ca0e4442ed3551c7d77f7 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 21 Aug 2024 21:52:08 -0700 Subject: [PATCH 029/139] dont need mobile demo anymore --- Cargo.lock | 32 +- Cargo.toml | 2 +- examples/mobile_demo/.gitignore | 10 - examples/mobile_demo/Cargo.lock | 4007 --------------------- examples/mobile_demo/Cargo.toml | 51 - examples/mobile_demo/README.md | 11 - examples/mobile_demo/mobile.toml | 8 - examples/mobile_demo/src/index.html | 12 - examples/mobile_demo/src/lib.rs | 90 - packages/cli/Cargo.toml | 2 +- packages/dioxus-lib/Cargo.toml | 1 + packages/dioxus/Cargo.toml | 79 +- packages/dioxus/src/lib.rs | 109 +- packages/liveview/Cargo.toml | 2 +- packages/liveview/examples/axum.rs | 2 +- packages/liveview/examples/axum_stress.rs | 2 +- packages/manganis-macro/src/lib.rs | 95 - packages/manganis/Cargo.toml | 5 +- packages/signals/src/lib.rs | 2 + 19 files changed, 127 insertions(+), 4395 deletions(-) delete mode 100644 examples/mobile_demo/.gitignore delete mode 100644 examples/mobile_demo/Cargo.lock delete mode 100644 examples/mobile_demo/Cargo.toml delete mode 100644 examples/mobile_demo/README.md delete mode 100644 examples/mobile_demo/mobile.toml delete mode 100644 examples/mobile_demo/src/index.html delete mode 100644 examples/mobile_demo/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 2437742607..33fab60d32 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1187,7 +1187,7 @@ dependencies = [ "clap", "console", "dialoguer", - "env_logger 0.11.5", + "env_logger", "fs-err", "git2", "gix-config", @@ -2308,7 +2308,7 @@ dependencies = [ "dioxus-ssr", "dioxus-static-site-generation", "dioxus-web", - "env_logger 0.10.2", + "env_logger", "futures-util", "manganis", "rand 0.8.5", @@ -2375,7 +2375,7 @@ dependencies = [ "dioxus-html", "dioxus-rsx", "dirs", - "env_logger 0.11.5", + "env_logger", "fern", "flate2", "fs_extra", @@ -2713,6 +2713,7 @@ dependencies = [ "dioxus-html", "dioxus-rsx", "dioxus-signals", + "manganis", ] [[package]] @@ -2727,10 +2728,10 @@ dependencies = [ "dioxus-hot-reload", "dioxus-html", "dioxus-interpreter-js", + "env_logger", "futures-channel", "futures-util", "generational-box", - "pretty_env_logger", "rustc-hash 1.1.0", "serde", "serde_json", @@ -3280,19 +3281,6 @@ dependencies = [ "regex", ] -[[package]] -name = "env_logger" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" -dependencies = [ - "humantime", - "is-terminal", - "log", - "regex", - "termcolor", -] - [[package]] name = "env_logger" version = "0.11.5" @@ -7242,16 +7230,6 @@ dependencies = [ "yansi", ] -[[package]] -name = "pretty_env_logger" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "865724d4dbe39d9f3dd3b52b88d859d66bcb2d6a0acfd5ea68a65fb66d4bdc1c" -dependencies = [ - "env_logger 0.10.2", - "log", -] - [[package]] name = "prettyplease" version = "0.2.20" diff --git a/Cargo.toml b/Cargo.toml index e6896c684a..e6efefceb3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,7 +67,6 @@ members = [ "examples/manganis-test-package/test-package-nested-dependency", ] -exclude = ["examples/mobile_demo", "examples/openid_connect_demo"] [workspace.package] version = "0.6.0-alpha.2" @@ -162,6 +161,7 @@ gloo-timers = "0.3.0" fluent-uri = { version = "0.2.0", features = ["serde"] } internment = { version = "0.7.0" } proc-macro2-diagnostics = { version = "0.10", default-features = false } +env_logger = "0.11.0" # desktop wry = { version = "0.42.0", default-features = false } diff --git a/examples/mobile_demo/.gitignore b/examples/mobile_demo/.gitignore deleted file mode 100644 index e1e084c4bb..0000000000 --- a/examples/mobile_demo/.gitignore +++ /dev/null @@ -1,10 +0,0 @@ -# Rust -target/ -**/*.rs.bk - -# cargo-mobile2 -.cargo/ -/gen - -# macOS -.DS_Store diff --git a/examples/mobile_demo/Cargo.lock b/examples/mobile_demo/Cargo.lock deleted file mode 100644 index 7f50292263..0000000000 --- a/examples/mobile_demo/Cargo.lock +++ /dev/null @@ -1,4007 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "addr2line" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "ahash" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy", -] - -[[package]] -name = "aho-corasick" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" -dependencies = [ - "memchr", -] - -[[package]] -name = "allocator-api2" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" - -[[package]] -name = "android_log-sys" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85965b6739a430150bdd138e2374a98af0c3ee0d030b3bb7fc3bddff58d0102e" - -[[package]] -name = "android_logger" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ec2333c185d826313162cee39d3fcc6a84ba08114a839bebf53b961e7e75773" -dependencies = [ - "android_log-sys", - "env_logger 0.7.1", - "lazy_static", - "log", -] - -[[package]] -name = "anyhow" -version = "1.0.72" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" - -[[package]] -name = "async-channel" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" -dependencies = [ - "concurrent-queue", - "event-listener", - "futures-core", -] - -[[package]] -name = "async-lock" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa24f727524730b077666307f2734b4a1a1c57acb79193127dcc8914d5242dd7" -dependencies = [ - "event-listener", -] - -[[package]] -name = "async-task" -version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae" - -[[package]] -name = "async-trait" -version = "0.1.72" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "atk" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4af014b17dd80e8af9fa689b2d4a211ddba6eb583c1622f35d0cb543f6b17e4" -dependencies = [ - "atk-sys", - "glib", - "libc", -] - -[[package]] -name = "atk-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "251e0b7d90e33e0ba930891a505a9a35ece37b2dd37a14f3ffc306c13b980009" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "atomic-waker" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3" - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "backtrace" -version = "0.3.68" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - -[[package]] -name = "base64" -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" -dependencies = [ - "serde", -] - -[[package]] -name = "block" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "blocking" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77231a1c8f801696fc0123ec6150ce92cffb8e164a02afb9c8ddee0e9b65ad65" -dependencies = [ - "async-channel", - "async-lock", - "async-task", - "atomic-waker", - "fastrand", - "futures-lite", - "log", -] - -[[package]] -name = "bumpalo" -version = "3.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" - -[[package]] -name = "bytemuck" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "bytes" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" - -[[package]] -name = "cairo-rs" -version = "0.18.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" -dependencies = [ - "bitflags 2.4.2", - "cairo-sys-rs", - "glib", - "libc", - "once_cell", - "thiserror", -] - -[[package]] -name = "cairo-sys-rs" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" -dependencies = [ - "glib-sys", - "libc", - "system-deps", -] - -[[package]] -name = "cc" -version = "1.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" - -[[package]] -name = "cesu8" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" - -[[package]] -name = "cfb" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" -dependencies = [ - "byteorder", - "fnv", - "uuid", -] - -[[package]] -name = "cfg-expr" -version = "0.15.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b40ccee03b5175c18cde8f37e7d2a33bcef6f8ec8f7cc0d81090d1bb380949c9" -dependencies = [ - "smallvec", - "target-lexicon", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "cfg_aliases" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" - -[[package]] -name = "ciborium" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" -dependencies = [ - "ciborium-io", - "ciborium-ll", - "serde", -] - -[[package]] -name = "ciborium-io" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" - -[[package]] -name = "ciborium-ll" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" -dependencies = [ - "ciborium-io", - "half", -] - -[[package]] -name = "cocoa" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" -dependencies = [ - "bitflags 1.3.2", - "block", - "cocoa-foundation", - "core-foundation", - "core-graphics", - "foreign-types 0.5.0", - "libc", - "objc", -] - -[[package]] -name = "cocoa-foundation" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "931d3837c286f56e3c58423ce4eba12d08db2374461a785c86f672b08b5650d6" -dependencies = [ - "bitflags 1.3.2", - "block", - "core-foundation", - "core-graphics-types", - "foreign-types 0.3.2", - "libc", - "objc", -] - -[[package]] -name = "color_quant" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" - -[[package]] -name = "combine" -version = "4.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" -dependencies = [ - "bytes", - "memchr", -] - -[[package]] -name = "concurrent-queue" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "const_format" -version = "0.2.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3a214c7af3d04997541b18d432afaff4c455e79e2029079647e72fc2bd27673" -dependencies = [ - "const_format_proc_macros", -] - -[[package]] -name = "const_format_proc_macros" -version = "0.2.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - -[[package]] -name = "constcat" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd7e35aee659887cbfb97aaf227ac12cad1a9d7c71e55ff3376839ed4e282d08" - -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - -[[package]] -name = "convert_case" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "core-foundation" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" - -[[package]] -name = "core-graphics" -version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "970a29baf4110c26fedbc7f82107d42c23f7e88e404c4577ed73fe99ff85a212" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "core-graphics-types", - "foreign-types 0.5.0", - "libc", -] - -[[package]] -name = "core-graphics-types" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bb142d41022986c1d8ff29103a1411c8a3dfad3552f87a4f8dc50d61d4f4e33" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "libc", -] - -[[package]] -name = "cpufeatures" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" -dependencies = [ - "libc", -] - -[[package]] -name = "crc32fast" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[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 = "cssparser" -version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a" -dependencies = [ - "cssparser-macros", - "dtoa-short", - "itoa 0.4.8", - "matches", - "phf 0.8.0", - "proc-macro2", - "quote", - "smallvec", - "syn 1.0.109", -] - -[[package]] -name = "cssparser-macros" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" -dependencies = [ - "quote", - "syn 2.0.52", -] - -[[package]] -name = "darling" -version = "0.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "darling_macro" -version = "0.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" -dependencies = [ - "darling_core", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "dashmap" -version = "5.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" -dependencies = [ - "cfg-if", - "hashbrown 0.14.0", - "lock_api", - "once_cell", - "parking_lot_core", -] - -[[package]] -name = "derive_more" -version = "0.99.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" -dependencies = [ - "convert_case 0.4.0", - "proc-macro2", - "quote", - "rustc_version", - "syn 1.0.109", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", -] - -[[package]] -name = "dioxus" -version = "0.5.0-alpha.0" -dependencies = [ - "dioxus-config-macro", - "dioxus-core", - "dioxus-core-macro", - "dioxus-desktop", - "dioxus-fullstack", - "dioxus-hooks", - "dioxus-hot-reload", - "dioxus-html", - "dioxus-mobile", - "dioxus-signals", -] - -[[package]] -name = "dioxus-cli-config" -version = "0.5.0-alpha.0" -dependencies = [ - "once_cell", - "serde", - "serde_json", - "tracing", -] - -[[package]] -name = "dioxus-config-macro" -version = "0.5.0-alpha.0" -dependencies = [ - "proc-macro2", - "quote", -] - -[[package]] -name = "dioxus-core" -version = "0.5.0-alpha.0" -dependencies = [ - "futures-channel", - "futures-util", - "longest-increasing-subsequence", - "rustc-hash", - "serde", - "slab", - "tracing", - "tracing-subscriber", -] - -[[package]] -name = "dioxus-core-macro" -version = "0.5.0-alpha.0" -dependencies = [ - "constcat", - "convert_case 0.6.0", - "dioxus-rsx", - "prettyplease", - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "dioxus-debug-cell" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ea539174bb236e0e7dc9c12b19b88eae3cb574dedbd0252a2d43ea7e6de13e2" - -[[package]] -name = "dioxus-desktop" -version = "0.5.0-alpha.0" -dependencies = [ - "async-trait", - "core-foundation", - "dioxus-cli-config", - "dioxus-core", - "dioxus-hooks", - "dioxus-hot-reload", - "dioxus-html", - "dioxus-interpreter-js", - "dunce", - "futures-channel", - "futures-util", - "generational-box", - "global-hotkey", - "infer", - "muda", - "objc", - "objc_id", - "rfd", - "rustc-hash", - "serde", - "serde_json", - "slab", - "tao", - "thiserror", - "tokio", - "tracing", - "urlencoding", - "webbrowser", - "wry 0.37.0", -] - -[[package]] -name = "dioxus-fullstack" -version = "0.5.0-alpha.0" -dependencies = [ - "async-trait", - "base64", - "bytes", - "ciborium", - "dioxus-hot-reload", - "dioxus-lib", - "dioxus-mobile", - "dioxus_server_macro", - "futures-util", - "once_cell", - "serde", - "serde_json", - "server_fn", - "tracing", -] - -[[package]] -name = "dioxus-hooks" -version = "0.5.0-alpha.0" -dependencies = [ - "dioxus-core", - "dioxus-debug-cell", - "dioxus-signals", - "futures-channel", - "futures-util", - "generational-box", - "slab", - "thiserror", - "tracing", -] - -[[package]] -name = "dioxus-hot-reload" -version = "0.5.0-alpha.0" -dependencies = [ - "dioxus-core", - "dioxus-html", - "dioxus-rsx", - "interprocess", - "serde", - "serde_json", -] - -[[package]] -name = "dioxus-html" -version = "0.5.0-alpha.0" -dependencies = [ - "async-trait", - "dioxus-core", - "dioxus-html-internal-macro", - "enumset", - "euclid", - "futures-channel", - "generational-box", - "keyboard-types", - "serde", - "serde-value", - "serde_json", - "serde_repr", - "tokio", - "web-sys", -] - -[[package]] -name = "dioxus-html-internal-macro" -version = "0.5.0-alpha.0" -dependencies = [ - "convert_case 0.6.0", - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "dioxus-interpreter-js" -version = "0.5.0-alpha.0" -dependencies = [ - "dioxus-core", - "dioxus-html", - "md5", - "sledgehammer_bindgen", - "sledgehammer_utils", -] - -[[package]] -name = "dioxus-lib" -version = "0.5.0-alpha.0" -dependencies = [ - "dioxus-core", - "dioxus-core-macro", - "dioxus-hooks", - "dioxus-html", - "dioxus-rsx", - "dioxus-signals", -] - -[[package]] -name = "dioxus-mobile" -version = "0.5.0-alpha.0" -dependencies = [ - "dioxus-desktop", -] - -[[package]] -name = "dioxus-rsx" -version = "0.5.0-alpha.0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", - "tracing", -] - -[[package]] -name = "dioxus-signals" -version = "0.5.0-alpha.0" -dependencies = [ - "dioxus-core", - "futures-channel", - "futures-util", - "generational-box", - "once_cell", - "parking_lot", - "rustc-hash", - "tracing", -] - -[[package]] -name = "dioxus_server_macro" -version = "0.5.0-alpha.0" -dependencies = [ - "convert_case 0.6.0", - "proc-macro2", - "quote", - "server_fn_macro", - "syn 2.0.52", -] - -[[package]] -name = "dispatch" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" - -[[package]] -name = "dlopen2" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1297103d2bbaea85724fcee6294c2d50b1081f9ad47d0f6f6f61eda65315a6" -dependencies = [ - "dlopen2_derive", - "libc", - "once_cell", - "winapi", -] - -[[package]] -name = "dlopen2_derive" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "dtoa" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" - -[[package]] -name = "dtoa-short" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbaceec3c6e4211c79e7b1800fb9680527106beb2f9c51904a3210c03a448c74" -dependencies = [ - "dtoa", -] - -[[package]] -name = "dunce" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" - -[[package]] -name = "enumset" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e875f1719c16de097dee81ed675e2d9bb63096823ed3f0ca827b7dea3028bbbb" -dependencies = [ - "enumset_derive", -] - -[[package]] -name = "enumset_derive" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08b6c6ab82d70f08844964ba10c7babb716de2ecaeab9be5717918a5177d3af" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "env_logger" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" -dependencies = [ - "log", - "regex", -] - -[[package]] -name = "env_logger" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", -] - -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - -[[package]] -name = "euclid" -version = "0.22.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f253bc5c813ca05792837a0ff4b3a580336b224512d48f7eda1d7dd9210787" -dependencies = [ - "num-traits", - "serde", -] - -[[package]] -name = "event-listener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - -[[package]] -name = "fastrand" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] - -[[package]] -name = "fdeflate" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10" -dependencies = [ - "simd-adler32", -] - -[[package]] -name = "field-offset" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" -dependencies = [ - "memoffset", - "rustc_version", -] - -[[package]] -name = "flate2" -version = "1.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "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.52", -] - -[[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.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "futf" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" -dependencies = [ - "mac", - "new_debug_unreachable", -] - -[[package]] -name = "futures" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" - -[[package]] -name = "futures-executor" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" - -[[package]] -name = "futures-lite" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" -dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", -] - -[[package]] -name = "futures-macro" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "futures-sink" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" - -[[package]] -name = "futures-task" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" - -[[package]] -name = "futures-util" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "fxhash" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" -dependencies = [ - "byteorder", -] - -[[package]] -name = "gdk" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5ba081bdef3b75ebcdbfc953699ed2d7417d6bd853347a42a37d76406a33646" -dependencies = [ - "cairo-rs", - "gdk-pixbuf", - "gdk-sys", - "gio", - "glib", - "libc", - "pango", -] - -[[package]] -name = "gdk-pixbuf" -version = "0.18.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" -dependencies = [ - "gdk-pixbuf-sys", - "gio", - "glib", - "libc", - "once_cell", -] - -[[package]] -name = "gdk-pixbuf-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" -dependencies = [ - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "gdk-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31ff856cb3386dae1703a920f803abafcc580e9b5f711ca62ed1620c25b51ff2" -dependencies = [ - "cairo-sys-rs", - "gdk-pixbuf-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "pango-sys", - "pkg-config", - "system-deps", -] - -[[package]] -name = "gdkwayland-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a90fbf5c033c65d93792192a49a8efb5bb1e640c419682a58bb96f5ae77f3d4a" -dependencies = [ - "gdk-sys", - "glib-sys", - "gobject-sys", - "libc", - "pkg-config", - "system-deps", -] - -[[package]] -name = "gdkx11" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2ea8a4909d530f79921290389cbd7c34cb9d623bfe970eaae65ca5f9cd9cce" -dependencies = [ - "gdk", - "gdkx11-sys", - "gio", - "glib", - "libc", - "x11", -] - -[[package]] -name = "gdkx11-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fee8f00f4ee46cad2939b8990f5c70c94ff882c3028f3cc5abf950fa4ab53043" -dependencies = [ - "gdk-sys", - "glib-sys", - "libc", - "system-deps", - "x11", -] - -[[package]] -name = "generational-box" -version = "0.5.0-alpha.0" -dependencies = [ - "parking_lot", -] - -[[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 = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - -[[package]] -name = "getrandom" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.11.0+wasi-snapshot-preview1", -] - -[[package]] -name = "gimli" -version = "0.27.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" - -[[package]] -name = "gio" -version = "0.18.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-util", - "gio-sys", - "glib", - "libc", - "once_cell", - "pin-project-lite", - "smallvec", - "thiserror", -] - -[[package]] -name = "gio-sys" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", - "winapi", -] - -[[package]] -name = "glib" -version = "0.18.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" -dependencies = [ - "bitflags 2.4.2", - "futures-channel", - "futures-core", - "futures-executor", - "futures-task", - "futures-util", - "gio-sys", - "glib-macros", - "glib-sys", - "gobject-sys", - "libc", - "memchr", - "once_cell", - "smallvec", - "thiserror", -] - -[[package]] -name = "glib-macros" -version = "0.18.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" -dependencies = [ - "heck", - "proc-macro-crate 2.0.2", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "glib-sys" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" -dependencies = [ - "libc", - "system-deps", -] - -[[package]] -name = "global-hotkey" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0d37e95d3937251ee2019709389bb793c1237f16d45fc0fe7b2464b5f97c68" -dependencies = [ - "crossbeam-channel", - "keyboard-types", - "once_cell", - "thiserror", - "windows-sys 0.52.0", - "x11-dl", -] - -[[package]] -name = "gloo-net" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43aaa242d1239a8822c15c645f02166398da4f8b5c4bae795c1f5b44e9eee173" -dependencies = [ - "futures-channel", - "futures-core", - "futures-sink", - "gloo-utils", - "http 0.2.9", - "js-sys", - "pin-project", - "serde", - "serde_json", - "thiserror", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "gloo-utils" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" -dependencies = [ - "js-sys", - "serde", - "serde_json", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gobject-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" -dependencies = [ - "glib-sys", - "libc", - "system-deps", -] - -[[package]] -name = "gtk" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93c4f5e0e20b60e10631a5f06da7fe3dda744b05ad0ea71fee2f47adf865890c" -dependencies = [ - "atk", - "cairo-rs", - "field-offset", - "futures-channel", - "gdk", - "gdk-pixbuf", - "gio", - "glib", - "gtk-sys", - "gtk3-macros", - "libc", - "pango", - "pkg-config", -] - -[[package]] -name = "gtk-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771437bf1de2c1c0b496c11505bdf748e26066bbe942dfc8f614c9460f6d7722" -dependencies = [ - "atk-sys", - "cairo-sys-rs", - "gdk-pixbuf-sys", - "gdk-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "pango-sys", - "system-deps", -] - -[[package]] -name = "gtk3-macros" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6063efb63db582968fb7df72e1ae68aa6360dcfb0a75143f34fc7d616bad75e" -dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "half" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5eceaaeec696539ddaf7b333340f1af35a5aa87ae3e4f3ead0532f72affab2e" -dependencies = [ - "cfg-if", - "crunchy", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hashbrown" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" -dependencies = [ - "ahash", - "allocator-api2", -] - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" - -[[package]] -name = "home" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" -dependencies = [ - "windows-sys 0.48.0", -] - -[[package]] -name = "html5ever" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" -dependencies = [ - "log", - "mac", - "markup5ever", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "http" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" -dependencies = [ - "bytes", - "fnv", - "itoa 1.0.9", -] - -[[package]] -name = "http" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" -dependencies = [ - "bytes", - "fnv", - "itoa 1.0.9", -] - -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "idna" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "image" -version = "0.24.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "527909aa81e20ac3a44803521443a765550f09b5130c2c2fa1ea59c2f8f50a3a" -dependencies = [ - "bytemuck", - "byteorder", - "color_quant", - "num-rational", - "num-traits", -] - -[[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", -] - -[[package]] -name = "indexmap" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" -dependencies = [ - "equivalent", - "hashbrown 0.14.0", -] - -[[package]] -name = "infer" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6c16b11a665b26aeeb9b1d7f954cdeb034be38dd00adab4f2ae921a8fee804" -dependencies = [ - "cfb", -] - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "interprocess" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81f2533f3be42fffe3b5e63b71aeca416c1c3bc33e4e27be018521e76b1f38fb" -dependencies = [ - "blocking", - "cfg-if", - "futures-core", - "futures-io", - "intmap", - "libc", - "once_cell", - "rustc_version", - "spinning", - "thiserror", - "to_method", - "winapi", -] - -[[package]] -name = "intmap" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae52f28f45ac2bc96edb7714de995cffc174a395fb0abf5bff453587c980d7b9" - -[[package]] -name = "itoa" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" - -[[package]] -name = "itoa" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" - -[[package]] -name = "javascriptcore-rs" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca5671e9ffce8ffba57afc24070e906da7fc4b1ba66f2cabebf61bf2ea257fcc" -dependencies = [ - "bitflags 1.3.2", - "glib", - "javascriptcore-rs-sys", -] - -[[package]] -name = "javascriptcore-rs-sys" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1be78d14ffa4b75b66df31840478fef72b51f8c2465d4ca7c194da9f7a5124" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "jni" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec" -dependencies = [ - "cesu8", - "combine", - "jni-sys", - "log", - "thiserror", - "walkdir", -] - -[[package]] -name = "jni" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" -dependencies = [ - "cesu8", - "cfg-if", - "combine", - "jni-sys", - "log", - "thiserror", - "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 = "js-sys" -version = "0.3.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "keyboard-types" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" -dependencies = [ - "bitflags 2.4.2", - "serde", - "unicode-segmentation", -] - -[[package]] -name = "kuchikiki" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e4755b7b995046f510a7520c42b2fed58b77bd94d5a87a8eb43d2fd126da8" -dependencies = [ - "cssparser", - "html5ever", - "indexmap 1.9.3", - "matches", - "selectors", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "libc" -version = "0.2.147" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" - -[[package]] -name = "libxdo" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00333b8756a3d28e78def82067a377de7fa61b24909000aeaa2b446a948d14db" -dependencies = [ - "libxdo-sys", -] - -[[package]] -name = "libxdo-sys" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db23b9e7e2b7831bbd8aac0bbeeeb7b68cbebc162b227e7052e8e55829a09212" -dependencies = [ - "libc", - "x11", -] - -[[package]] -name = "lock_api" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" - -[[package]] -name = "longest-increasing-subsequence" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3bd0dd2cd90571056fdb71f6275fada10131182f84899f4b2a916e565d81d86" - -[[package]] -name = "lru" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" -dependencies = [ - "hashbrown 0.14.0", -] - -[[package]] -name = "mac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" - -[[package]] -name = "malloc_buf" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" -dependencies = [ - "libc", -] - -[[package]] -name = "markup5ever" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" -dependencies = [ - "log", - "phf 0.10.1", - "phf_codegen 0.10.0", - "string_cache", - "string_cache_codegen", - "tendril", -] - -[[package]] -name = "matches" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" - -[[package]] -name = "md5" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "memoffset" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" -dependencies = [ - "autocfg", -] - -[[package]] -name = "miniz_oxide" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" -dependencies = [ - "adler", - "simd-adler32", -] - -[[package]] -name = "mobile-demo" -version = "0.1.0" -dependencies = [ - "android_logger", - "anyhow", - "core-foundation", - "dioxus", - "env_logger 0.9.3", - "jni 0.19.0", - "log", - "paste", - "wry 0.35.2", -] - -[[package]] -name = "muda" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c47e7625990fc1af2226ea4f34fb2412b03c12639fcb91868581eb3a6893453" -dependencies = [ - "cocoa", - "crossbeam-channel", - "gtk", - "keyboard-types", - "libxdo", - "objc", - "once_cell", - "png", - "thiserror", - "windows-sys 0.52.0", -] - -[[package]] -name = "ndk" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" -dependencies = [ - "bitflags 1.3.2", - "jni-sys", - "ndk-sys", - "num_enum", - "raw-window-handle 0.5.2", - "thiserror", -] - -[[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.4.1+23.1.7779620" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cf2aae958bd232cac5069850591667ad422d263686d75b52a065f9badeee5a3" -dependencies = [ - "jni-sys", -] - -[[package]] -name = "new_debug_unreachable" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" - -[[package]] -name = "nodrop" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" - -[[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "winapi", -] - -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" -dependencies = [ - "autocfg", -] - -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi 0.3.2", - "libc", -] - -[[package]] -name = "num_enum" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" -dependencies = [ - "num_enum_derive", -] - -[[package]] -name = "num_enum_derive" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" -dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "objc" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" -dependencies = [ - "malloc_buf", - "objc_exception", -] - -[[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_exception" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" -dependencies = [ - "cc", -] - -[[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.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" - -[[package]] -name = "ordered-float" -version = "2.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7940cf2ca942593318d07fcf2596cdca60a85c9e7fab408a5e21a4f9dcd40d87" -dependencies = [ - "num-traits", -] - -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - -[[package]] -name = "pango" -version = "0.18.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" -dependencies = [ - "gio", - "glib", - "libc", - "once_cell", - "pango-sys", -] - -[[package]] -name = "pango-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "parking" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e" - -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets 0.48.1", -] - -[[package]] -name = "paste" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" - -[[package]] -name = "percent-encoding" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" - -[[package]] -name = "phf" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" -dependencies = [ - "phf_macros", - "phf_shared 0.8.0", - "proc-macro-hack", -] - -[[package]] -name = "phf" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" -dependencies = [ - "phf_shared 0.10.0", -] - -[[package]] -name = "phf_codegen" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" -dependencies = [ - "phf_generator 0.8.0", - "phf_shared 0.8.0", -] - -[[package]] -name = "phf_codegen" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" -dependencies = [ - "phf_generator 0.10.0", - "phf_shared 0.10.0", -] - -[[package]] -name = "phf_generator" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" -dependencies = [ - "phf_shared 0.8.0", - "rand 0.7.3", -] - -[[package]] -name = "phf_generator" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" -dependencies = [ - "phf_shared 0.10.0", - "rand 0.8.5", -] - -[[package]] -name = "phf_macros" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" -dependencies = [ - "phf_generator 0.8.0", - "phf_shared 0.8.0", - "proc-macro-hack", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "phf_shared" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" -dependencies = [ - "siphasher", -] - -[[package]] -name = "phf_shared" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" -dependencies = [ - "siphasher", -] - -[[package]] -name = "pin-project" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkg-config" -version = "0.3.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" - -[[package]] -name = "png" -version = "0.17.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59871cc5b6cce7eaccca5a802b4173377a1c2ba90654246789a8fa2334426d11" -dependencies = [ - "bitflags 1.3.2", - "crc32fast", - "fdeflate", - "flate2", - "miniz_oxide", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "precomputed-hash" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" - -[[package]] -name = "prettyplease" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" -dependencies = [ - "proc-macro2", - "syn 2.0.52", -] - -[[package]] -name = "proc-macro-crate" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" -dependencies = [ - "once_cell", - "toml_edit 0.19.14", -] - -[[package]] -name = "proc-macro-crate" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" -dependencies = [ - "toml_datetime", - "toml_edit 0.20.2", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro-hack" -version = "0.5.20+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" - -[[package]] -name = "proc-macro2" -version = "1.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", - "rand_pcg", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", -] - -[[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 0.6.4", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.10", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "rand_pcg" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "raw-window-handle" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" - -[[package]] -name = "raw-window-handle" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42a9830a0e1b9fb145ebb365b8bc4ccd75f290f98c0247deafbbe2c75cefb544" - -[[package]] -name = "redox_syscall" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "regex" -version = "1.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7b6d6190b7594385f61bd3911cd1be99dfddcfc365a4160cc2ab5bff4aed294" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" - -[[package]] -name = "rfd" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c9e7b57df6e8472152674607f6cc68aa14a748a3157a857a94f516e11aeacc2" -dependencies = [ - "block", - "dispatch", - "glib-sys", - "gobject-sys", - "gtk-sys", - "js-sys", - "log", - "objc", - "objc-foundation", - "objc_id", - "raw-window-handle 0.5.2", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "windows-sys 0.48.0", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" - -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - -[[package]] -name = "rustc_version" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver", -] - -[[package]] -name = "ryu" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" - -[[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 = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "selectors" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe" -dependencies = [ - "bitflags 1.3.2", - "cssparser", - "derive_more", - "fxhash", - "log", - "matches", - "phf 0.8.0", - "phf_codegen 0.8.0", - "precomputed-hash", - "servo_arc", - "smallvec", - "thin-slice", -] - -[[package]] -name = "semver" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" - -[[package]] -name = "send_wrapper" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" -dependencies = [ - "futures-core", -] - -[[package]] -name = "serde" -version = "1.0.180" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ea67f183f058fe88a4e3ec6e2788e003840893b91bac4559cabedd00863b3ed" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde-value" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" -dependencies = [ - "ordered-float", - "serde", -] - -[[package]] -name = "serde_derive" -version = "1.0.180" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24e744d7782b686ab3b73267ef05697159cc0e5abbed3f47f9933165e5219036" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "serde_json" -version = "1.0.104" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" -dependencies = [ - "itoa 1.0.9", - "ryu", - "serde", -] - -[[package]] -name = "serde_qs" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0431a35568651e363364210c91983c1da5eb29404d9f0928b67d4ebcfa7d330c" -dependencies = [ - "percent-encoding", - "serde", - "thiserror", -] - -[[package]] -name = "serde_repr" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "serde_spanned" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" -dependencies = [ - "serde", -] - -[[package]] -name = "server_fn" -version = "0.6.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2955da1dc5fcd970c182ebf1089af6c5f19051e1f286a21f7b96490a49b7a531" -dependencies = [ - "bytes", - "const_format", - "dashmap", - "futures", - "gloo-net", - "http 1.1.0", - "js-sys", - "once_cell", - "send_wrapper", - "serde", - "serde_json", - "serde_qs", - "server_fn_macro_default", - "thiserror", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-streams", - "web-sys", - "xxhash-rust", -] - -[[package]] -name = "server_fn_macro" -version = "0.6.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adfdd051ef905fdb3da20942b0c52d536158d7489a724e14cc2fd47323e7ca91" -dependencies = [ - "const_format", - "convert_case 0.6.0", - "proc-macro2", - "quote", - "syn 2.0.52", - "xxhash-rust", -] - -[[package]] -name = "server_fn_macro_default" -version = "0.6.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "060af1def72353a779fcc184c53e1965d3055a38b9e827f2259b2bff2d9c371e" -dependencies = [ - "server_fn_macro", - "syn 2.0.52", -] - -[[package]] -name = "servo_arc" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432" -dependencies = [ - "nodrop", - "stable_deref_trait", -] - -[[package]] -name = "sha2" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "simd-adler32" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" - -[[package]] -name = "siphasher" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" - -[[package]] -name = "slab" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" -dependencies = [ - "autocfg", -] - -[[package]] -name = "sledgehammer_bindgen" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcfaf791ff02f48f3518ce825d32cf419c13a43c1d8b1232f74ac89f339c46d2" -dependencies = [ - "sledgehammer_bindgen_macro", -] - -[[package]] -name = "sledgehammer_bindgen_macro" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdd941cc539bd3dc694edaf9d0c4e1221d02baa67c6b45ec04fad1024d9e8139" -dependencies = [ - "quote", - "syn 2.0.52", -] - -[[package]] -name = "sledgehammer_utils" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f20798defa0e9d4eff9ca451c7f84774c7378a9c3b5a40112cfa2b3eadb97ae2" -dependencies = [ - "lru", - "once_cell", - "rustc-hash", -] - -[[package]] -name = "smallvec" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" - -[[package]] -name = "soup3" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "471f924a40f31251afc77450e781cb26d55c0b650842efafc9c6cbd2f7cc4f9f" -dependencies = [ - "futures-channel", - "gio", - "glib", - "libc", - "soup3-sys", -] - -[[package]] -name = "soup3-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ebe8950a680a12f24f15ebe1bf70db7af98ad242d9db43596ad3108aab86c27" -dependencies = [ - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "spinning" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d4f0e86297cad2658d92a707320d87bf4e6ae1050287f51d19b67ef3f153a7b" -dependencies = [ - "lock_api", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - -[[package]] -name = "string_cache" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" -dependencies = [ - "new_debug_unreachable", - "once_cell", - "parking_lot", - "phf_shared 0.10.0", - "precomputed-hash", - "serde", -] - -[[package]] -name = "string_cache_codegen" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" -dependencies = [ - "phf_generator 0.10.0", - "phf_shared 0.10.0", - "proc-macro2", - "quote", -] - -[[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.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "system-deps" -version = "6.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30c2de8a4d8f4b823d634affc9cd2a74ec98c53a756f317e529a48046cbf71f3" -dependencies = [ - "cfg-expr", - "heck", - "pkg-config", - "toml", - "version-compare", -] - -[[package]] -name = "tao" -version = "0.26.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccba570365293ca309d60f30fdac2c5271b732dc762e6154e59c85d2c762a0a1" -dependencies = [ - "bitflags 1.3.2", - "cocoa", - "core-foundation", - "core-graphics", - "crossbeam-channel", - "dispatch", - "dlopen2", - "gdkwayland-sys", - "gdkx11-sys", - "gtk", - "image", - "instant", - "jni 0.21.1", - "lazy_static", - "libc", - "log", - "ndk", - "ndk-context", - "ndk-sys", - "objc", - "once_cell", - "parking_lot", - "png", - "raw-window-handle 0.5.2", - "raw-window-handle 0.6.0", - "scopeguard", - "tao-macros", - "unicode-segmentation", - "url", - "windows", - "windows-implement", - "windows-version", - "x11-dl", -] - -[[package]] -name = "tao-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b27a4bcc5eb524658234589bdffc7e7bfb996dbae6ce9393bfd39cb4159b445" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "target-lexicon" -version = "0.12.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a" - -[[package]] -name = "tendril" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" -dependencies = [ - "futf", - "mac", - "utf-8", -] - -[[package]] -name = "termcolor" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "thin-slice" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" - -[[package]] -name = "thiserror" -version = "1.0.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "thread_local" -version = "1.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" -dependencies = [ - "cfg-if", - "once_cell", -] - -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "to_method" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7c4ceeeca15c8384bbc3e011dbd8fccb7f068a440b752b7d9b32ceb0ca0e2e8" - -[[package]] -name = "tokio" -version = "1.29.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" -dependencies = [ - "autocfg", - "backtrace", - "bytes", - "num_cpus", - "pin-project-lite", - "tokio-macros", -] - -[[package]] -name = "tokio-macros" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "toml" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit 0.19.14", -] - -[[package]] -name = "toml_datetime" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_edit" -version = "0.19.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" -dependencies = [ - "indexmap 2.0.0", - "serde", - "serde_spanned", - "toml_datetime", - "winnow", -] - -[[package]] -name = "toml_edit" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" -dependencies = [ - "indexmap 2.0.0", - "toml_datetime", - "winnow", -] - -[[package]] -name = "tracing" -version = "0.1.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" -dependencies = [ - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "tracing-core" -version = "0.1.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" -dependencies = [ - "nu-ansi-term", - "sharded-slab", - "smallvec", - "thread_local", - "tracing-core", - "tracing-log", -] - -[[package]] -name = "typenum" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" - -[[package]] -name = "unicode-bidi" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" - -[[package]] -name = "unicode-ident" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" - -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-segmentation" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" - -[[package]] -name = "unicode-xid" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" - -[[package]] -name = "url" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", -] - -[[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 = "uuid" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" - -[[package]] -name = "valuable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - -[[package]] -name = "version-compare" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "waker-fn" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" - -[[package]] -name = "walkdir" -version = "2.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - -[[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.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn 2.0.52", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" - -[[package]] -name = "wasm-streams" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" -dependencies = [ - "futures-util", - "js-sys", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "web-sys" -version = "0.3.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webbrowser" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd222aa310eb7532e3fd427a5d7db7e44bc0b0cf1c1e21139c345325511a85b6" -dependencies = [ - "core-foundation", - "home", - "jni 0.21.1", - "log", - "ndk-context", - "objc", - "raw-window-handle 0.5.2", - "url", - "web-sys", -] - -[[package]] -name = "webkit2gtk" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76b1bc1e54c581da1e9f179d0b38512ba358fb1af2d634a1affe42e37172361a" -dependencies = [ - "bitflags 1.3.2", - "cairo-rs", - "gdk", - "gdk-sys", - "gio", - "gio-sys", - "glib", - "glib-sys", - "gobject-sys", - "gtk", - "gtk-sys", - "javascriptcore-rs", - "libc", - "once_cell", - "soup3", - "webkit2gtk-sys", -] - -[[package]] -name = "webkit2gtk-sys" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62daa38afc514d1f8f12b8693d30d5993ff77ced33ce30cd04deebc267a6d57c" -dependencies = [ - "bitflags 1.3.2", - "cairo-sys-rs", - "gdk-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "gtk-sys", - "javascriptcore-rs-sys", - "libc", - "pkg-config", - "soup3-sys", - "system-deps", -] - -[[package]] -name = "webview2-com" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0ae9c7e420783826cf769d2c06ac9ba462f450eca5893bb8c6c6529a4e5dd33" -dependencies = [ - "webview2-com-macros", - "webview2-com-sys", - "windows", - "windows-core", - "windows-implement", - "windows-interface", -] - -[[package]] -name = "webview2-com-macros" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1345798ecd8122468840bcdf1b95e5dc6d2206c5e4b0eafa078d061f59c9bc" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "webview2-com-sys" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6ad85fceee6c42fa3d61239eba5a11401bf38407a849ed5ea1b407df08cca72" -dependencies = [ - "thiserror", - "windows", - "windows-core", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[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.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" -dependencies = [ - "windows-core", - "windows-implement", - "windows-interface", - "windows-targets 0.52.4", -] - -[[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.4", -] - -[[package]] -name = "windows-implement" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12168c33176773b86799be25e2a2ba07c7aab9968b37541f1094dbd7a60c8946" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "windows-interface" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d8dc32e0095a7eeccebd0e3f09e9509365ecb3fc6ac4d6f5f14a3f6392942d1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[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.1", -] - -[[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.4", -] - -[[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.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" -dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", -] - -[[package]] -name = "windows-targets" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" -dependencies = [ - "windows_aarch64_gnullvm 0.52.4", - "windows_aarch64_msvc 0.52.4", - "windows_i686_gnu 0.52.4", - "windows_i686_msvc 0.52.4", - "windows_x86_64_gnu 0.52.4", - "windows_x86_64_gnullvm 0.52.4", - "windows_x86_64_msvc 0.52.4", -] - -[[package]] -name = "windows-version" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75aa004c988e080ad34aff5739c39d0312f4684699d6d71fc8a198d057b8b9b4" -dependencies = [ - "windows-targets 0.52.4", -] - -[[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.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" - -[[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.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" - -[[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.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" - -[[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.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" - -[[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.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" - -[[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.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" - -[[package]] -name = "winnow" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bd122eb777186e60c3fdf765a58ac76e41c582f1f535fbf3314434c6b58f3f7" -dependencies = [ - "memchr", -] - -[[package]] -name = "wry" -version = "0.35.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3016c47c9b6f7029a9da7cd48af8352327226bba0e955f3c92e2966651365a9" -dependencies = [ - "base64", - "block", - "cfg_aliases", - "cocoa", - "core-graphics", - "crossbeam-channel", - "dunce", - "gdkx11", - "gtk", - "html5ever", - "http 0.2.9", - "javascriptcore-rs", - "jni 0.21.1", - "kuchikiki", - "libc", - "log", - "ndk", - "ndk-context", - "ndk-sys", - "objc", - "objc_id", - "once_cell", - "raw-window-handle 0.5.2", - "serde", - "serde_json", - "sha2", - "soup3", - "tao-macros", - "thiserror", - "url", - "webkit2gtk", - "webkit2gtk-sys", - "webview2-com", - "windows", - "windows-implement", - "windows-version", - "x11-dl", -] - -[[package]] -name = "wry" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b717040ba9771fd88eb428c6ea6b555f8e734ff8534f02c13e8f10d97f5935e" -dependencies = [ - "base64", - "block", - "cfg_aliases", - "cocoa", - "core-graphics", - "crossbeam-channel", - "dunce", - "gdkx11", - "gtk", - "html5ever", - "http 0.2.9", - "javascriptcore-rs", - "jni 0.21.1", - "kuchikiki", - "libc", - "log", - "ndk", - "ndk-context", - "ndk-sys", - "objc", - "objc_id", - "once_cell", - "percent-encoding", - "raw-window-handle 0.6.0", - "serde", - "serde_json", - "sha2", - "soup3", - "tao-macros", - "thiserror", - "webkit2gtk", - "webkit2gtk-sys", - "webview2-com", - "windows", - "windows-implement", - "windows-version", - "x11-dl", -] - -[[package]] -name = "x11" -version = "2.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" -dependencies = [ - "libc", - "pkg-config", -] - -[[package]] -name = "x11-dl" -version = "2.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" -dependencies = [ - "libc", - "once_cell", - "pkg-config", -] - -[[package]] -name = "xxhash-rust" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927da81e25be1e1a2901d59b81b37dd2efd1fc9c9345a55007f09bf5a2d3ee03" - -[[package]] -name = "zerocopy" -version = "0.7.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", -] diff --git a/examples/mobile_demo/Cargo.toml b/examples/mobile_demo/Cargo.toml deleted file mode 100644 index 4d4155750b..0000000000 --- a/examples/mobile_demo/Cargo.toml +++ /dev/null @@ -1,51 +0,0 @@ -[package] -name = "mobile-demo" -version = "0.1.0" -authors = ["Jonathan Kelley "] -edition = "2021" - -[lib] -crate-type = ["staticlib", "cdylib", "rlib"] - -[[bin]] -name = "mobile-demo-desktop" -path = "gen/bin/desktop.rs" - -[package.metadata.cargo-android] -app-activity-name = "com.example.mobile_demo.MainActivity" -app-dependencies = [ - "androidx.webkit:webkit:1.6.1", - "androidx.appcompat:appcompat:1.6.1", - "com.google.android.material:material:1.8.0", -] -project-dependencies = ["org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21"] -app-plugins = ["org.jetbrains.kotlin.android"] -app-permissions = ["android.permission.INTERNET"] -app-theme-parent = "Theme.MaterialComponents.DayNight.DarkActionBar" -vulkan-validation = false - -[package.metadata.cargo-android.env-vars] -WRY_ANDROID_PACKAGE = "com.example.mobile_demo" -WRY_ANDROID_LIBRARY = "mobile_demo" -WRY_ANDROID_KOTLIN_FILES_OUT_DIR = "/app/src/main/kotlin/com/example/mobile_demo" - -[package.metadata.cargo-apple.ios] -frameworks = ["WebKit"] - -[dependencies] -anyhow = "1.0.56" -log = "0.4.11" -wry = "0.35.0" -dioxus = { path = "../../packages/dioxus", features = ["mobile"]} - - -[target.'cfg(target_os = "android")'.dependencies] -android_logger = "0.9.0" -jni = "0.19.0" -paste = "1.0" - -[target.'cfg(not(target_os = "android"))'.dependencies] -env_logger = "0.9.0" - -[target.'cfg(target_os = "ios")'.dependencies] -core-foundation = "0.9.3" diff --git a/examples/mobile_demo/README.md b/examples/mobile_demo/README.md deleted file mode 100644 index 4d5ea46ed0..0000000000 --- a/examples/mobile_demo/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Dioxus Mobile demo - -## How this project was generated - -Right now, Dioxus supports mobile targets including iOS and Android. However, our tooling is not mature enough to include the build commands directly. - -This project was generated using [cargo-mobile2](https://github.com/tauri-apps/cargo-mobile2). We have yet to integrate this generation into the Dioxus-CLI. The open issue for this is [#1157](https://github.com/DioxusLabs/dioxus/issues/1157). - -## Running this project - -Because the tooling and ecosystem is still young, Dioxus mobile can be difficult to setup and run. We have [detailed guides](https://dioxuslabs.com/learn/0.5/getting_started) for creating, building, and running on both iOS and Android. diff --git a/examples/mobile_demo/mobile.toml b/examples/mobile_demo/mobile.toml deleted file mode 100644 index 3b87772508..0000000000 --- a/examples/mobile_demo/mobile.toml +++ /dev/null @@ -1,8 +0,0 @@ -[app] -name = "mobile-demo" -stylized-name = "Mobile Demo" -domain = "example.com" -template-pack = "wry" - -[apple] -development-team = "34U4FG9TJ8" diff --git a/examples/mobile_demo/src/index.html b/examples/mobile_demo/src/index.html deleted file mode 100644 index b0e6290e12..0000000000 --- a/examples/mobile_demo/src/index.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - Dioxus app - - - - -
- - - diff --git a/examples/mobile_demo/src/lib.rs b/examples/mobile_demo/src/lib.rs deleted file mode 100644 index 6fc142d9a3..0000000000 --- a/examples/mobile_demo/src/lib.rs +++ /dev/null @@ -1,90 +0,0 @@ -use anyhow::Result; -use dioxus::mobile::Config; -use dioxus::prelude::*; - -#[cfg(target_os = "android")] -use dioxus::mobile::wry::android_binding; - -#[cfg(target_os = "android")] -fn init_logging() { - android_logger::init_once( - android_logger::Config::default() - .with_min_level(log::Level::Trace) - .with_tag("mobile-demo"), - ); -} - -#[cfg(not(target_os = "android"))] -fn init_logging() { - env_logger::init(); -} - -#[cfg(any(target_os = "android", target_os = "ios"))] -fn stop_unwind T, T>(f: F) -> T { - match std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)) { - Ok(t) => t, - Err(err) => { - eprintln!("attempt to unwind out of `rust` with err: {:?}", err); - std::process::abort() - } - } -} - -#[cfg(any(target_os = "android", target_os = "ios"))] -fn _start_app() { - stop_unwind(|| main().unwrap()); -} - -#[no_mangle] -#[inline(never)] -#[cfg(any(target_os = "android", target_os = "ios"))] -pub extern "C" fn start_app() { - #[cfg(target_os = "android")] - android_binding!(com_example, mobile_demo, _start_app); - #[cfg(target_os = "ios")] - _start_app() -} - -pub fn main() -> Result<()> { - init_logging(); - - // Right now we're going through dioxus-desktop but we'd like to go through dioxus-mobile - // That will seed the index.html with some fixes that prevent the page from scrolling/zooming etc - LaunchBuilder::mobile() - .with_cfg( - // Note that we have to disable the viewport goofiness of the browser. - // Dioxus_mobile should do this for us - Config::default().with_custom_index(include_str!("index.html").to_string()), - ) - .launch(app); - - Ok(()) -} - -fn app() -> Element { - let mut items = use_signal(|| vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); - - log::debug!("Hello from the app"); - - rsx! { - div { - h1 { "Hello, Mobile" } - div { - margin_left: "auto", - margin_right: "auto", - width: "200px", - padding: "10px", - border: "1px solid black", - button { - onclick: move |_| { - items.push(items.len()); - }, - "Add item" - } - for item in items.iter() { - div { "- {item}" } - } - } - } - } -} diff --git a/packages/cli/Cargo.toml b/packages/cli/Cargo.toml index a89fe71025..0028179701 100644 --- a/packages/cli/Cargo.toml +++ b/packages/cli/Cargo.toml @@ -94,7 +94,7 @@ dioxus-core = { workspace = true, features = ["serialize"] } dioxus-core-types = { workspace = true } dioxus-hot-reload = { workspace = true, features = ["serve"] } ignore = "0.4.22" -env_logger = "0.11.3" +env_logger = { workspace = true } tracing-subscriber = { version = "0.3.18", features = ["std", "env-filter"] } console-subscriber = { version = "0.3.0", optional = true } diff --git a/packages/dioxus-lib/Cargo.toml b/packages/dioxus-lib/Cargo.toml index 86144a9bfe..7c356b2b64 100644 --- a/packages/dioxus-lib/Cargo.toml +++ b/packages/dioxus-lib/Cargo.toml @@ -18,6 +18,7 @@ dioxus-config-macro = { workspace = true, optional = true } dioxus-hooks = { workspace = true, optional = true } dioxus-rsx = { workspace = true, optional = true } dioxus-signals = { workspace = true, optional = true } +manganis = { workspace = true, optional = true } [dev-dependencies] dioxus = { workspace = true } diff --git a/packages/dioxus/Cargo.toml b/packages/dioxus/Cargo.toml index e461f25172..074c8dc28d 100644 --- a/packages/dioxus/Cargo.toml +++ b/packages/dioxus/Cargo.toml @@ -28,22 +28,33 @@ dioxus-static-site-generation = { workspace = true, optional = true } dioxus-liveview = { workspace = true, optional = true } dioxus-ssr = { workspace = true, optional = true } manganis = { workspace = true, optional = true } - +dioxus-hot-reload = { workspace = true, optional = true } serde = { version = "1.0.136", optional = true } axum = { workspace = true, optional = true } -[target.'cfg(not(any(target_arch = "wasm32", target_os = "ios", target_os = "android")))'.dependencies] -dioxus-hot-reload = { workspace = true, optional = true } - [features] -default = ["macro", "html", "hot-reload", "signals", "hooks", "launch", "mounted", "document", "asset"] -# default = ["macro", "html", "hot-reload", "signals", "hooks", "launch", "mounted", "file_engine", "document", "asset"] +default = [ + "macro", + "html", + "hot-reload", + "signals", + "hooks", + "launch", + "mounted", + "document", + "asset", +] minimal = ["macro", "html", "signals", "hooks", "launch"] +# default = ["macro", "html", "hot-reload", "signals", "hooks", "launch", "mounted", "file_engine", "document", "asset"] signals = ["dep:dioxus-signals"] macro = ["dep:dioxus-core-macro"] html = ["dep:dioxus-html"] hooks = ["dep:dioxus-hooks"] -hot-reload = ["dep:dioxus-hot-reload", "dioxus-web?/hot_reload", "dioxus-fullstack?/hot-reload"] +hot-reload = [ + "dep:dioxus-hot-reload", + "dioxus-web?/hot_reload", + "dioxus-fullstack?/hot-reload", +] mounted = ["dioxus-web?/mounted", "dioxus-html?/mounted"] file_engine = ["dioxus-web?/file_engine"] asset = ["dep:manganis"] @@ -53,18 +64,48 @@ launch = ["dep:dioxus-config-macro"] router = ["dep:dioxus-router"] # Platforms -fullstack = ["dep:dioxus-fullstack", "dioxus-config-macro/fullstack", "dep:serde", "dioxus-router?/fullstack"] -desktop = ["dep:dioxus-desktop", "dioxus-fullstack?/desktop", "dioxus-config-macro/desktop"] -mobile = ["dep:dioxus-mobile", "dioxus-fullstack?/mobile", "dioxus-config-macro/mobile"] -web = ["dep:dioxus-web", "dioxus-fullstack?/web", "dioxus-static-site-generation?/web", "dioxus-config-macro/web", "dioxus-router?/web"] +fullstack = [ + "dep:dioxus-fullstack", + "dioxus-config-macro/fullstack", + "dep:serde", + "dioxus-router?/fullstack", +] +desktop = [ + "dep:dioxus-desktop", + "dioxus-fullstack?/desktop", + "dioxus-config-macro/desktop", +] +mobile = [ + "dep:dioxus-mobile", + "dioxus-fullstack?/mobile", + "dioxus-config-macro/mobile", +] +web = [ + "dep:dioxus-web", + "dioxus-fullstack?/web", + "dioxus-static-site-generation?/web", + "dioxus-config-macro/web", + "dioxus-router?/web", +] ssr = ["dep:dioxus-ssr", "dioxus-router?/ssr", "dioxus-config-macro/ssr"] -liveview = ["dep:dioxus-liveview", "dioxus-config-macro/liveview", "dioxus-router?/liveview"] -static-generation = ["dep:dioxus-static-site-generation", "dioxus-config-macro/static-generation"] +liveview = [ + "dep:dioxus-liveview", + "dioxus-config-macro/liveview", + "dioxus-router?/liveview", +] +static-generation = [ + "dep:dioxus-static-site-generation", + "dioxus-config-macro/static-generation", +] axum = ["server"] -server = ["dioxus-fullstack?/axum", "dioxus-fullstack?/server", "dioxus-static-site-generation?/server", "ssr", "dioxus-liveview?/axum", "dep:axum"] - -# This feature just disables the no-renderer-enabled warning -third-party-renderer = [] +server = [ + "dioxus-fullstack?/axum", + "dioxus-fullstack?/server", + "dioxus-static-site-generation?/server", + "ssr", + "dioxus-liveview?/axum", + "dep:axum", +] [dev-dependencies] futures-util = { workspace = true } @@ -72,7 +113,7 @@ tracing = { workspace = true } rand = { version = "0.8.4", features = ["small_rng"] } criterion = { workspace = true } thiserror = { workspace = true } -env_logger = "0.10.0" +env_logger = { workspace = true } tokio = { workspace = true, features = ["full"] } dioxus = { workspace = true } @@ -92,5 +133,5 @@ features = [ "html", "liveview", "static-generation", - "server" + "server", ] diff --git a/packages/dioxus/src/lib.rs b/packages/dioxus/src/lib.rs index 0532530704..f745b0c01f 100644 --- a/packages/dioxus/src/lib.rs +++ b/packages/dioxus/src/lib.rs @@ -32,6 +32,12 @@ pub use dioxus_core::{CapturedError, Ok, Result}; #[cfg_attr(docsrs, doc(cfg(feature = "launch")))] mod launch; +pub mod events { + #[cfg(feature = "html")] + #[cfg_attr(docsrs, doc(cfg(feature = "html")))] + pub use dioxus_html::prelude::*; +} + #[cfg(feature = "launch")] #[cfg_attr(docsrs, doc(cfg(feature = "launch")))] #[allow(deprecated)] @@ -45,12 +51,6 @@ pub use dioxus_hooks as hooks; #[cfg_attr(docsrs, doc(cfg(feature = "signals")))] pub use dioxus_signals as signals; -pub mod events { - #[cfg(feature = "html")] - #[cfg_attr(docsrs, doc(cfg(feature = "html")))] - pub use dioxus_html::prelude::*; -} - #[cfg(feature = "html")] #[cfg_attr(docsrs, doc(cfg(feature = "html")))] pub use dioxus_html as html; @@ -59,11 +59,55 @@ pub use dioxus_html as html; #[cfg_attr(docsrs, doc(cfg(feature = "macro")))] pub use dioxus_core_macro as core_macro; +#[cfg(feature = "web")] +#[cfg_attr(docsrs, doc(cfg(feature = "web")))] +pub use dioxus_web as web; + +#[cfg(feature = "router")] +#[cfg_attr(docsrs, doc(cfg(feature = "router")))] +pub use dioxus_router as router; + +#[cfg(feature = "fullstack")] +#[cfg_attr(docsrs, doc(cfg(feature = "fullstack")))] +pub use dioxus_fullstack as fullstack; + +#[cfg(feature = "static-generation")] +#[cfg_attr(docsrs, doc(cfg(feature = "static-generation")))] +pub use dioxus_static_site_generation as static_site_generation; + +#[cfg(feature = "desktop")] +#[cfg_attr(docsrs, doc(cfg(feature = "desktop")))] +pub use dioxus_desktop as desktop; + +#[cfg(feature = "mobile")] +#[cfg_attr(docsrs, doc(cfg(feature = "mobile")))] +pub use dioxus_mobile as mobile; + +#[cfg(feature = "liveview")] +#[cfg_attr(docsrs, doc(cfg(feature = "liveview")))] +pub use dioxus_liveview as liveview; + +#[cfg(feature = "ssr")] +#[cfg_attr(docsrs, doc(cfg(feature = "ssr")))] +pub use dioxus_ssr as ssr; + +#[cfg(feature = "asset")] +#[cfg_attr(docsrs, doc(cfg(feature = "asset")))] +pub use manganis as assets; + +#[cfg(feature = "asset")] +#[cfg_attr(docsrs, doc(cfg(feature = "asset")))] +pub use manganis; + pub mod prelude { #[cfg(feature = "launch")] #[cfg_attr(docsrs, doc(cfg(feature = "launch")))] pub use crate::launch::*; + #[cfg(feature = "launch")] + #[cfg_attr(docsrs, doc(cfg(feature = "launch")))] + pub use dioxus_config_macro::*; + #[cfg(feature = "hooks")] #[cfg_attr(docsrs, doc(cfg(feature = "hooks")))] pub use crate::hooks::*; @@ -79,10 +123,6 @@ pub mod prelude { #[allow(deprecated)] pub use dioxus_core_macro::{component, rsx, Props}; - #[cfg(feature = "launch")] - #[cfg_attr(docsrs, doc(cfg(feature = "launch")))] - pub use dioxus_config_macro::*; - #[cfg(feature = "html")] #[cfg_attr(docsrs, doc(cfg(feature = "html")))] pub use dioxus_html as dioxus_elements; @@ -91,13 +131,6 @@ pub mod prelude { #[cfg_attr(docsrs, doc(cfg(feature = "html")))] pub use dioxus_elements::{global_attributes, prelude::*, svg_attributes}; - #[cfg(all( - not(any(target_arch = "wasm32", target_os = "ios", target_os = "android")), - feature = "hot-reload" - ))] - #[cfg_attr(docsrs, doc(cfg(feature = "hot-reload")))] - pub use dioxus_hot_reload; - pub use dioxus_core; #[cfg(feature = "fullstack")] @@ -125,49 +158,9 @@ pub mod prelude { #[cfg(feature = "asset")] #[cfg_attr(docsrs, doc(cfg(feature = "asset")))] - pub use manganis::{self, self as assets, asset, classes, Asset, ImageAsset, ImageType}; + pub use manganis::{self, self as assets, asset, Asset, ImageAsset, ImageType}; #[cfg(feature = "document")] #[cfg_attr(docsrs, doc(cfg(feature = "document")))] pub use dioxus_document as document; } - -#[cfg(feature = "web")] -#[cfg_attr(docsrs, doc(cfg(feature = "web")))] -pub use dioxus_web as web; - -#[cfg(feature = "router")] -#[cfg_attr(docsrs, doc(cfg(feature = "router")))] -pub use dioxus_router as router; - -#[cfg(feature = "fullstack")] -#[cfg_attr(docsrs, doc(cfg(feature = "fullstack")))] -pub use dioxus_fullstack as fullstack; - -#[cfg(feature = "static-generation")] -#[cfg_attr(docsrs, doc(cfg(feature = "static-generation")))] -pub use dioxus_static_site_generation as static_site_generation; - -#[cfg(feature = "desktop")] -#[cfg_attr(docsrs, doc(cfg(feature = "desktop")))] -pub use dioxus_desktop as desktop; - -#[cfg(feature = "mobile")] -#[cfg_attr(docsrs, doc(cfg(feature = "mobile")))] -pub use dioxus_mobile as mobile; - -#[cfg(feature = "liveview")] -#[cfg_attr(docsrs, doc(cfg(feature = "liveview")))] -pub use dioxus_liveview as liveview; - -#[cfg(feature = "ssr")] -#[cfg_attr(docsrs, doc(cfg(feature = "ssr")))] -pub use dioxus_ssr as ssr; - -#[cfg(feature = "asset")] -#[cfg_attr(docsrs, doc(cfg(feature = "asset")))] -pub use manganis as assets; - -#[cfg(feature = "asset")] -#[cfg_attr(docsrs, doc(cfg(feature = "asset")))] -pub use manganis; diff --git a/packages/liveview/Cargo.toml b/packages/liveview/Cargo.toml index 31aa8b41f8..f40dce184a 100644 --- a/packages/liveview/Cargo.toml +++ b/packages/liveview/Cargo.toml @@ -36,7 +36,7 @@ generational-box = { workspace = true } axum = { workspace = true, optional = true, features = ["ws"] } [dev-dependencies] -pretty_env_logger = { version = "0.5.0" } +env_logger = { workspace = true } tokio = { workspace = true, features = ["full"] } axum = { workspace = true, features = ["ws"] } tower = { workspace = true } diff --git a/packages/liveview/examples/axum.rs b/packages/liveview/examples/axum.rs index d015259e9f..6790af90d1 100644 --- a/packages/liveview/examples/axum.rs +++ b/packages/liveview/examples/axum.rs @@ -15,7 +15,7 @@ fn app() -> Element { #[tokio::main] async fn main() { - pretty_env_logger::init(); + env_logger::init(); let addr: std::net::SocketAddr = ([127, 0, 0, 1], 3030).into(); diff --git a/packages/liveview/examples/axum_stress.rs b/packages/liveview/examples/axum_stress.rs index 80e146eed8..6714ad30d3 100644 --- a/packages/liveview/examples/axum_stress.rs +++ b/packages/liveview/examples/axum_stress.rs @@ -21,7 +21,7 @@ fn app() -> Element { #[tokio::main] async fn main() { - pretty_env_logger::init(); + env_logger::init(); let addr: std::net::SocketAddr = ([127, 0, 0, 1], 3030).into(); diff --git a/packages/manganis-macro/src/lib.rs b/packages/manganis-macro/src/lib.rs index cb442c41ae..6313e33621 100644 --- a/packages/manganis-macro/src/lib.rs +++ b/packages/manganis-macro/src/lib.rs @@ -1,34 +1,15 @@ #![doc = include_str!("../README.md")] #![deny(missing_docs)] -// use css::CssAssetParser; -// use file::FileAssetParser; -// use folder::FolderAssetParser; -// use font::FontAssetParser; -// use image::ImageAssetParser; -// use js::JsAssetParser; -// use json::JsonAssetParser; -// use manganis_common::cache::macro_log_file; -// use manganis_common::{MetadataAsset, ResourceAsset, TailwindAsset}; use proc_macro::TokenStream; use proc_macro2::Ident; use proc_macro2::TokenStream as TokenStream2; use quote::{quote, quote_spanned, ToTokens}; -// use resource::ResourceAssetParser; use serde::Serialize; use std::sync::atomic::AtomicBool; use std::sync::atomic::Ordering; use syn::{parse::Parse, parse_macro_input, LitStr}; - pub(crate) mod asset; -// pub(crate) mod css; -// pub(crate) mod file; -// pub(crate) mod folder; -// pub(crate) mod font; -// pub(crate) mod image; -// pub(crate) mod js; -// pub(crate) mod json; -// pub(crate) mod resource; static LOG_FILE_FRESH: AtomicBool = AtomicBool::new(false); @@ -70,34 +51,6 @@ fn generate_link_section(asset: &impl Serialize) -> TokenStream2 { } } -/// Collects tailwind classes that will be included in the final binary and returns them unmodified -/// -/// ```rust -/// // You can include tailwind classes that will be collected into the final binary -/// const TAILWIND_CLASSES: &str = manganis::classes!("flex flex-col p-5"); -/// assert_eq!(TAILWIND_CLASSES, "flex flex-col p-5"); -/// ``` -#[proc_macro] -pub fn classes(input: TokenStream) -> TokenStream { - todo!() - // trace_to_file(); - - // let input_as_str = parse_macro_input!(input as LitStr); - // let input_as_str = input_as_str.value(); - - // let asset = TailwindAsset::new(&input_as_str); - // let link_section = generate_link_section(&asset); - - // quote! { - // { - // #link_section - // #input_as_str - // } - // } - // .into_token_stream() - // .into() -} - /// The mg macro collects assets that will be included in the final binary /// /// # Files @@ -193,54 +146,6 @@ pub fn meta(input: TokenStream) -> TokenStream { // .into() } -// #[cfg(feature = "url-encoding")] -// pub(crate) fn url_encoded_asset( -// file_asset: &manganis_common::ResourceAsset, -// ) -> Result { -// use base64::Engine; - -// let target_directory = -// std::env::var("CARGO_TARGET_DIR").unwrap_or_else(|_| "target".to_string()); -// let output_folder = std::path::Path::new(&target_directory) -// .join("manganis") -// .join("assets"); -// std::fs::create_dir_all(&output_folder).map_err(|e| { -// syn::Error::new( -// proc_macro2::Span::call_site(), -// format!("Failed to create output folder: {}", e), -// ) -// })?; -// manganis_cli_support::process_file(file_asset, &output_folder).map_err(|e| { -// syn::Error::new( -// proc_macro2::Span::call_site(), -// format!("Failed to process file: {}", e), -// ) -// })?; -// let file = output_folder.join(file_asset.location().unique_name()); -// let data = std::fs::read(file).map_err(|e| { -// syn::Error::new( -// proc_macro2::Span::call_site(), -// format!("Failed to read file: {}", e), -// ) -// })?; -// let data = base64::engine::general_purpose::STANDARD_NO_PAD.encode(data); -// let mime = manganis_common::get_mime_from_ext(file_asset.options().extension()); -// Ok(format!("data:{mime};base64,{data}")) -// } - -pub(crate) fn verify_preload_valid(ident: &Ident) -> Result<(), syn::Error> { - // Compile time preload is only supported for the primary package - if std::env::var("CARGO_PRIMARY_PACKAGE").is_err() { - return Err(syn::Error::new( - ident.span(), - "The `preload` option is only supported for the primary package. Libraries should not preload assets or should preload assets\ - at runtime with utilities your framework provides", - )); - } - - Ok(()) -} - /// Information about the manganis link section for a given platform #[derive(Debug, Clone, Copy)] struct LinkSection { diff --git a/packages/manganis/Cargo.toml b/packages/manganis/Cargo.toml index 0011c123bc..82d74cc674 100644 --- a/packages/manganis/Cargo.toml +++ b/packages/manganis/Cargo.toml @@ -14,8 +14,6 @@ keywords = ["assets"] [dependencies] manganis-macro = { workspace = true, optional = true } -once_cell = "1.19.0" -dunce = "1.0.2" dioxus-core-types = { workspace = true } # dunce = "1.0.2" @@ -28,6 +26,9 @@ dioxus-core-types = { workspace = true } # dirs = "5.0.1" # infer = { workspace = true } # manganis-common = { workspace = true } + +once_cell = "1.19.0" +dunce = "1.0.2" serde = { version = "1.0.183", features = ["derive"] } anyhow = "1" base64 = { workspace = true } diff --git a/packages/signals/src/lib.rs b/packages/signals/src/lib.rs index b8b6a75747..3ff8a3f494 100644 --- a/packages/signals/src/lib.rs +++ b/packages/signals/src/lib.rs @@ -41,3 +41,5 @@ mod props; pub use props::*; pub mod warnings; + +pub mod prelude {} From 3346861365eea14281e993c62a3d6caf205280a6 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 21 Aug 2024 21:54:20 -0700 Subject: [PATCH 030/139] simplify some things, drop some crates --- Cargo.lock | 1 - Cargo.toml | 1 + examples/manganis-test-package/Cargo.toml | 1 - examples/manganis-test-package/src/main.rs | 2 -- .../test-package-nested-dependency/src/file.rs | 2 +- packages/dioxus/Cargo.toml | 8 ++++---- 6 files changed, 6 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 33fab60d32..e6b9d3fb9b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9407,7 +9407,6 @@ version = "0.2.1" dependencies = [ "manganis", "test-package-dependency", - "tracing-subscriber", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index e6efefceb3..132ec36b6c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -162,6 +162,7 @@ fluent-uri = { version = "0.2.0", features = ["serde"] } internment = { version = "0.7.0" } proc-macro2-diagnostics = { version = "0.10", default-features = false } env_logger = "0.11.0" +tracing-subscriber = "0.3.17" # desktop wry = { version = "0.42.0", default-features = false } diff --git a/examples/manganis-test-package/Cargo.toml b/examples/manganis-test-package/Cargo.toml index 60b2383110..3e8baeba20 100644 --- a/examples/manganis-test-package/Cargo.toml +++ b/examples/manganis-test-package/Cargo.toml @@ -9,4 +9,3 @@ publish = false [dependencies] manganis = { workspace = true } test-package-dependency = { path = "./test-package-dependency", version = "0.2.1" } -tracing-subscriber = "0.3.17" diff --git a/examples/manganis-test-package/src/main.rs b/examples/manganis-test-package/src/main.rs index fd65c5b21e..46683870ff 100644 --- a/examples/manganis-test-package/src/main.rs +++ b/examples/manganis-test-package/src/main.rs @@ -31,8 +31,6 @@ use test_package_dependency::*; // ]; fn main() { - tracing_subscriber::fmt::init(); - todo!() // // Make sure the macro paths match with the paths that actually exist // for path in ALL_ASSETS { diff --git a/examples/manganis-test-package/test-package-nested-dependency/src/file.rs b/examples/manganis-test-package/test-package-nested-dependency/src/file.rs index 2e560757a5..0bf083b4a4 100644 --- a/examples/manganis-test-package/test-package-nested-dependency/src/file.rs +++ b/examples/manganis-test-package/test-package-nested-dependency/src/file.rs @@ -1,4 +1,4 @@ -use manganis::{asset, classes, Asset, ImageAsset, ImageType}; +use manganis::{asset, Asset, ImageAsset, ImageType}; // const _: &str = manganis::classes!("flex flex-row p-4"); // pub const CSS_ASSET: &str = asset!(file("/all_the_assets/style.css")); diff --git a/packages/dioxus/Cargo.toml b/packages/dioxus/Cargo.toml index 074c8dc28d..3fd6c76487 100644 --- a/packages/dioxus/Cargo.toml +++ b/packages/dioxus/Cargo.toml @@ -108,14 +108,14 @@ server = [ ] [dev-dependencies] +criterion = { workspace = true } +dioxus = { workspace = true } +env_logger = { workspace = true } futures-util = { workspace = true } -tracing = { workspace = true } rand = { version = "0.8.4", features = ["small_rng"] } -criterion = { workspace = true } thiserror = { workspace = true } -env_logger = { workspace = true } tokio = { workspace = true, features = ["full"] } -dioxus = { workspace = true } +tracing = { workspace = true } [[bench]] name = "jsframework" From 3e1c05c9d2e9eee86fdb2bbec4705ec1f9144516 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 21 Aug 2024 21:58:32 -0700 Subject: [PATCH 031/139] remove gloo dialogs --- Cargo.lock | 1 - packages/web/Cargo.toml | 47 ++++++++++++++------------ packages/web/examples/timeout_count.rs | 6 +++- 3 files changed, 30 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e6b9d3fb9b..26d3dc1f2d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2960,7 +2960,6 @@ dependencies = [ "futures-channel", "futures-util", "generational-box", - "gloo-dialogs", "gloo-timers 0.3.0", "js-sys", "lazy-js-bundle", diff --git a/packages/web/Cargo.toml b/packages/web/Cargo.toml index ce4a060aa7..5032540ec8 100644 --- a/packages/web/Cargo.toml +++ b/packages/web/Cargo.toml @@ -23,24 +23,19 @@ dioxus-interpreter-js = { workspace = true, features = [ ] } generational-box = { workspace = true } -js-sys = "0.3.56" -wasm-bindgen = { workspace = true } -wasm-bindgen-futures = "0.4.29" -tracing = { workspace = true } -rustc-hash = { workspace = true } -console_error_panic_hook = { version = "0.1.7", optional = true } -futures-util = { workspace = true, features = [ - # "std", - "async-await", - "async-await-macro", -] } async-trait = { workspace = true } +ciborium = { workspace = true, optional = true } +console_error_panic_hook = { version = "0.1.7", optional = true } futures-channel = { workspace = true } -serde_json = { version = "1.0", optional = true } -serde = { version = "1.0", optional = true } +futures-util = { workspace = true, features = [ "async-await", "async-await-macro" ] } +js-sys = "0.3.56" +rustc-hash = { workspace = true } +serde = { workspace = true, optional = true } +serde_json = { workspace = true, optional = true } serde-wasm-bindgen = { version = "0.5.0", optional = true } - -ciborium = { workspace = true, optional = true } +tracing = { workspace = true } +wasm-bindgen = { workspace = true } +wasm-bindgen-futures = "0.4.29" [dependencies.web-sys] version = "0.3.56" @@ -82,25 +77,33 @@ features = [ [build-dependencies] lazy-js-bundle = { workspace = true } - [features] -default = ["panic_hook", "mounted","hot_reload", "document"] +default = ["panic_hook", "mounted", "hot_reload", "document"] # default = ["panic_hook", "mounted", "file_engine", "hot_reload", "document"] panic_hook = ["dep:console_error_panic_hook"] hydrate = ["web-sys/Comment", "ciborium", "dep:serde"] mounted = ["web-sys/Element", "dioxus-html/mounted"] -file_engine = [ - "dioxus-html/file-engine", +file_engine = ["dioxus-html/file-engine"] +hot_reload = [ + "web-sys/MessageEvent", + "web-sys/WebSocket", + "web-sys/Location", + "dep:serde_json", + "dep:serde", + "dioxus-core/serialize", +] +document = [ + "dioxus-html/document", + "dep:serde-wasm-bindgen", + "dep:serde_json", + "dep:serde", ] -hot_reload = ["web-sys/MessageEvent", "web-sys/WebSocket", "web-sys/Location", "dep:serde_json", "dep:serde", "dioxus-core/serialize"] -document = ["dioxus-html/document", "dep:serde-wasm-bindgen", "dep:serde_json", "dep:serde"] [dev-dependencies] dioxus = { workspace = true, default-features = true } wasm-bindgen-test = "0.3.29" dioxus-ssr = { workspace = true, default-features = false } gloo-timers = { workspace = true } -gloo-dialogs = "0.1.1" dioxus-web = { path = ".", features = ["hydrate"] } tracing-wasm = "0.2.1" diff --git a/packages/web/examples/timeout_count.rs b/packages/web/examples/timeout_count.rs index 57736f19ee..c6a836dd27 100644 --- a/packages/web/examples/timeout_count.rs +++ b/packages/web/examples/timeout_count.rs @@ -11,7 +11,11 @@ fn app() -> Element { let mut start = move || { if !started() { - let alert = move || gloo_dialogs::alert(&format!("Your score was {count}!",)); + let alert = move || { + web_sys::console::log_1(&wasm_bindgen::JsValue::from(&format!( + "Your score was {count}!", + ))) + }; gloo_timers::callback::Timeout::new(5_000, alert).forget(); } started.set(true); // this cannot be done inside condition or infinite loop From 170bc6b47e742e848601c8574c4aadacaa652d9e Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 21 Aug 2024 22:16:44 -0700 Subject: [PATCH 032/139] html doesnt need document --- Cargo.lock | 3 - Cargo.toml | 1 + packages/cli/Cargo.toml | 2 +- packages/desktop/Cargo.toml | 1 - packages/dioxus/Cargo.toml | 2 +- packages/html/Cargo.toml | 15 +-- packages/html/src/document/bindings.rs | 35 ----- packages/html/src/document/head.rs | 1 - packages/html/src/document/mod.rs | 50 ------- packages/html/src/lib.rs | 8 -- packages/liveview/Cargo.toml | 2 +- packages/manganis-macro/src/asset.rs | 1 + packages/manganis-macro/src/css.rs | 130 ------------------- packages/manganis-macro/src/font.rs | 173 ------------------------- packages/manganis-macro/src/js.rs | 150 --------------------- packages/manganis-macro/src/json.rs | 70 ---------- packages/ssr/Cargo.toml | 13 +- packages/web/Cargo.toml | 7 +- 18 files changed, 20 insertions(+), 644 deletions(-) delete mode 100644 packages/html/src/document/bindings.rs delete mode 100644 packages/html/src/document/head.rs delete mode 100644 packages/html/src/document/mod.rs delete mode 100644 packages/manganis-macro/src/css.rs delete mode 100644 packages/manganis-macro/src/font.rs delete mode 100644 packages/manganis-macro/src/js.rs delete mode 100644 packages/manganis-macro/src/json.rs diff --git a/Cargo.lock b/Cargo.lock index 26d3dc1f2d..ca7385ac66 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2661,7 +2661,6 @@ dependencies = [ "enumset", "euclid", "futures-channel", - "js-sys", "keyboard-types", "lazy-js-bundle", "manganis", @@ -2670,8 +2669,6 @@ dependencies = [ "serde_repr", "tokio", "tracing", - "wasm-bindgen", - "wasm-bindgen-futures", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 132ec36b6c..856b21f063 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -163,6 +163,7 @@ internment = { version = "0.7.0" } proc-macro2-diagnostics = { version = "0.10", default-features = false } env_logger = "0.11.0" tracing-subscriber = "0.3.17" +chrono = { version = "0.4.34" } # desktop wry = { version = "0.42.0", default-features = false } diff --git a/packages/cli/Cargo.toml b/packages/cli/Cargo.toml index 0028179701..ba6376234b 100644 --- a/packages/cli/Cargo.toml +++ b/packages/cli/Cargo.toml @@ -32,7 +32,7 @@ cargo_metadata = "0.18.1" tokio = { version = "1.16.1", features = ["fs", "sync", "rt", "macros", "process", "rt-multi-thread"] } tokio-stream = "0.1.15" atty = "0.2.14" -chrono = "0.4.19" +chrono = { workspace = true } anyhow = "1" hyper = { workspace = true } hyper-util = "0.1.3" diff --git a/packages/desktop/Cargo.toml b/packages/desktop/Cargo.toml index fe3acefce7..2b3e3f8ceb 100644 --- a/packages/desktop/Cargo.toml +++ b/packages/desktop/Cargo.toml @@ -14,7 +14,6 @@ dioxus-core = { workspace = true, features = ["serialize"] } dioxus-html = { workspace = true, features = [ "serialize", "mounted", - "document", "file-engine", ] } dioxus-document = { workspace = true } diff --git a/packages/dioxus/Cargo.toml b/packages/dioxus/Cargo.toml index 3fd6c76487..8a485df151 100644 --- a/packages/dioxus/Cargo.toml +++ b/packages/dioxus/Cargo.toml @@ -58,7 +58,7 @@ hot-reload = [ mounted = ["dioxus-web?/mounted", "dioxus-html?/mounted"] file_engine = ["dioxus-web?/file_engine"] asset = ["dep:manganis"] -document = ["dioxus-web?/document", "dioxus-html?/document"] +document = ["dioxus-web?/document"] launch = ["dep:dioxus-config-macro"] router = ["dep:dioxus-router"] diff --git a/packages/html/Cargo.toml b/packages/html/Cargo.toml index af86a18385..33a1ee7e7f 100644 --- a/packages/html/Cargo.toml +++ b/packages/html/Cargo.toml @@ -29,9 +29,9 @@ futures-channel = { workspace = true } # todo: we shouldn't have any wasm-bindgen dependencies in the html cratea # [target.'cfg(target_arch = "wasm32")'.dependencies] -wasm-bindgen = { workspace = true, optional = true } -js-sys = { version = "0.3.56", optional = true } -wasm-bindgen-futures = { workspace = true, optional = true } +# wasm-bindgen = { workspace = true, optional = true } +# js-sys = { version = "0.3.56", optional = true } +# wasm-bindgen-futures = { workspace = true, optional = true } [build-dependencies] lazy-js-bundle = { workspace = true } @@ -44,7 +44,7 @@ tokio = { workspace = true, features = ["time"] } manganis = { workspace = true } [features] -default = ["serialize", "mounted", "document", "file-engine"] +default = ["serialize", "mounted", "file-engine"] serialize = [ "dep:serde", "dep:serde_json", @@ -61,10 +61,6 @@ mounted = [ # "web-sys?/ScrollBehavior", # "web-sys?/HtmlElement", ] -document = [ - "dep:serde", - "dep:serde_json" -] file-engine = [ "dep:async-trait", # "dep:js-sys", @@ -72,12 +68,9 @@ file-engine = [ # "web-sys?/FileList", # "web-sys?/FileReader" ] -# wasm-bind = ["dep:web-sys", "dep:wasm-bindgen", "dep:wasm-bindgen-futures"] -# native-bind = ["dep:tokio", "file-engine"] hot-reload-context = ["dep:dioxus-rsx", "dioxus-rsx/hot_reload_traits"] html-to-rsx = [] [package.metadata.docs.rs] cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"] feature = ["html-to-rsx", "hot-reload-context", "html-to-rsx"] -# feature = ["html-to-rsx", "hot-reload-context", "html-to-rsx", "native-bind", "wasm-bind"] diff --git a/packages/html/src/document/bindings.rs b/packages/html/src/document/bindings.rs deleted file mode 100644 index 6227372358..0000000000 --- a/packages/html/src/document/bindings.rs +++ /dev/null @@ -1,35 +0,0 @@ -// /// Code for the Dioxus channel used to communicate between the dioxus and javascript code -// #[cfg(feature = "native-bind")] -pub const NATIVE_EVAL_JS: &str = include_str!("../js/native_eval.js"); - -// #[cfg(feature = "wasm-bind")] -// #[wasm_bindgen::prelude::wasm_bindgen(module = "/src/js/eval.js")] -// extern "C" { -// pub type WebDioxusChannel; - -// #[wasm_bindgen(constructor)] -// pub fn new(owner: JSOwner) -> WebDioxusChannel; - -// #[wasm_bindgen(method, js_name = "rustSend")] -// pub fn rust_send(this: &WebDioxusChannel, value: wasm_bindgen::JsValue); - -// #[wasm_bindgen(method, js_name = "rustRecv")] -// pub async fn rust_recv(this: &WebDioxusChannel) -> wasm_bindgen::JsValue; - -// #[wasm_bindgen(method)] -// pub fn send(this: &WebDioxusChannel, value: wasm_bindgen::JsValue); - -// #[wasm_bindgen(method)] -// pub async fn recv(this: &WebDioxusChannel) -> wasm_bindgen::JsValue; - -// #[wasm_bindgen(method)] -// pub fn weak(this: &WebDioxusChannel) -> WeakDioxusChannel; - -// pub type WeakDioxusChannel; - -// #[wasm_bindgen(method, js_name = "rustSend")] -// pub fn rust_send(this: &WeakDioxusChannel, value: wasm_bindgen::JsValue); - -// #[wasm_bindgen(method, js_name = "rustRecv")] -// pub async fn rust_recv(this: &WeakDioxusChannel) -> wasm_bindgen::JsValue; -// } diff --git a/packages/html/src/document/head.rs b/packages/html/src/document/head.rs deleted file mode 100644 index 8b13789179..0000000000 --- a/packages/html/src/document/head.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/packages/html/src/document/mod.rs b/packages/html/src/document/mod.rs deleted file mode 100644 index 9e34b33cef..0000000000 --- a/packages/html/src/document/mod.rs +++ /dev/null @@ -1,50 +0,0 @@ -// API inspired by Reacts implementation of head only elements. We use components here instead of elements to simplify internals. - -use std::{ - rc::Rc, - task::{Context, Poll}, -}; - -// use generational_box::{AnyStorage, GenerationalBox, UnsyncStorage}; - -// mod bindings; -// #[allow(unused)] -// pub use bindings::*; - -// pub mod head; -// pub use head::{ -// Meta, MetaProps, Script, ScriptProps, Style, StyleProps, Stylesheet, Title, TitleProps, -// }; - -// /// The default No-Op document -// pub struct NoOpDocument; - -// impl Document for NoOpDocument { -// fn new_evaluator(&self, _js: String) -> GenerationalBox> { -// tracing::error!("Eval is not supported on this platform. If you are using dioxus fullstack, you can wrap your code with `client! {{}}` to only include the code that runs eval in the client bundle."); -// UnsyncStorage::owner().insert(Box::new(NoOpEvaluator)) -// } - -// fn as_any(&self) -> &dyn std::any::Any { -// self -// } -// } - -// struct NoOpEvaluator; -// impl Evaluator for NoOpEvaluator { -// fn send(&self, _data: serde_json::Value) -> Result<(), EvalError> { -// Err(EvalError::Unsupported) -// } -// fn poll_recv( -// &mut self, -// _context: &mut Context<'_>, -// ) -> Poll> { -// Poll::Ready(Err(EvalError::Unsupported)) -// } -// fn poll_join( -// &mut self, -// _context: &mut Context<'_>, -// ) -> Poll> { -// Poll::Ready(Err(EvalError::Unsupported)) -// } -// } diff --git a/packages/html/src/lib.rs b/packages/html/src/lib.rs index 373bde5b4d..4a96a4ceda 100644 --- a/packages/html/src/lib.rs +++ b/packages/html/src/lib.rs @@ -43,9 +43,6 @@ pub use attribute_groups::*; pub use elements::*; pub use events::*; -#[cfg(feature = "document")] -pub mod document; - pub mod extensions { pub use crate::attribute_groups::{GlobalAttributesExtension, SvgAttributesExtension}; pub use crate::elements::extensions::*; @@ -53,11 +50,6 @@ pub mod extensions { pub mod prelude { pub use crate::attribute_groups::{GlobalAttributesExtension, SvgAttributesExtension}; - // #[cfg(feature = "document")] - // pub use crate::document::{ - // self, document, eval, head, Document, Meta, MetaProps, Script, ScriptProps, Style, - // StyleProps, Stylesheet, Title, TitleProps, UseEval, - // }; pub use crate::elements::extensions::*; pub use crate::events::*; pub use crate::point_interaction::*; diff --git a/packages/liveview/Cargo.toml b/packages/liveview/Cargo.toml index f40dce184a..4a002c4e5d 100644 --- a/packages/liveview/Cargo.toml +++ b/packages/liveview/Cargo.toml @@ -22,7 +22,7 @@ tokio-stream = { version = "0.1.11", features = ["net"] } tokio-util = { version = "0.7.4", features = ["rt"] } serde = { version = "1.0.151", features = ["derive"] } serde_json = "1.0.91" -dioxus-html = { workspace = true, features = ["serialize", "document", "mounted"] } +dioxus-html = { workspace = true, features = ["serialize", "mounted"] } rustc-hash = { workspace = true } dioxus-core = { workspace = true, features = ["serialize"] } diff --git a/packages/manganis-macro/src/asset.rs b/packages/manganis-macro/src/asset.rs index 4d614a7f4e..2bf8c065ea 100644 --- a/packages/manganis-macro/src/asset.rs +++ b/packages/manganis-macro/src/asset.rs @@ -28,6 +28,7 @@ struct ResourceAsset { pub local: Option, pub bundled: PathBuf, } + #[derive(Debug)] struct AssetError {} impl ResourceAsset { diff --git a/packages/manganis-macro/src/css.rs b/packages/manganis-macro/src/css.rs deleted file mode 100644 index 0ad917b992..0000000000 --- a/packages/manganis-macro/src/css.rs +++ /dev/null @@ -1,130 +0,0 @@ -use manganis_common::{AssetType, CssOptions, FileOptions, ManganisSupportError, ResourceAsset}; -use quote::{quote, ToTokens}; -use syn::{parenthesized, parse::Parse, LitBool}; - -// use crate::{generate_link_section, resource::ResourceAssetParser}; - -struct ParseCssOptions { - options: Vec, -} - -impl ParseCssOptions { - fn apply_to_options(self, file: &mut ResourceAsset) { - for option in self.options { - option.apply_to_options(file); - } - } -} - -impl Parse for ParseCssOptions { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let mut options = Vec::new(); - while !input.is_empty() { - options.push(input.parse::()?); - } - Ok(ParseCssOptions { options }) - } -} - -enum ParseCssOption { - UrlEncoded(bool), - Preload(bool), - Minify(bool), -} - -impl ParseCssOption { - fn apply_to_options(self, file: &mut ResourceAsset) { - match self { - ParseCssOption::Preload(_) | ParseCssOption::Minify(_) => { - file.with_options_mut(|options| { - if let FileOptions::Css(options) = options { - match self { - ParseCssOption::Minify(format) => { - options.set_minify(format); - } - ParseCssOption::Preload(preload) => { - options.set_preload(preload); - } - _ => {} - } - } - }) - } - ParseCssOption::UrlEncoded(url_encoded) => { - file.set_url_encoded(url_encoded); - } - } - } -} - -impl Parse for ParseCssOption { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let _ = input.parse::()?; - let ident = input.parse::()?; - let content; - parenthesized!(content in input); - match ident.to_string().as_str() { - "preload" => { - crate::verify_preload_valid(&ident)?; - Ok(ParseCssOption::Preload(true)) - } - "url_encoded" => { - Ok(ParseCssOption::UrlEncoded(true)) - } - "minify" => { - Ok(ParseCssOption::Minify(content.parse::()?.value())) - } - _ => Err(syn::Error::new( - proc_macro2::Span::call_site(), - format!( - "Unknown Css option: {}. Supported options are preload, url_encoded, and minify", - ident - ), - )), - } - } -} - -pub struct CssAssetParser { - asset: ResourceAsset, -} - -impl Parse for CssAssetParser { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let inside; - parenthesized!(inside in input); - let path = inside.parse::()?; - - let parsed_options = { - if input.is_empty() { - None - } else { - Some(input.parse::()?) - } - }; - - let path_as_str = path.value(); - - let mut asset: ResourceAsset = match ResourceAsset::parse_file(&path_as_str) { - Ok(asset) => asset.with_options(manganis_common::FileOptions::Css(CssOptions::new())), - Err(e) => { - return Err(syn::Error::new( - proc_macro2::Span::call_site(), - format!("{e}"), - )) - } - }; - - if let Some(parsed_options) = parsed_options { - parsed_options.apply_to_options(&mut asset); - } - - Ok(CssAssetParser { asset }) - } -} - -impl ToTokens for CssAssetParser { - fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { - ResourceAssetParser::to_ref_tokens(&self.asset, tokens) - } -} diff --git a/packages/manganis-macro/src/font.rs b/packages/manganis-macro/src/font.rs deleted file mode 100644 index 3bbac2412e..0000000000 --- a/packages/manganis-macro/src/font.rs +++ /dev/null @@ -1,173 +0,0 @@ -use manganis_common::{AssetType, CssOptions, ManganisSupportError, ResourceAsset}; -use quote::{quote, ToTokens}; -use syn::{bracketed, parenthesized, parse::Parse}; - -use crate::{generate_link_section, resource::ResourceAssetParser}; - -#[derive(Default)] -struct FontFamilies { - families: Vec, -} - -impl Parse for FontFamilies { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let inside; - bracketed!(inside in input); - let array = - syn::punctuated::Punctuated::::parse_separated_nonempty( - &inside, - )?; - Ok(FontFamilies { - families: array.into_iter().map(|f| f.value()).collect(), - }) - } -} - -#[derive(Default)] -struct FontWeights { - weights: Vec, -} - -impl Parse for FontWeights { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let inside; - bracketed!(inside in input); - let array = - syn::punctuated::Punctuated::::parse_separated_nonempty( - &inside, - )?; - Ok(FontWeights { - weights: array - .into_iter() - .map(|f| f.base10_parse().unwrap()) - .collect(), - }) - } -} - -struct ParseFontOptions { - families: FontFamilies, - weights: FontWeights, - text: Option, - display: Option, -} - -impl ParseFontOptions { - fn url(&self) -> String { - let mut segments = Vec::new(); - - let families: Vec<_> = self - .families - .families - .iter() - .map(|f| f.replace(' ', "+")) - .collect(); - if !families.is_empty() { - segments.push(format!("family={}", families.join("&"))); - } - - let weights: Vec<_> = self.weights.weights.iter().map(|w| w.to_string()).collect(); - if !weights.is_empty() { - segments.push(format!("weight={}", weights.join(","))); - } - - if let Some(text) = &self.text { - segments.push(format!("text={}", text.replace(' ', "+"))); - } - - if let Some(display) = &self.display { - segments.push(format!("display={}", display.replace(' ', "+"))); - } - - let query = if segments.is_empty() { - String::new() - } else { - format!("?{}", segments.join("&")) - }; - - format!("https://fonts.googleapis.com/css2{}", query) - } -} - -impl Parse for ParseFontOptions { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let mut families = None; - let mut weights = None; - let mut text = None; - let mut display = None; - loop { - if input.is_empty() { - break; - } - let _ = input.parse::()?; - let ident = input.parse::()?; - let inside; - parenthesized!(inside in input); - match ident.to_string().to_lowercase().as_str() { - "families" => { - families = Some(inside.parse::()?); - } - "weights" => { - weights = Some(inside.parse::()?); - } - "text" => { - text = Some(inside.parse::()?.value()); - } - "display" => { - display = Some(inside.parse::()?.value()); - } - _ => { - return Err(syn::Error::new( - proc_macro2::Span::call_site(), - format!("Unknown font option: {ident}. Supported options are families, weights, text, display"), - )) - } - } - } - - Ok(ParseFontOptions { - families: families.unwrap_or_default(), - weights: weights.unwrap_or_default(), - text, - display, - }) - } -} - -pub struct FontAssetParser { - asset: ResourceAsset, -} - -impl Parse for FontAssetParser { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let inside; - parenthesized!(inside in input); - if !inside.is_empty() { - return Err(syn::Error::new( - proc_macro2::Span::call_site(), - "Font assets do not support paths. Please use file() if you want to import a local font file", - )); - } - - let options = input.parse::()?; - - let url = options.url(); - let asset: ResourceAsset = match ResourceAsset::parse_file(&url) { - Ok(url) => url, - Err(e) => { - return Err(syn::Error::new( - proc_macro2::Span::call_site(), - format!("Failed to parse url: {url:?}\n{e}"), - )) - } - }; - - Ok(FontAssetParser { asset }) - } -} - -impl ToTokens for FontAssetParser { - fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { - ResourceAssetParser::to_ref_tokens(&self.asset, tokens) - } -} diff --git a/packages/manganis-macro/src/js.rs b/packages/manganis-macro/src/js.rs deleted file mode 100644 index 88d4319261..0000000000 --- a/packages/manganis-macro/src/js.rs +++ /dev/null @@ -1,150 +0,0 @@ -use manganis_common::{ - AssetType, FileOptions, JsOptions, JsType, ManganisSupportError, ResourceAsset, -}; -use quote::{quote, ToTokens}; -use syn::{parenthesized, parse::Parse, LitBool}; - -use crate::{generate_link_section, resource::ResourceAssetParser}; - -struct ParseJsOptions { - options: Vec, -} - -impl ParseJsOptions { - fn apply_to_options(self, file: &mut ResourceAsset) { - for option in self.options { - option.apply_to_options(file); - } - } -} - -impl Parse for ParseJsOptions { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let mut options = Vec::new(); - while !input.is_empty() { - options.push(input.parse::()?); - } - Ok(ParseJsOptions { options }) - } -} - -enum ParseJsOption { - UrlEncoded(bool), - Preload(bool), - Minify(bool), -} - -impl ParseJsOption { - fn apply_to_options(self, file: &mut ResourceAsset) { - match self { - ParseJsOption::Preload(_) | ParseJsOption::Minify(_) => { - file.with_options_mut(|options| { - if let FileOptions::Js(options) = options { - match self { - ParseJsOption::Minify(format) => { - options.set_minify(format); - } - ParseJsOption::Preload(preload) => { - options.set_preload(preload); - } - _ => {} - } - } - }) - } - ParseJsOption::UrlEncoded(url_encoded) => { - file.set_url_encoded(url_encoded); - } - } - } -} - -impl Parse for ParseJsOption { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let _ = input.parse::()?; - let ident = input.parse::()?; - let content; - parenthesized!(content in input); - match ident.to_string().as_str() { - "preload" => { - crate::verify_preload_valid(&ident)?; - Ok(ParseJsOption::Preload(true)) - } - "url_encoded" => Ok(ParseJsOption::UrlEncoded(true)), - "minify" => Ok(ParseJsOption::Minify(content.parse::()?.value())), - _ => Err(syn::Error::new( - proc_macro2::Span::call_site(), - format!( - "Unknown Js option: {}. Supported options are preload, url_encoded, and minify", - ident - ), - )), - } - } -} - -pub struct JsAssetParser { - asset: ResourceAsset, -} - -impl Parse for JsAssetParser { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let inside; - parenthesized!(inside in input); - let path = inside.parse::()?; - - let parsed_options = { - if input.is_empty() { - None - } else { - Some(input.parse::()?) - } - }; - - let path_as_str = path.value(); - let mut asset: ResourceAsset = match ResourceAsset::parse_file(&path_as_str) { - Ok(path) => { - path.with_options(manganis_common::FileOptions::Js(JsOptions::new(JsType::Js))) - } - Err(e) => { - return Err(syn::Error::new( - proc_macro2::Span::call_site(), - format!("{e}"), - )) - } - }; - - if let Some(parsed_options) = parsed_options { - parsed_options.apply_to_options(&mut asset); - } - - // let mut this_file = ResourceAsset::new(path.clone()) - // .with_options(manganis_common::FileOptions::Js(JsOptions::new(JsType::Js))); - // let asset = manganis_common::AssetType::Resource(this_file.clone()); - - // let file_name = if this_file.url_encoded() { - // #[cfg(not(feature = "url-encoding"))] - // return Err(syn::Error::new( - // proc_macro2::Span::call_site(), - // "URL encoding is not enabled. Enable the url-encoding feature to use this feature", - // )); - // #[cfg(feature = "url-encoding")] - // Ok(crate::url_encoded_asset(&this_file).map_err(|e| { - // syn::Error::new( - // proc_macro2::Span::call_site(), - // format!("Failed to encode file: {}", e), - // ) - // })?) - // } else { - // this_file.served_location() - // }; - - Ok(JsAssetParser { asset }) - } -} - -impl ToTokens for JsAssetParser { - fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { - ResourceAssetParser::to_ref_tokens(&self.asset, tokens) - } -} diff --git a/packages/manganis-macro/src/json.rs b/packages/manganis-macro/src/json.rs deleted file mode 100644 index f3d2dfcb76..0000000000 --- a/packages/manganis-macro/src/json.rs +++ /dev/null @@ -1,70 +0,0 @@ -use manganis_common::{AssetType, FileOptions, ManganisSupportError, ResourceAsset}; -use quote::{quote, ToTokens}; -use syn::{parenthesized, parse::Parse}; - -use crate::{generate_link_section, resource::ResourceAssetParser}; - -pub enum ParseJsonOption { - UrlEncoded(bool), - Preload(bool), -} - -impl ParseJsonOption { - fn apply(options: Vec, file: &mut ResourceAsset) { - for option in options { - option.apply_to_options(file); - } - } - fn apply_to_options(self, file: &mut ResourceAsset) { - match self { - ParseJsonOption::Preload(preload) => file.with_options_mut(|options| { - if let FileOptions::Json(options) = options { - options.set_preload(preload); - } - }), - ParseJsonOption::UrlEncoded(url_encoded) => { - file.set_url_encoded(url_encoded); - } - } - } -} - -impl Parse for ParseJsonOption { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let _ = input.parse::()?; - let ident = input.parse::()?; - let _content; - parenthesized!(_content in input); - match ident.to_string().as_str() { - "preload" => { - crate::verify_preload_valid(&ident)?; - Ok(ParseJsonOption::Preload(true)) - }, - "url_encoded" => Ok(ParseJsonOption::UrlEncoded(true)), - _ => Err(syn::Error::new( - proc_macro2::Span::call_site(), - format!( - "Unknown Json option: {}. Supported options are preload, url_encoded, and minify", - ident - ), - )), - } - } -} - -// let file_name = if asset.url_encoded() { -// #[cfg(not(feature = "url-encoding"))] -// return Err(syn::Error::new( -// proc_macro2::Span::call_site(), -// "URL encoding is not enabled. Enable the url-encoding feature to use this feature", -// )); -// #[cfg(feature = "url-encoding")] -// Ok(crate::url_encoded_asset(&asset).map_err(|e| { -// syn::Error::new( -// proc_macro2::Span::call_site(), -// format!("Failed to encode file: {}", e), -// ) -// })?) -// } else { -// asset.served_location() -// }; diff --git a/packages/ssr/Cargo.toml b/packages/ssr/Cargo.toml index e313fd7285..0e33053724 100644 --- a/packages/ssr/Cargo.toml +++ b/packages/ssr/Cargo.toml @@ -10,26 +10,25 @@ keywords = ["dom", "ui", "gui", "react", "ssr"] [dependencies] dioxus-core = { workspace = true, features = ["serialize"] } -dioxus-html = { workspace = true, features = ["document"]} +dioxus-html = { workspace = true } dioxus-core-types = { workspace = true } -# dioxus-cli-config = { workspace = true, features = ["read-config"], optional = true } dioxus-interpreter-js = { workspace = true } generational-box = { workspace = true } askama_escape = "0.10.3" -thiserror = "1.0.23" -rustc-hash = "1.1.0" +thiserror = { workspace = true } +rustc-hash = { workspace = true } lru = { workspace = true } tracing = { workspace = true } http = { workspace = true } async-trait = { workspace = true } serde_json = { workspace = true } -chrono = { version = "0.4.34", optional = true } +chrono = { workspace = true, optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] -tokio = { version = "1.28", features = ["io-util"], optional = true } +tokio = { workspace = true, features = ["io-util"], optional = true } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] -tokio = { version = "1.28", features = ["fs", "io-util"], optional = true } +tokio = { workspace = true, features = ["fs", "io-util"], optional = true } [dev-dependencies] dioxus = { workspace = true } diff --git a/packages/web/Cargo.toml b/packages/web/Cargo.toml index 5032540ec8..b1c214f54e 100644 --- a/packages/web/Cargo.toml +++ b/packages/web/Cargo.toml @@ -78,9 +78,13 @@ features = [ lazy-js-bundle = { workspace = true } [features] -default = ["panic_hook", "mounted", "hot_reload", "document"] +default = ["panic_hook", "full"] +full = [ + +] # default = ["panic_hook", "mounted", "file_engine", "hot_reload", "document"] panic_hook = ["dep:console_error_panic_hook"] + hydrate = ["web-sys/Comment", "ciborium", "dep:serde"] mounted = ["web-sys/Element", "dioxus-html/mounted"] file_engine = ["dioxus-html/file-engine"] @@ -93,7 +97,6 @@ hot_reload = [ "dioxus-core/serialize", ] document = [ - "dioxus-html/document", "dep:serde-wasm-bindgen", "dep:serde_json", "dep:serde", From 76ef0bda2278d36dd0764bc329213083aa232a5b Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 21 Aug 2024 22:43:47 -0700 Subject: [PATCH 033/139] rename hotreload to devtools --- Cargo.lock | 11 ++ Cargo.toml | 8 +- packages/cli/Cargo.toml | 2 +- packages/core-types/src/attributes.rs | 0 packages/core-types/src/lib.rs | 2 + packages/desktop/Cargo.toml | 9 +- packages/desktop/src/app.rs | 6 +- packages/desktop/src/ipc.rs | 2 +- packages/desktop/src/launch.rs | 2 +- packages/desktop/src/webview.rs | 2 +- packages/devtools-types/Cargo.toml | 7 + .../{hot-reload => devtools-types}/src/lib.rs | 12 -- packages/{hot-reload => devtools}/Cargo.toml | 2 +- packages/{hot-reload => devtools}/README.md | 0 .../{hot-reload => devtools}/src/client.rs | 0 packages/devtools/src/lib.rs | 7 + .../src/ws_receiver.rs | 0 packages/dioxus/Cargo.toml | 14 +- packages/fullstack/Cargo.toml | 8 +- packages/html/Cargo.toml | 3 +- packages/html/src/events/animation.rs | 2 +- packages/html/src/events/clipboard.rs | 2 +- packages/html/src/events/composition.rs | 2 +- packages/html/src/events/drag.rs | 2 +- packages/html/src/events/focus.rs | 2 +- packages/html/src/events/form.rs | 2 +- packages/html/src/events/image.rs | 2 +- packages/html/src/events/keyboard.rs | 2 +- packages/html/src/events/media.rs | 2 +- packages/html/src/events/mounted.rs | 2 +- packages/html/src/events/mouse.rs | 2 +- packages/html/src/events/pointer.rs | 2 +- packages/html/src/events/resize.rs | 2 +- packages/html/src/events/scroll.rs | 2 +- packages/html/src/events/selection.rs | 2 +- packages/html/src/events/toggle.rs | 2 +- packages/html/src/events/touch.rs | 2 +- packages/html/src/events/transition.rs | 2 +- packages/html/src/events/wheel.rs | 2 +- packages/liveview/Cargo.toml | 6 +- packages/liveview/src/pool.rs | 10 +- packages/manganis-macro/src/font.rs | 173 ++++++++++++++++++ packages/static-generation/Cargo.toml | 16 +- packages/web/Cargo.toml | 2 +- 44 files changed, 271 insertions(+), 71 deletions(-) create mode 100644 packages/core-types/src/attributes.rs create mode 100644 packages/devtools-types/Cargo.toml rename packages/{hot-reload => devtools-types}/src/lib.rs (87%) rename packages/{hot-reload => devtools}/Cargo.toml (97%) rename packages/{hot-reload => devtools}/README.md (100%) rename packages/{hot-reload => devtools}/src/client.rs (100%) create mode 100644 packages/devtools/src/lib.rs rename packages/{hot-reload => devtools}/src/ws_receiver.rs (100%) create mode 100644 packages/manganis-macro/src/font.rs diff --git a/Cargo.lock b/Cargo.lock index ca7385ac66..39d90a722b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2524,6 +2524,13 @@ dependencies = [ "wry", ] +[[package]] +name = "dioxus-devserver-types" +version = "0.6.0-alpha.2" +dependencies = [ + "serde", +] + [[package]] name = "dioxus-document" version = "0.6.0-alpha.2" @@ -2647,6 +2654,10 @@ dependencies = [ "warnings", ] +[[package]] +name = "dioxus-hotreload-receiver" +version = "0.6.0-alpha.2" + [[package]] name = "dioxus-html" version = "0.6.0-alpha.2" diff --git a/Cargo.toml b/Cargo.toml index 856b21f063..93f523bd8a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,8 @@ members = [ "packages/rsx-rosetta", "packages/generational-box", "packages/signals", - "packages/hot-reload", + "packages/devtools", + "packages/devtools-types", "packages/fullstack", "packages/server-macro", "packages/static-generation", @@ -65,6 +66,8 @@ members = [ "examples/manganis-test-package", "examples/manganis-test-package/test-package-dependency", "examples/manganis-test-package/test-package-nested-dependency", + "packages/devtools", + "packages/devtools-types", ] @@ -97,7 +100,8 @@ dioxus-rsx = { path = "packages/rsx", version = "0.6.0-alpha.2" } rsx-rosetta = { path = "packages/rsx-rosetta", version = "0.6.0-alpha.2" } dioxus-signals = { path = "packages/signals", version = "0.6.0-alpha.2" } generational-box = { path = "packages/generational-box", version = "0.6.0-alpha.2" } -dioxus-hot-reload = { path = "packages/hot-reload", version = "0.6.0-alpha.2" } +dioxus-devtools = { path = "packages/devtools", version = "0.6.0-alpha.2" } +dioxus-devtools-types = { path = "packages/devtools-types", version = "0.6.0-alpha.2" } dioxus-fullstack = { path = "packages/fullstack", version = "0.6.0-alpha.2" } dioxus-static-site-generation = { path = "packages/static-generation", version = "0.6.0-alpha.2" } dioxus_server_macro = { path = "packages/server-macro", version = "0.6.0-alpha.2", default-features = false } diff --git a/packages/cli/Cargo.toml b/packages/cli/Cargo.toml index ba6376234b..75d6292b5d 100644 --- a/packages/cli/Cargo.toml +++ b/packages/cli/Cargo.toml @@ -92,7 +92,7 @@ dioxus-rsx = { workspace = true, features = ["hot_reload"] } dioxus-html = { workspace = true, features = ["hot-reload-context"] } dioxus-core = { workspace = true, features = ["serialize"] } dioxus-core-types = { workspace = true } -dioxus-hot-reload = { workspace = true, features = ["serve"] } +# dioxus-hot-reload = { workspace = true, features = ["serve"] } ignore = "0.4.22" env_logger = { workspace = true } diff --git a/packages/core-types/src/attributes.rs b/packages/core-types/src/attributes.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/core-types/src/lib.rs b/packages/core-types/src/lib.rs index 432d49e695..956ca51a7c 100644 --- a/packages/core-types/src/lib.rs +++ b/packages/core-types/src/lib.rs @@ -1,3 +1,4 @@ +pub mod attributes; pub mod bubbles; mod core; mod event; @@ -5,6 +6,7 @@ pub mod formatter; mod hotreload; pub mod hr_context; +pub use attributes::*; pub use bubbles::*; pub use core::*; pub use event::*; diff --git a/packages/desktop/Cargo.toml b/packages/desktop/Cargo.toml index 2b3e3f8ceb..77ea7df452 100644 --- a/packages/desktop/Cargo.toml +++ b/packages/desktop/Cargo.toml @@ -19,9 +19,8 @@ dioxus-html = { workspace = true, features = [ dioxus-document = { workspace = true } dioxus-signals = { workspace = true, optional = true } dioxus-interpreter-js = { workspace = true, features = ["binary-protocol", "serialize"] } -# dioxus-cli-config = { workspace = true, features = ["read-config"] } generational-box = { workspace = true } -dioxus-hot-reload = { workspace = true, optional = true, features = ["serve", "client"]} +dioxus-devtools = { workspace = true, optional = true } thiserror = { workspace = true } tracing = { workspace = true } tao = { workspace = true, features = ["rwh_05"] } @@ -70,16 +69,16 @@ objc = { workspace = true } objc_id = { workspace = true } [features] -default = ["tokio_runtime", "wry/objc-exception", "hot-reload", "devtools"] +default = ["tokio_runtime", "wry/objc-exception", "devtools"] tokio_runtime = ["dep:tokio"] fullscreen = ["wry/fullscreen"] transparent = ["wry/transparent"] devtools = ["wry/devtools"] -hot-reload = ["dep:dioxus-hot-reload", "dioxus-signals"] +dev = ["dep:dioxus-devtools"] gnu = [] [package.metadata.docs.rs] -features = ["tokio_runtime", "hot-reload"] +features = ["tokio_runtime", "devtools"] cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"] default-features = false targets = [ diff --git a/packages/desktop/src/app.rs b/packages/desktop/src/app.rs index d3105cb954..64be2996b9 100644 --- a/packages/desktop/src/app.rs +++ b/packages/desktop/src/app.rs @@ -86,7 +86,7 @@ impl App { app.set_menubar_receiver(); // Allow hotreloading to work - but only in debug mode - #[cfg(all(feature = "hot-reload", debug_assertions))] + #[cfg(all(feature = "devtools", debug_assertions))] app.connect_hotreload(); #[cfg(debug_assertions)] @@ -135,7 +135,7 @@ impl App { } } - #[cfg(all(feature = "hot-reload", debug_assertions,))] + #[cfg(all(feature = "devtools", debug_assertions,))] pub fn connect_hotreload(&self) { let proxy = self.shared.proxy.clone(); @@ -266,7 +266,7 @@ impl App { view.desktop_context.query.send(result); } - #[cfg(all(feature = "hot-reload", debug_assertions,))] + #[cfg(all(feature = "devtools", debug_assertions,))] pub fn handle_hot_reload_msg(&mut self, msg: dioxus_hot_reload::DevserverMsg) { use dioxus_hot_reload::DevserverMsg; diff --git a/packages/desktop/src/ipc.rs b/packages/desktop/src/ipc.rs index 9fba684e11..4d46f99776 100644 --- a/packages/desktop/src/ipc.rs +++ b/packages/desktop/src/ipc.rs @@ -18,7 +18,7 @@ pub enum UserWindowEvent { Ipc { id: WindowId, msg: IpcMessage }, /// Handle a hotreload event, basically telling us to update our templates - #[cfg(all(feature = "hot-reload", debug_assertions))] + #[cfg(all(feature = "devtools", debug_assertions))] HotReloadEvent(dioxus_hot_reload::DevserverMsg), /// Create a new window diff --git a/packages/desktop/src/launch.rs b/packages/desktop/src/launch.rs index d8a801bce3..0bef0fc113 100644 --- a/packages/desktop/src/launch.rs +++ b/packages/desktop/src/launch.rs @@ -41,7 +41,7 @@ pub fn launch_virtual_dom_blocking(virtual_dom: VirtualDom, desktop_config: Conf #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] UserWindowEvent::MudaMenuEvent(evnt) => app.handle_menu_event(evnt), - #[cfg(all(feature = "hot-reload", debug_assertions))] + #[cfg(all(feature = "devtools", debug_assertions))] UserWindowEvent::HotReloadEvent(msg) => app.handle_hot_reload_msg(msg), UserWindowEvent::Ipc { id, msg } => match msg.method() { diff --git a/packages/desktop/src/webview.rs b/packages/desktop/src/webview.rs index 3eee89a9bf..195136eb6f 100644 --- a/packages/desktop/src/webview.rs +++ b/packages/desktop/src/webview.rs @@ -384,7 +384,7 @@ impl WebviewInstance { } } - #[cfg(all(feature = "hot-reload", debug_assertions))] + #[cfg(all(feature = "devtools", debug_assertions))] pub fn kick_stylsheets(&self) { // run eval in the webview to kick the stylesheets by appending a query string // we should do something less clunky than this diff --git a/packages/devtools-types/Cargo.toml b/packages/devtools-types/Cargo.toml new file mode 100644 index 0000000000..097fcb05c1 --- /dev/null +++ b/packages/devtools-types/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "dioxus-devtools-types" +edition = "2021" +version.workspace = true + +[dependencies] +serde = { workspace = true, features = ["derive"] } diff --git a/packages/hot-reload/src/lib.rs b/packages/devtools-types/src/lib.rs similarity index 87% rename from packages/hot-reload/src/lib.rs rename to packages/devtools-types/src/lib.rs index e1956a547a..20355de3b7 100644 --- a/packages/hot-reload/src/lib.rs +++ b/packages/devtools-types/src/lib.rs @@ -2,18 +2,6 @@ use dioxus_core::internal::HotReloadTemplateWithLocation; use serde::{Deserialize, Serialize}; use std::path::PathBuf; -#[cfg(feature = "client")] -mod client; - -#[cfg(feature = "client")] -pub use client::*; - -#[cfg(feature = "serve")] -mod ws_receiver; - -#[cfg(feature = "serve")] -pub use ws_receiver::*; - /// A message the hot reloading server sends to the client #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] pub enum DevserverMsg { diff --git a/packages/hot-reload/Cargo.toml b/packages/devtools/Cargo.toml similarity index 97% rename from packages/hot-reload/Cargo.toml rename to packages/devtools/Cargo.toml index 6b805cc581..2ebf7c6220 100644 --- a/packages/hot-reload/Cargo.toml +++ b/packages/devtools/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "dioxus-hot-reload" +name = "dioxus-devtools" authors = ["Jonathan Kelley", "Evan Almloff"] version = { workspace = true } edition = "2021" diff --git a/packages/hot-reload/README.md b/packages/devtools/README.md similarity index 100% rename from packages/hot-reload/README.md rename to packages/devtools/README.md diff --git a/packages/hot-reload/src/client.rs b/packages/devtools/src/client.rs similarity index 100% rename from packages/hot-reload/src/client.rs rename to packages/devtools/src/client.rs diff --git a/packages/devtools/src/lib.rs b/packages/devtools/src/lib.rs new file mode 100644 index 0000000000..b384e94a41 --- /dev/null +++ b/packages/devtools/src/lib.rs @@ -0,0 +1,7 @@ +mod client; + +pub use client::*; + +mod ws_receiver; + +pub use ws_receiver::*; diff --git a/packages/hot-reload/src/ws_receiver.rs b/packages/devtools/src/ws_receiver.rs similarity index 100% rename from packages/hot-reload/src/ws_receiver.rs rename to packages/devtools/src/ws_receiver.rs diff --git a/packages/dioxus/Cargo.toml b/packages/dioxus/Cargo.toml index 8a485df151..a3eafa6cbd 100644 --- a/packages/dioxus/Cargo.toml +++ b/packages/dioxus/Cargo.toml @@ -28,7 +28,8 @@ dioxus-static-site-generation = { workspace = true, optional = true } dioxus-liveview = { workspace = true, optional = true } dioxus-ssr = { workspace = true, optional = true } manganis = { workspace = true, optional = true } -dioxus-hot-reload = { workspace = true, optional = true } +dioxus-devtools = { workspace = true, optional = true } + serde = { version = "1.0.136", optional = true } axum = { workspace = true, optional = true } @@ -36,7 +37,7 @@ axum = { workspace = true, optional = true } default = [ "macro", "html", - "hot-reload", + "devtools", "signals", "hooks", "launch", @@ -45,15 +46,14 @@ default = [ "asset", ] minimal = ["macro", "html", "signals", "hooks", "launch"] -# default = ["macro", "html", "hot-reload", "signals", "hooks", "launch", "mounted", "file_engine", "document", "asset"] signals = ["dep:dioxus-signals"] macro = ["dep:dioxus-core-macro"] html = ["dep:dioxus-html"] hooks = ["dep:dioxus-hooks"] -hot-reload = [ - "dep:dioxus-hot-reload", - "dioxus-web?/hot_reload", - "dioxus-fullstack?/hot-reload", +devtools = [ + "dioxus-web?/devtools", + "dioxus-fullstack?/devtools", + "dep:dioxus-devtools" ] mounted = ["dioxus-web?/mounted", "dioxus-html?/mounted"] file_engine = ["dioxus-web?/file_engine"] diff --git a/packages/fullstack/Cargo.toml b/packages/fullstack/Cargo.toml index e44eb15c5e..ecd542d72d 100644 --- a/packages/fullstack/Cargo.toml +++ b/packages/fullstack/Cargo.toml @@ -61,26 +61,24 @@ tower = { workspace = true, features = ["util"], optional = true } tower-layer = { version = "0.3.2", optional = true } parking_lot = { version = "0.12.1", features = ["send_guard"], optional = true } web-sys = { version = "0.3.61", optional = true, features = ["Window", "Document", "Element", "HtmlDocument", "Storage", "console"] } - clap = { version = "4.5.7", optional = true, features = ["derive"] } - aws-lc-rs = { version = "1.8.1", optional = true } +dioxus-devtools = { workspace = true, optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] tokio = { workspace = true, features = ["rt", "sync"], optional = true } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] -dioxus-hot-reload = { workspace = true, features = ["serve"] } tokio = { workspace = true, features = ["rt", "sync", "rt-multi-thread"], optional = true } [dev-dependencies] dioxus = { workspace = true, features = ["fullstack"] } [features] -default = ["hot-reload", "panic_hook", "document", "mounted"] +default = ["panic_hook", "document", "mounted", "devtools"] panic_hook = ["dioxus-web?/panic_hook"] -hot-reload = ["dioxus-web?/hot_reload", "dioxus-hot-reload/serve"] mounted = ["dioxus-web?/mounted"] +devtools = ["dep:dioxus-devtools", "dioxus-web?/devtools"] # file_engine = ["dioxus-web?/file_engine"] document = ["dioxus-web?/document"] web = ["dep:dioxus-web", "dep:web-sys"] diff --git a/packages/html/Cargo.toml b/packages/html/Cargo.toml index 33a1ee7e7f..7fffe8dc79 100644 --- a/packages/html/Cargo.toml +++ b/packages/html/Cargo.toml @@ -15,7 +15,6 @@ dioxus-core-types = { workspace = true } dioxus-rsx = { workspace = true, optional = true } dioxus-html-internal-macro = { workspace = true } -# generational-box = { workspace = true } serde = { version = "1", features = ["derive"], optional = true } serde_repr = { version = "0.1", optional = true } euclid = "0.22.7" @@ -27,6 +26,8 @@ tracing.workspace = true futures-channel = { workspace = true } +# dioxus-core = { workspace = true } +# generational-box = { workspace = true } # todo: we shouldn't have any wasm-bindgen dependencies in the html cratea # [target.'cfg(target_arch = "wasm32")'.dependencies] # wasm-bindgen = { workspace = true, optional = true } diff --git a/packages/html/src/events/animation.rs b/packages/html/src/events/animation.rs index 23f6219121..5bed6c6f53 100644 --- a/packages/html/src/events/animation.rs +++ b/packages/html/src/events/animation.rs @@ -1,4 +1,4 @@ -use dioxus_core::Event; +use dioxus_core_types::Event; pub type AnimationEvent = Event; diff --git a/packages/html/src/events/clipboard.rs b/packages/html/src/events/clipboard.rs index 265a747e9a..d159f05908 100644 --- a/packages/html/src/events/clipboard.rs +++ b/packages/html/src/events/clipboard.rs @@ -1,4 +1,4 @@ -use dioxus_core::Event; +use dioxus_core_types::Event; pub type ClipboardEvent = Event; diff --git a/packages/html/src/events/composition.rs b/packages/html/src/events/composition.rs index 4d161436ed..9c57f9642e 100644 --- a/packages/html/src/events/composition.rs +++ b/packages/html/src/events/composition.rs @@ -1,4 +1,4 @@ -use dioxus_core::Event; +use dioxus_core_types::Event; pub type CompositionEvent = Event; diff --git a/packages/html/src/events/drag.rs b/packages/html/src/events/drag.rs index 18df85c44a..8c00e952dd 100644 --- a/packages/html/src/events/drag.rs +++ b/packages/html/src/events/drag.rs @@ -2,7 +2,7 @@ use crate::geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenP use crate::input_data::{MouseButton, MouseButtonSet}; use crate::prelude::*; -use dioxus_core::Event; +use dioxus_core_types::Event; use keyboard_types::Modifiers; use crate::HasMouseData; diff --git a/packages/html/src/events/focus.rs b/packages/html/src/events/focus.rs index c5571e7ffd..f5335c35ac 100644 --- a/packages/html/src/events/focus.rs +++ b/packages/html/src/events/focus.rs @@ -1,4 +1,4 @@ -use dioxus_core::Event; +use dioxus_core_types::Event; pub type FocusEvent = Event; diff --git a/packages/html/src/events/form.rs b/packages/html/src/events/form.rs index 170adef7e7..d4751e1203 100644 --- a/packages/html/src/events/form.rs +++ b/packages/html/src/events/form.rs @@ -1,7 +1,7 @@ use crate::file_data::HasFileData; use std::{collections::HashMap, fmt::Debug, ops::Deref}; -use dioxus_core::Event; +use dioxus_core_types::Event; pub type FormEvent = Event; diff --git a/packages/html/src/events/image.rs b/packages/html/src/events/image.rs index 3c75edbdd1..55a5a3fda3 100644 --- a/packages/html/src/events/image.rs +++ b/packages/html/src/events/image.rs @@ -1,4 +1,4 @@ -use dioxus_core::Event; +use dioxus_core_types::Event; pub type ImageEvent = Event; pub struct ImageData { diff --git a/packages/html/src/events/keyboard.rs b/packages/html/src/events/keyboard.rs index 172991f976..90db7e4b2e 100644 --- a/packages/html/src/events/keyboard.rs +++ b/packages/html/src/events/keyboard.rs @@ -1,4 +1,4 @@ -use dioxus_core::Event; +use dioxus_core_types::Event; use keyboard_types::{Code, Key, Location, Modifiers}; use std::fmt::Debug; diff --git a/packages/html/src/events/media.rs b/packages/html/src/events/media.rs index f7ea3d6b7e..1df16d5b58 100644 --- a/packages/html/src/events/media.rs +++ b/packages/html/src/events/media.rs @@ -1,4 +1,4 @@ -use dioxus_core::Event; +use dioxus_core_types::Event; pub type MediaEvent = Event; pub struct MediaData { diff --git a/packages/html/src/events/mounted.rs b/packages/html/src/events/mounted.rs index cccf53c7a6..2722fba34b 100644 --- a/packages/html/src/events/mounted.rs +++ b/packages/html/src/events/mounted.rs @@ -127,7 +127,7 @@ impl MountedData { } } -use dioxus_core::Event; +use dioxus_core_types::Event; use crate::geometry::{PixelsRect, PixelsSize, PixelsVector2D}; diff --git a/packages/html/src/events/mouse.rs b/packages/html/src/events/mouse.rs index ed50c8ae7c..65d10bd77e 100644 --- a/packages/html/src/events/mouse.rs +++ b/packages/html/src/events/mouse.rs @@ -1,7 +1,7 @@ use crate::geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint}; use crate::input_data::{MouseButton, MouseButtonSet}; use crate::prelude::*; -use dioxus_core::Event; +use dioxus_core_types::Event; use keyboard_types::Modifiers; pub type MouseEvent = Event; diff --git a/packages/html/src/events/pointer.rs b/packages/html/src/events/pointer.rs index 312f14879f..f25df4c2d9 100644 --- a/packages/html/src/events/pointer.rs +++ b/packages/html/src/events/pointer.rs @@ -1,4 +1,4 @@ -use dioxus_core::Event; +use dioxus_core_types::Event; use keyboard_types::Modifiers; use crate::{geometry::*, input_data::*, prelude::*}; diff --git a/packages/html/src/events/resize.rs b/packages/html/src/events/resize.rs index e489fc9484..6995b88437 100644 --- a/packages/html/src/events/resize.rs +++ b/packages/html/src/events/resize.rs @@ -126,7 +126,7 @@ pub trait HasResizeData: std::any::Any { fn as_any(&self) -> &dyn std::any::Any; } -use dioxus_core::Event; +use dioxus_core_types::Event; use crate::geometry::PixelsSize; diff --git a/packages/html/src/events/scroll.rs b/packages/html/src/events/scroll.rs index 0ee2a2de72..ffff928c9e 100644 --- a/packages/html/src/events/scroll.rs +++ b/packages/html/src/events/scroll.rs @@ -1,4 +1,4 @@ -use dioxus_core::Event; +use dioxus_core_types::Event; pub type ScrollEvent = Event; diff --git a/packages/html/src/events/selection.rs b/packages/html/src/events/selection.rs index 0f23a4ad2d..97d3af2eec 100644 --- a/packages/html/src/events/selection.rs +++ b/packages/html/src/events/selection.rs @@ -1,4 +1,4 @@ -use dioxus_core::Event; +use dioxus_core_types::Event; pub type SelectionEvent = Event; diff --git a/packages/html/src/events/toggle.rs b/packages/html/src/events/toggle.rs index c8d352ed9d..14dcdd1e50 100644 --- a/packages/html/src/events/toggle.rs +++ b/packages/html/src/events/toggle.rs @@ -1,4 +1,4 @@ -use dioxus_core::Event; +use dioxus_core_types::Event; pub type ToggleEvent = Event; diff --git a/packages/html/src/events/touch.rs b/packages/html/src/events/touch.rs index 999c9717bf..8a0229f3c1 100644 --- a/packages/html/src/events/touch.rs +++ b/packages/html/src/events/touch.rs @@ -1,4 +1,4 @@ -use dioxus_core::Event; +use dioxus_core_types::Event; use keyboard_types::Modifiers; use crate::geometry::*; diff --git a/packages/html/src/events/transition.rs b/packages/html/src/events/transition.rs index 18863318dd..04aaf8a5c1 100644 --- a/packages/html/src/events/transition.rs +++ b/packages/html/src/events/transition.rs @@ -1,4 +1,4 @@ -use dioxus_core::Event; +use dioxus_core_types::Event; pub type TransitionEvent = Event; diff --git a/packages/html/src/events/wheel.rs b/packages/html/src/events/wheel.rs index 0a51be8176..41f0e026d4 100644 --- a/packages/html/src/events/wheel.rs +++ b/packages/html/src/events/wheel.rs @@ -1,4 +1,4 @@ -use dioxus_core::Event; +use dioxus_core_types::Event; use std::fmt::Formatter; use crate::geometry::*; diff --git a/packages/liveview/Cargo.toml b/packages/liveview/Cargo.toml index 4a002c4e5d..30cd4fc0de 100644 --- a/packages/liveview/Cargo.toml +++ b/packages/liveview/Cargo.toml @@ -28,7 +28,7 @@ rustc-hash = { workspace = true } dioxus-core = { workspace = true, features = ["serialize"] } dioxus-core-types = { workspace = true } dioxus-document = { workspace = true } -dioxus-hot-reload = { workspace = true, optional = true, features = ["serve", "client"] } +dioxus-devtools = { workspace = true, optional = true } dioxus-interpreter-js = { workspace = true, features = ["binary-protocol"] } generational-box = { workspace = true } @@ -43,10 +43,10 @@ tower = { workspace = true } dioxus = { workspace = true } [features] -default = ["hot-reload", "multi-thread"] +default = ["devtools", "multi-thread"] axum = ["dep:axum"] multi-thread = ["tokio/rt-multi-thread"] -hot-reload = ["dep:dioxus-hot-reload"] +devtools = ["dep:dioxus-devtools"] [[example]] name = "axum" diff --git a/packages/liveview/src/pool.rs b/packages/liveview/src/pool.rs index 2f15de86b6..fa9ff570fd 100644 --- a/packages/liveview/src/pool.rs +++ b/packages/liveview/src/pool.rs @@ -117,7 +117,7 @@ impl LiveViewSocket for S where /// /// You might need to transform the error types of the web backend into the LiveView error type. pub async fn run(mut vdom: VirtualDom, ws: impl LiveViewSocket) -> Result<(), LiveViewError> { - #[cfg(all(feature = "hot-reload", debug_assertions))] + #[cfg(all(feature = "devtools", debug_assertions))] let mut hot_reload_rx = { let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); dioxus_hot_reload::connect(move |template| _ = tx.send(template)); @@ -157,9 +157,9 @@ pub async fn run(mut vdom: VirtualDom, ws: impl LiveViewSocket) -> Result<(), Li } loop { - #[cfg(all(feature = "hot-reload", debug_assertions))] + #[cfg(all(feature = "devtools", debug_assertions))] let hot_reload_wait = hot_reload_rx.recv(); - #[cfg(not(all(feature = "hot-reload", debug_assertions)))] + #[cfg(not(all(feature = "devtools", debug_assertions)))] let hot_reload_wait: std::future::Pending> = std::future::pending(); tokio::select! { @@ -213,7 +213,7 @@ pub async fn run(mut vdom: VirtualDom, ws: impl LiveViewSocket) -> Result<(), Li } Some(msg) = hot_reload_wait => { - #[cfg(all(feature = "hot-reload", debug_assertions))] + #[cfg(all(feature = "devtools", debug_assertions))] match msg{ DevserverMsg::HotReload(msg)=> { dioxus_hot_reload::apply_changes(&mut vdom, &msg); @@ -228,7 +228,7 @@ pub async fn run(mut vdom: VirtualDom, ws: impl LiveViewSocket) -> Result<(), Li // Maybe we could just binary patch ourselves in place without losing window state? }, } - #[cfg(not(all(feature = "hot-reload", debug_assertions)))] + #[cfg(not(all(feature = "devtools", debug_assertions)))] let () = msg; } } diff --git a/packages/manganis-macro/src/font.rs b/packages/manganis-macro/src/font.rs new file mode 100644 index 0000000000..3bbac2412e --- /dev/null +++ b/packages/manganis-macro/src/font.rs @@ -0,0 +1,173 @@ +use manganis_common::{AssetType, CssOptions, ManganisSupportError, ResourceAsset}; +use quote::{quote, ToTokens}; +use syn::{bracketed, parenthesized, parse::Parse}; + +use crate::{generate_link_section, resource::ResourceAssetParser}; + +#[derive(Default)] +struct FontFamilies { + families: Vec, +} + +impl Parse for FontFamilies { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let inside; + bracketed!(inside in input); + let array = + syn::punctuated::Punctuated::::parse_separated_nonempty( + &inside, + )?; + Ok(FontFamilies { + families: array.into_iter().map(|f| f.value()).collect(), + }) + } +} + +#[derive(Default)] +struct FontWeights { + weights: Vec, +} + +impl Parse for FontWeights { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let inside; + bracketed!(inside in input); + let array = + syn::punctuated::Punctuated::::parse_separated_nonempty( + &inside, + )?; + Ok(FontWeights { + weights: array + .into_iter() + .map(|f| f.base10_parse().unwrap()) + .collect(), + }) + } +} + +struct ParseFontOptions { + families: FontFamilies, + weights: FontWeights, + text: Option, + display: Option, +} + +impl ParseFontOptions { + fn url(&self) -> String { + let mut segments = Vec::new(); + + let families: Vec<_> = self + .families + .families + .iter() + .map(|f| f.replace(' ', "+")) + .collect(); + if !families.is_empty() { + segments.push(format!("family={}", families.join("&"))); + } + + let weights: Vec<_> = self.weights.weights.iter().map(|w| w.to_string()).collect(); + if !weights.is_empty() { + segments.push(format!("weight={}", weights.join(","))); + } + + if let Some(text) = &self.text { + segments.push(format!("text={}", text.replace(' ', "+"))); + } + + if let Some(display) = &self.display { + segments.push(format!("display={}", display.replace(' ', "+"))); + } + + let query = if segments.is_empty() { + String::new() + } else { + format!("?{}", segments.join("&")) + }; + + format!("https://fonts.googleapis.com/css2{}", query) + } +} + +impl Parse for ParseFontOptions { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let mut families = None; + let mut weights = None; + let mut text = None; + let mut display = None; + loop { + if input.is_empty() { + break; + } + let _ = input.parse::()?; + let ident = input.parse::()?; + let inside; + parenthesized!(inside in input); + match ident.to_string().to_lowercase().as_str() { + "families" => { + families = Some(inside.parse::()?); + } + "weights" => { + weights = Some(inside.parse::()?); + } + "text" => { + text = Some(inside.parse::()?.value()); + } + "display" => { + display = Some(inside.parse::()?.value()); + } + _ => { + return Err(syn::Error::new( + proc_macro2::Span::call_site(), + format!("Unknown font option: {ident}. Supported options are families, weights, text, display"), + )) + } + } + } + + Ok(ParseFontOptions { + families: families.unwrap_or_default(), + weights: weights.unwrap_or_default(), + text, + display, + }) + } +} + +pub struct FontAssetParser { + asset: ResourceAsset, +} + +impl Parse for FontAssetParser { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let inside; + parenthesized!(inside in input); + if !inside.is_empty() { + return Err(syn::Error::new( + proc_macro2::Span::call_site(), + "Font assets do not support paths. Please use file() if you want to import a local font file", + )); + } + + let options = input.parse::()?; + + let url = options.url(); + let asset: ResourceAsset = match ResourceAsset::parse_file(&url) { + Ok(url) => url, + Err(e) => { + return Err(syn::Error::new( + proc_macro2::Span::call_site(), + format!("Failed to parse url: {url:?}\n{e}"), + )) + } + }; + + Ok(FontAssetParser { asset }) + } +} + +impl ToTokens for FontAssetParser { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + ResourceAssetParser::to_ref_tokens(&self.asset, tokens) + } +} diff --git a/packages/static-generation/Cargo.toml b/packages/static-generation/Cargo.toml index cd3b79ad07..32af0183d3 100644 --- a/packages/static-generation/Cargo.toml +++ b/packages/static-generation/Cargo.toml @@ -13,11 +13,11 @@ resolver = "2" [dependencies] dioxus-fullstack = { workspace = true } dioxus-lib.workspace = true -dioxus-router = { workspace = true, features = ["fullstack"]} +dioxus-router = { workspace = true, features = ["fullstack"] } dioxus-ssr = { workspace = true, features = ["incremental"], optional = true } axum = { workspace = true, features = ["ws", "macros"], optional = true } tower-http = { workspace = true, features = ["fs"], optional = true } -dioxus-hot-reload = { workspace = true, features = ["serve"], optional = true } +dioxus-devtools = { workspace = true, optional = true } # dioxus-cli-config = { workspace = true, features = ["read-config"], optional = true } dioxus-web = { workspace = true, features = ["hydrate"], optional = true } tokio = { workspace = true, optional = true } @@ -31,7 +31,17 @@ criterion = { workspace = true } [features] default = [] -server = ["dioxus-fullstack/server", "dioxus-router/ssr", "dep:dioxus-ssr", "dep:tokio", "dep:http", "dep:axum", "dep:tower-http", "dep:dioxus-hot-reload", "dep:tower"] +server = [ + "dioxus-fullstack/server", + "dioxus-router/ssr", + "dep:dioxus-ssr", + "dep:tokio", + "dep:http", + "dep:axum", + "dep:tower-http", + "dep:dioxus-devtools", + "dep:tower", +] # server = ["dioxus-fullstack/server", "dioxus-router/ssr", "dep:dioxus-ssr", "dep:tokio", "dep:http", "dep:axum", "dep:tower-http", "dep:dioxus-hot-reload", "dep:dioxus-cli-config", "dep:tower"] web = ["dioxus-fullstack/web", "dioxus-router/web", "dep:dioxus-web"] diff --git a/packages/web/Cargo.toml b/packages/web/Cargo.toml index b1c214f54e..00b07a6e49 100644 --- a/packages/web/Cargo.toml +++ b/packages/web/Cargo.toml @@ -15,7 +15,7 @@ dioxus-core-types = { workspace = true } dioxus-core = { workspace = true } dioxus-html = { workspace = true } dioxus-document = { workspace = true } -dioxus-hot-reload = { workspace = true, features = ["client"] } +dioxus-devtools = { workspace = true } dioxus-signals = { workspace = true } dioxus-interpreter-js = { workspace = true, features = [ "minimal_bindings", From 9acbb4d8fcdb8fea160e1aeeb0d5c2d2a4e0a3b9 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 21 Aug 2024 22:53:44 -0700 Subject: [PATCH 034/139] rename hotreload to devtools --- Cargo.lock | 51 ++++++++--------- packages/cli/src/serve/output.rs | 2 +- packages/cli/src/serve/server.rs | 2 +- packages/cli/src/serve/watcher.rs | 2 +- packages/core/src/scope_context.rs | 4 +- packages/desktop/Cargo.toml | 3 +- packages/desktop/src/app.rs | 8 +-- packages/desktop/src/ipc.rs | 2 +- packages/devtools-types/Cargo.toml | 1 + packages/devtools/Cargo.toml | 11 +--- packages/devtools/src/client.rs | 25 --------- packages/devtools/src/lib.rs | 55 ++++++++++++++++-- packages/devtools/src/ws_receiver.rs | 84 ---------------------------- packages/dioxus/Cargo.toml | 2 +- packages/liveview/src/pool.rs | 6 +- packages/web/Cargo.toml | 9 ++- packages/web/src/event.rs | 4 +- packages/web/src/hot_reload.rs | 2 +- packages/web/src/lib.rs | 2 +- 19 files changed, 104 insertions(+), 171 deletions(-) delete mode 100644 packages/devtools/src/client.rs delete mode 100644 packages/devtools/src/ws_receiver.rs diff --git a/Cargo.lock b/Cargo.lock index 39d90a722b..91c4649a0a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2296,10 +2296,10 @@ dependencies = [ "dioxus-core-macro", "dioxus-core-types", "dioxus-desktop", + "dioxus-devtools", "dioxus-document", "dioxus-fullstack", "dioxus-hooks", - "dioxus-hot-reload", "dioxus-html", "dioxus-liveview", "dioxus-mobile", @@ -2371,7 +2371,6 @@ dependencies = [ "dioxus-check", "dioxus-core", "dioxus-core-types", - "dioxus-hot-reload", "dioxus-html", "dioxus-rsx", "dirs", @@ -2488,9 +2487,9 @@ dependencies = [ "core-foundation 0.10.0", "dioxus", "dioxus-core", + "dioxus-devtools", "dioxus-document", "dioxus-hooks", - "dioxus-hot-reload", "dioxus-html", "dioxus-interpreter-js", "dioxus-signals", @@ -2525,9 +2524,26 @@ dependencies = [ ] [[package]] -name = "dioxus-devserver-types" +name = "dioxus-devtools" version = "0.6.0-alpha.2" dependencies = [ + "dioxus-core", + "dioxus-devtools-types", + "dioxus-html", + "dioxus-rsx", + "dioxus-signals", + "serde", + "serde_json", + "tokio", + "tracing", + "warnings", +] + +[[package]] +name = "dioxus-devtools-types" +version = "0.6.0-alpha.2" +dependencies = [ + "dioxus-core", "serde", ] @@ -2588,8 +2604,8 @@ dependencies = [ "dioxus", "dioxus-core-types", "dioxus-desktop", + "dioxus-devtools", "dioxus-document", - "dioxus-hot-reload", "dioxus-interpreter-js", "dioxus-lib", "dioxus-mobile", @@ -2639,25 +2655,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "dioxus-hot-reload" -version = "0.6.0-alpha.2" -dependencies = [ - "dioxus-core", - "dioxus-html", - "dioxus-rsx", - "dioxus-signals", - "serde", - "serde_json", - "tokio", - "tracing", - "warnings", -] - -[[package]] -name = "dioxus-hotreload-receiver" -version = "0.6.0-alpha.2" - [[package]] name = "dioxus-html" version = "0.6.0-alpha.2" @@ -2732,8 +2729,8 @@ dependencies = [ "dioxus", "dioxus-core", "dioxus-core-types", + "dioxus-devtools", "dioxus-document", - "dioxus-hot-reload", "dioxus-html", "dioxus-interpreter-js", "env_logger", @@ -2927,8 +2924,8 @@ dependencies = [ "axum 0.7.5", "criterion", "dioxus", + "dioxus-devtools", "dioxus-fullstack", - "dioxus-hot-reload", "dioxus-lib", "dioxus-router", "dioxus-ssr", @@ -2958,8 +2955,8 @@ dependencies = [ "dioxus", "dioxus-core", "dioxus-core-types", + "dioxus-devtools", "dioxus-document", - "dioxus-hot-reload", "dioxus-html", "dioxus-interpreter-js", "dioxus-signals", diff --git a/packages/cli/src/serve/output.rs b/packages/cli/src/serve/output.rs index 1d0e6d1408..a5c2270a6b 100644 --- a/packages/cli/src/serve/output.rs +++ b/packages/cli/src/serve/output.rs @@ -18,7 +18,7 @@ use crossterm::{ tty::IsTty, ExecutableCommand, }; -use dioxus_hot_reload::ClientMsg; +use dioxus_devtools::ClientMsg; use futures_util::{future::select_all, Future, FutureExt, StreamExt}; use ratatui::{prelude::*, widgets::*, TerminalOptions, Viewport}; use std::{ diff --git a/packages/cli/src/serve/server.rs b/packages/cli/src/serve/server.rs index bac6a7535e..9bcb1487ca 100644 --- a/packages/cli/src/serve/server.rs +++ b/packages/cli/src/serve/server.rs @@ -19,7 +19,7 @@ use axum::{ Router, }; use axum_server::tls_rustls::RustlsConfig; -use dioxus_hot_reload::{DevserverMsg, HotReloadMsg}; +use dioxus_devtools::{DevserverMsg, HotReloadMsg}; use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender}; use futures_util::stream; use futures_util::{stream::FuturesUnordered, StreamExt}; diff --git a/packages/cli/src/serve/watcher.rs b/packages/cli/src/serve/watcher.rs index 0e97a01c0e..75d180b56e 100644 --- a/packages/cli/src/serve/watcher.rs +++ b/packages/cli/src/serve/watcher.rs @@ -4,7 +4,7 @@ use std::{fs, path::PathBuf, time::Duration}; use super::hot_reloading_file_map::HotreloadError; use crate::serve::hot_reloading_file_map::FileMap; use crate::{cli::serve::Serve, dioxus_crate::DioxusCrate}; -use dioxus_hot_reload::HotReloadMsg; +use dioxus_devtools::HotReloadMsg; use dioxus_html::HtmlCtx; use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender}; use futures_util::StreamExt; diff --git a/packages/core/src/scope_context.rs b/packages/core/src/scope_context.rs index 4fae8c1e47..5d740f20d0 100644 --- a/packages/core/src/scope_context.rs +++ b/packages/core/src/scope_context.rs @@ -556,13 +556,13 @@ impl ScopeId { /// fn Component() -> Element { /// let request = spawn(async move { /// match reqwest::get("https://api.example.com").await { - /// Ok(_) => todo!(), + /// Ok(_) => unimplemented!(), /// // You can explicitly throw an error into a scope with throw_error /// Err(err) => ScopeId::APP.throw_error(err) /// } /// }); /// - /// todo!() + /// unimplemented!() /// } /// ``` pub fn throw_error(self, error: impl Into + 'static) { diff --git a/packages/desktop/Cargo.toml b/packages/desktop/Cargo.toml index 77ea7df452..2399f54e1b 100644 --- a/packages/desktop/Cargo.toml +++ b/packages/desktop/Cargo.toml @@ -73,8 +73,7 @@ default = ["tokio_runtime", "wry/objc-exception", "devtools"] tokio_runtime = ["dep:tokio"] fullscreen = ["wry/fullscreen"] transparent = ["wry/transparent"] -devtools = ["wry/devtools"] -dev = ["dep:dioxus-devtools"] +devtools = ["wry/devtools", "dep:dioxus-devtools"] gnu = [] [package.metadata.docs.rs] diff --git a/packages/desktop/src/app.rs b/packages/desktop/src/app.rs index 64be2996b9..cf34556236 100644 --- a/packages/desktop/src/app.rs +++ b/packages/desktop/src/app.rs @@ -140,7 +140,7 @@ impl App { let proxy = self.shared.proxy.clone(); tokio::task::spawn(async move { - // let Some(Ok(mut receiver)) = dioxus_hot_reload::NativeReceiver::create_from_cli().await + // let Some(Ok(mut receiver)) = dioxus_devtools::NativeReceiver::create_from_cli().await // else { // return; // }; @@ -267,13 +267,13 @@ impl App { } #[cfg(all(feature = "devtools", debug_assertions,))] - pub fn handle_hot_reload_msg(&mut self, msg: dioxus_hot_reload::DevserverMsg) { - use dioxus_hot_reload::DevserverMsg; + pub fn handle_hot_reload_msg(&mut self, msg: dioxus_devtools::DevserverMsg) { + use dioxus_devtools::DevserverMsg; match msg { DevserverMsg::HotReload(hr_msg) => { for webview in self.webviews.values_mut() { - dioxus_hot_reload::apply_changes(&mut webview.dom, &hr_msg); + dioxus_devtools::apply_changes(&mut webview.dom, &hr_msg); webview.poll_vdom(); } diff --git a/packages/desktop/src/ipc.rs b/packages/desktop/src/ipc.rs index 4d46f99776..e78114f688 100644 --- a/packages/desktop/src/ipc.rs +++ b/packages/desktop/src/ipc.rs @@ -19,7 +19,7 @@ pub enum UserWindowEvent { /// Handle a hotreload event, basically telling us to update our templates #[cfg(all(feature = "devtools", debug_assertions))] - HotReloadEvent(dioxus_hot_reload::DevserverMsg), + HotReloadEvent(dioxus_devtools::DevserverMsg), /// Create a new window NewWindow, diff --git a/packages/devtools-types/Cargo.toml b/packages/devtools-types/Cargo.toml index 097fcb05c1..de88a9b9e4 100644 --- a/packages/devtools-types/Cargo.toml +++ b/packages/devtools-types/Cargo.toml @@ -5,3 +5,4 @@ version.workspace = true [dependencies] serde = { workspace = true, features = ["derive"] } +dioxus-core = { workspace = true } diff --git a/packages/devtools/Cargo.toml b/packages/devtools/Cargo.toml index 2ebf7c6220..a26dafb54a 100644 --- a/packages/devtools/Cargo.toml +++ b/packages/devtools/Cargo.toml @@ -11,10 +11,10 @@ keywords = ["dom", "ui", "gui", "react", "hot-reloading"] [dependencies] dioxus-rsx = { workspace = true } +dioxus-html = { workspace = true } +dioxus-signals = { workspace = true } dioxus-core = { workspace = true, features = ["serialize"] } -dioxus-html = { workspace = true, optional = true } -dioxus-signals = { workspace = true, optional = true } - +dioxus-devtools-types = { workspace = true } serde = { version = "1", features = ["derive"] } # hot reloading serve @@ -25,10 +25,5 @@ warnings.workspace = true tokio = { workspace = true, features = ["full"] } serde_json = "1.0.91" -[features] -default = [] -client = ["dep:dioxus-signals"] -serve = [] - [package.metadata.docs.rs] cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"] diff --git a/packages/devtools/src/client.rs b/packages/devtools/src/client.rs deleted file mode 100644 index b8d4ccde7c..0000000000 --- a/packages/devtools/src/client.rs +++ /dev/null @@ -1,25 +0,0 @@ -use crate::HotReloadMsg; -use dioxus_core::{ScopeId, VirtualDom}; -use dioxus_signals::Writable; -use warnings::Warning; - -/// Applies template and literal changes to the VirtualDom -/// -/// Assets need to be handled by the renderer. -pub fn apply_changes(dom: &VirtualDom, msg: &HotReloadMsg) { - dom.runtime().on_scope(ScopeId::ROOT, || { - let ctx = dioxus_signals::get_global_context(); - - for template in &msg.templates { - let id = &template.location; - let value = template.template.clone(); - if let Some(mut signal) = ctx.get_signal_with_key(id) { - dioxus_signals::warnings::signal_read_and_write_in_reactive_scope::allow(|| { - dioxus_signals::warnings::signal_write_in_component_body::allow(|| { - signal.set(Some(value)); - }); - }); - } - } - }); -} diff --git a/packages/devtools/src/lib.rs b/packages/devtools/src/lib.rs index b384e94a41..3915c93896 100644 --- a/packages/devtools/src/lib.rs +++ b/packages/devtools/src/lib.rs @@ -1,7 +1,54 @@ -mod client; +pub use dioxus_devtools_types::*; -pub use client::*; +use dioxus_core::{ScopeId, VirtualDom}; +use dioxus_signals::Writable; +use warnings::Warning; -mod ws_receiver; +/// Applies template and literal changes to the VirtualDom +/// +/// Assets need to be handled by the renderer. +pub fn apply_changes(dom: &VirtualDom, msg: &HotReloadMsg) { + dom.runtime().on_scope(ScopeId::ROOT, || { + let ctx = dioxus_signals::get_global_context(); -pub use ws_receiver::*; + for template in &msg.templates { + let id = &template.location; + let value = template.template.clone(); + if let Some(mut signal) = ctx.get_signal_with_key(id) { + dioxus_signals::warnings::signal_read_and_write_in_reactive_scope::allow(|| { + dioxus_signals::warnings::signal_write_in_component_body::allow(|| { + signal.set(Some(value)); + }); + }); + } + } + }); +} + +/// Connect to the devserver and handle its messages +pub fn connect(mut callback: impl FnMut(DevserverMsg) + Send + 'static) { + // Hi! + // + // yes, we read-raw from a tcp socket + // don't think about it too much :) + // + // we just don't want to bring in tls + tokio for just hotreloading + std::thread::spawn(move || { + let connect = std::net::TcpStream::connect("127.0.0.1:8080"); + let Ok(mut stream) = connect else { + return; + }; + + loop {} + + // let mut buf = [0; 1024]; + // loop { + // let len = stream.read(&mut buf).unwrap(); + // if len == 0 { + // break; + // } + // let msg = String::from_utf8_lossy(&buf[..len]); + // callback(serde_json::from_str(&msg).unwrap()); + // } + }); +} diff --git a/packages/devtools/src/ws_receiver.rs b/packages/devtools/src/ws_receiver.rs deleted file mode 100644 index e8866d02d9..0000000000 --- a/packages/devtools/src/ws_receiver.rs +++ /dev/null @@ -1,84 +0,0 @@ -use crate::DevserverMsg; -use std::io::Read; - -pub fn connect(mut callback: impl FnMut(DevserverMsg) + Send + 'static) { - // Hi! - // - // yes, we read-raw from a tcp socket - // don't think about it too much :) - // - // we just don't want to bring in tls + tokio for just hotreloading - std::thread::spawn(move || { - let connect = std::net::TcpStream::connect("127.0.0.1:8080"); - let Ok(mut stream) = connect else { - return; - }; - - loop {} - - // let mut buf = [0; 1024]; - // loop { - // let len = stream.read(&mut buf).unwrap(); - // if len == 0 { - // break; - // } - // let msg = String::from_utf8_lossy(&buf[..len]); - // callback(serde_json::from_str(&msg).unwrap()); - // } - }); -} - -/// A receiver for messages from the devserver -/// -/// Calling `next` will watch the channel for the next valid message from the devserver -pub struct NativeReceiver { - // socket: WebSocketStream>, -} - -impl NativeReceiver { - // /// Connect to the devserver - // async fn create(url: String) -> TtResult { - // let (socket, _ws) = tokio_tungstenite::connect_async(&url).await.unwrap(); - // Ok(Self { socket }) - // } - - // /// Connect to the devserver with an address from the CLI. Returns None if the current application was not run with the CLI - // pub async fn create_from_cli() -> Option> { - // // todo: allow external configuration of this address for use by mobile when launching - // // from the ios-deploy tooling. This could be stored in a config file= that gets - // // uploaded to the device. - // let addr = - // dioxus_cli_config::RuntimeCLIArguments::from_cli().map(|args| args.cli_address())?; - // Some(Self::create(format!("ws://{addr}/_dioxus")).await) - // } - - // /// Wait for the next message from the devserver - // /// - // /// Returns None when the connection is closed or socket.next() returns None - // pub async fn next(&mut self) -> Option> { - // loop { - // let res = self.socket.next().await?; - - // match res { - // Ok(res) => match res { - // Message::Text(text) => { - // // let leaked: &'static str = Box::leak(text.into_boxed_str()); - // let msg = serde_json::from_str::(&text); - // if let Ok(msg) = msg { - // return Some(Ok(msg)); - // } - // } - // // send a pong - // Message::Ping(_) => { - // let _ = self.socket.send(Message::Pong(vec![])).await; - // } - // Message::Close(_) => return None, - // Message::Binary(_) => {} - // Message::Pong(_) => {} - // Message::Frame(_) => {} - // }, - // Err(e) => return Some(Err(e)), - // }; - // } - // } -} diff --git a/packages/dioxus/Cargo.toml b/packages/dioxus/Cargo.toml index a3eafa6cbd..ae829d5645 100644 --- a/packages/dioxus/Cargo.toml +++ b/packages/dioxus/Cargo.toml @@ -56,7 +56,7 @@ devtools = [ "dep:dioxus-devtools" ] mounted = ["dioxus-web?/mounted", "dioxus-html?/mounted"] -file_engine = ["dioxus-web?/file_engine"] +file-engine = ["dioxus-web?/file-engine"] asset = ["dep:manganis"] document = ["dioxus-web?/document"] diff --git a/packages/liveview/src/pool.rs b/packages/liveview/src/pool.rs index fa9ff570fd..6e4ba69d26 100644 --- a/packages/liveview/src/pool.rs +++ b/packages/liveview/src/pool.rs @@ -6,7 +6,7 @@ use crate::{ LiveViewError, }; use dioxus_core::prelude::*; -use dioxus_hot_reload::DevserverMsg; +use dioxus_devtools::DevserverMsg; use dioxus_html::{EventData, HtmlEvent, PlatformEventData}; use dioxus_interpreter_js::MutationState; use futures_util::{pin_mut, SinkExt, StreamExt}; @@ -120,7 +120,7 @@ pub async fn run(mut vdom: VirtualDom, ws: impl LiveViewSocket) -> Result<(), Li #[cfg(all(feature = "devtools", debug_assertions))] let mut hot_reload_rx = { let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); - dioxus_hot_reload::connect(move |template| _ = tx.send(template)); + dioxus_devtools::connect(move |template| _ = tx.send(template)); rx }; @@ -216,7 +216,7 @@ pub async fn run(mut vdom: VirtualDom, ws: impl LiveViewSocket) -> Result<(), Li #[cfg(all(feature = "devtools", debug_assertions))] match msg{ DevserverMsg::HotReload(msg)=> { - dioxus_hot_reload::apply_changes(&mut vdom, &msg); + dioxus_devtools::apply_changes(&mut vdom, &msg); } DevserverMsg::Shutdown => { std::process::exit(0); diff --git a/packages/web/Cargo.toml b/packages/web/Cargo.toml index 00b07a6e49..9134751cc0 100644 --- a/packages/web/Cargo.toml +++ b/packages/web/Cargo.toml @@ -80,15 +80,18 @@ lazy-js-bundle = { workspace = true } [features] default = ["panic_hook", "full"] full = [ - + "devtools", + "mounted", + "file-engine", + "document" ] # default = ["panic_hook", "mounted", "file_engine", "hot_reload", "document"] panic_hook = ["dep:console_error_panic_hook"] hydrate = ["web-sys/Comment", "ciborium", "dep:serde"] mounted = ["web-sys/Element", "dioxus-html/mounted"] -file_engine = ["dioxus-html/file-engine"] -hot_reload = [ +file-engine = ["dioxus-html/file-engine"] +devtools = [ "web-sys/MessageEvent", "web-sys/WebSocket", "web-sys/Location", diff --git a/packages/web/src/event.rs b/packages/web/src/event.rs index 99162e0cb1..eec63078b4 100644 --- a/packages/web/src/event.rs +++ b/packages/web/src/event.rs @@ -470,7 +470,7 @@ impl HasFormData for WebFormData { } impl HasFileData for WebFormData { - #[cfg(feature = "file_engine")] + #[cfg(feature = "file-engine")] fn files(&self) -> Option> { use crate::bindings::WebFileEngine; @@ -561,7 +561,7 @@ impl InteractionLocation for WebDragData { } impl HasFileData for WebDragData { - #[cfg(feature = "file_engine")] + #[cfg(feature = "file-engine")] fn files(&self) -> Option> { use crate::bindings::WebFileEngine; diff --git a/packages/web/src/hot_reload.rs b/packages/web/src/hot_reload.rs index 28bd82f23d..59fd12b46e 100644 --- a/packages/web/src/hot_reload.rs +++ b/packages/web/src/hot_reload.rs @@ -7,8 +7,8 @@ use std::fmt::Display; use std::time::Duration; use dioxus_core::ScopeId; +use dioxus_devtools::{DevserverMsg, HotReloadMsg}; use dioxus_document::eval; -use dioxus_hot_reload::{DevserverMsg, HotReloadMsg}; use futures_channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender}; use js_sys::JsString; use wasm_bindgen::JsCast; diff --git a/packages/web/src/lib.rs b/packages/web/src/lib.rs index 1df8caccde..193ce726d5 100644 --- a/packages/web/src/lib.rs +++ b/packages/web/src/lib.rs @@ -179,7 +179,7 @@ pub async fn run(mut virtual_dom: VirtualDom, web_config: Config) -> ! { #[cfg(all(feature = "hot_reload", debug_assertions))] if let Some(hr_msg) = template { // Replace all templates - dioxus_hot_reload::apply_changes(&mut virtual_dom, &hr_msg); + dioxus_devtools::apply_changes(&mut virtual_dom, &hr_msg); if !hr_msg.assets.is_empty() { crate::hot_reload::invalidate_browser_asset_cache(); From 18606116d14979cb341bab7a0c23340e17758c08 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 21 Aug 2024 23:08:11 -0700 Subject: [PATCH 035/139] really clean up the html crate --- Cargo.lock | 61 +++++-------------- Cargo.toml | 4 +- packages/fullstack/Cargo.toml | 3 +- packages/isrg/Cargo.toml | 12 ++++ .../src/incremental => isrg/src}/config.rs | 4 +- .../src/incremental => isrg/src}/freshness.rs | 0 .../src/incremental => isrg/src}/fs_cache.rs | 0 .../incremental/mod.rs => isrg/src/lib.rs} | 0 .../incremental => isrg/src}/memory_cache.rs | 0 packages/ssr/Cargo.toml | 31 +--------- packages/ssr/src/lib.rs | 2 - packages/static-generation/Cargo.toml | 4 +- 12 files changed, 36 insertions(+), 85 deletions(-) create mode 100644 packages/isrg/Cargo.toml rename packages/{ssr/src/incremental => isrg/src}/config.rs (95%) rename packages/{ssr/src/incremental => isrg/src}/freshness.rs (100%) rename packages/{ssr/src/incremental => isrg/src}/fs_cache.rs (100%) rename packages/{ssr/src/incremental/mod.rs => isrg/src/lib.rs} (100%) rename packages/{ssr/src/incremental => isrg/src}/memory_cache.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 91c4649a0a..6ff702aa21 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -233,37 +233,6 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" -[[package]] -name = "argh" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7af5ba06967ff7214ce4c7419c7d185be7ecd6cc4965a8f6e1d8ce0398aad219" -dependencies = [ - "argh_derive", - "argh_shared", -] - -[[package]] -name = "argh_derive" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56df0aeedf6b7a2fc67d06db35b09684c3e8da0c95f8f27685cb17e08413d87a" -dependencies = [ - "argh_shared", - "proc-macro2", - "quote", - "syn 2.0.74", -] - -[[package]] -name = "argh_shared" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5693f39141bda5760ecc4111ab08da40565d1771038c4a0250f03457ec707531" -dependencies = [ - "serde", -] - [[package]] name = "arrayvec" version = "0.7.4" @@ -2607,6 +2576,7 @@ dependencies = [ "dioxus-devtools", "dioxus-document", "dioxus-interpreter-js", + "dioxus-isrg", "dioxus-lib", "dioxus-mobile", "dioxus-ssr", @@ -2706,6 +2676,18 @@ dependencies = [ "web-sys", ] +[[package]] +name = "dioxus-isrg" +version = "0.6.0-alpha.2" +dependencies = [ + "chrono", + "http 1.1.0", + "lru", + "rustc-hash 1.1.0", + "thiserror", + "tracing", +] + [[package]] name = "dioxus-lib" version = "0.6.0-alpha.2" @@ -2893,28 +2875,12 @@ dependencies = [ name = "dioxus-ssr" version = "0.6.0-alpha.2" dependencies = [ - "anyhow", - "argh", "askama_escape", - "async-trait", - "chrono", "dioxus", "dioxus-core", "dioxus-core-types", "dioxus-html", - "dioxus-interpreter-js", - "dioxus-signals", - "fern", - "fs_extra", - "generational-box", - "http 1.1.0", - "lru", "rustc-hash 1.1.0", - "serde", - "serde_json", - "thiserror", - "tokio", - "tracing", ] [[package]] @@ -2926,6 +2892,7 @@ dependencies = [ "dioxus", "dioxus-devtools", "dioxus-fullstack", + "dioxus-isrg", "dioxus-lib", "dioxus-router", "dioxus-ssr", diff --git a/Cargo.toml b/Cargo.toml index 93f523bd8a..accbbbae36 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -68,7 +68,7 @@ members = [ "examples/manganis-test-package/test-package-nested-dependency", "packages/devtools", "packages/devtools-types", - + "packages/isrg", ] [workspace.package] @@ -105,6 +105,7 @@ dioxus-devtools-types = { path = "packages/devtools-types", version = "0.6.0-alp dioxus-fullstack = { path = "packages/fullstack", version = "0.6.0-alpha.2" } dioxus-static-site-generation = { path = "packages/static-generation", version = "0.6.0-alpha.2" } dioxus_server_macro = { path = "packages/server-macro", version = "0.6.0-alpha.2", default-features = false } +dioxus-isrg = { path = "packages/isrg", version = "0.6.0-alpha.2" } lazy-js-bundle = { path = "packages/lazy-js-bundle", version = "0.6.0-alpha.2" } manganis = { path = "packages/manganis", version = "0.6.0-alpha.2" } @@ -116,6 +117,7 @@ warnings = { version = "0.2.0" } # a fork of pretty please for tests prettier-please = { version = "0.3.0", features = ["verbatim"]} +askama_escape = "0.10.3" tracing = "0.1.37" tracing-futures = "0.2.5" toml = "0.8" diff --git a/packages/fullstack/Cargo.toml b/packages/fullstack/Cargo.toml index ecd542d72d..7bde28a05d 100644 --- a/packages/fullstack/Cargo.toml +++ b/packages/fullstack/Cargo.toml @@ -24,6 +24,7 @@ generational-box = { workspace = true } # Dioxus + SSR dioxus-ssr = { workspace = true, optional = true } +dioxus-isrg = { workspace = true, optional = true } hyper = { workspace = true, optional = true } http = { workspace = true, optional = true } @@ -94,7 +95,7 @@ server = [ "dep:tokio-util", "dep:tokio-stream", "dep:dioxus-ssr", - "dioxus-ssr/incremental", + "dep:dioxus-isrg", "dep:tower", "dep:hyper", "dep:http", diff --git a/packages/isrg/Cargo.toml b/packages/isrg/Cargo.toml new file mode 100644 index 0000000000..db0dfe3707 --- /dev/null +++ b/packages/isrg/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "dioxus-isrg" +edition = "2021" +version.workspace = true + +[dependencies] +chrono = { workspace = true } +http = { workspace = true } +thiserror = { workspace = true } +rustc-hash = { workspace = true } +lru = { workspace = true } +tracing = { workspace = true } diff --git a/packages/ssr/src/incremental/config.rs b/packages/isrg/src/config.rs similarity index 95% rename from packages/ssr/src/incremental/config.rs rename to packages/isrg/src/config.rs index b05487def9..c785265e65 100644 --- a/packages/ssr/src/incremental/config.rs +++ b/packages/isrg/src/config.rs @@ -1,6 +1,6 @@ #![allow(non_snake_case)] -use crate::incremental::IncrementalRenderer; +use crate::IncrementalRenderer; use std::{ path::{Path, PathBuf}, @@ -82,7 +82,7 @@ impl IncrementalRendererConfig { pub fn build(self) -> IncrementalRenderer { let mut renderer = IncrementalRenderer { #[cfg(not(target_arch = "wasm32"))] - file_system_cache: crate::incremental::fs_cache::FileSystemCache::new( + file_system_cache: crate::fs_cache::FileSystemCache::new( self.static_dir.clone(), self.map_path, self.invalidate_after, diff --git a/packages/ssr/src/incremental/freshness.rs b/packages/isrg/src/freshness.rs similarity index 100% rename from packages/ssr/src/incremental/freshness.rs rename to packages/isrg/src/freshness.rs diff --git a/packages/ssr/src/incremental/fs_cache.rs b/packages/isrg/src/fs_cache.rs similarity index 100% rename from packages/ssr/src/incremental/fs_cache.rs rename to packages/isrg/src/fs_cache.rs diff --git a/packages/ssr/src/incremental/mod.rs b/packages/isrg/src/lib.rs similarity index 100% rename from packages/ssr/src/incremental/mod.rs rename to packages/isrg/src/lib.rs diff --git a/packages/ssr/src/incremental/memory_cache.rs b/packages/isrg/src/memory_cache.rs similarity index 100% rename from packages/ssr/src/incremental/memory_cache.rs rename to packages/isrg/src/memory_cache.rs diff --git a/packages/ssr/Cargo.toml b/packages/ssr/Cargo.toml index 0e33053724..1740bb02e5 100644 --- a/packages/ssr/Cargo.toml +++ b/packages/ssr/Cargo.toml @@ -12,40 +12,11 @@ keywords = ["dom", "ui", "gui", "react", "ssr"] dioxus-core = { workspace = true, features = ["serialize"] } dioxus-html = { workspace = true } dioxus-core-types = { workspace = true } -dioxus-interpreter-js = { workspace = true } -generational-box = { workspace = true } -askama_escape = "0.10.3" -thiserror = { workspace = true } +askama_escape = { workspace = true } rustc-hash = { workspace = true } -lru = { workspace = true } -tracing = { workspace = true } -http = { workspace = true } -async-trait = { workspace = true } -serde_json = { workspace = true } -chrono = { workspace = true, optional = true } - -[target.'cfg(target_arch = "wasm32")'.dependencies] -tokio = { workspace = true, features = ["io-util"], optional = true } - -[target.'cfg(not(target_arch = "wasm32"))'.dependencies] -tokio = { workspace = true, features = ["fs", "io-util"], optional = true } [dev-dependencies] dioxus = { workspace = true } -dioxus-signals = { workspace = true } -tokio = { version = "1", features = ["full"] } - -tracing = { workspace = true } -fern = { version = "0.6.0", features = ["colored"] } -anyhow = "1.0" -argh = "0.1.4" -serde = "1.0.120" -serde_json = "1.0.61" -fs_extra = "1.2.0" - -[features] -default = [] -incremental = ["dep:tokio", "dep:chrono"] [package.metadata.docs.rs] cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"] diff --git a/packages/ssr/src/lib.rs b/packages/ssr/src/lib.rs index 7d666dab83..18764c1e3b 100644 --- a/packages/ssr/src/lib.rs +++ b/packages/ssr/src/lib.rs @@ -4,8 +4,6 @@ mod cache; pub mod config; -#[cfg(feature = "incremental")] -pub mod incremental; pub mod renderer; pub mod template; diff --git a/packages/static-generation/Cargo.toml b/packages/static-generation/Cargo.toml index 32af0183d3..b9ce25e28e 100644 --- a/packages/static-generation/Cargo.toml +++ b/packages/static-generation/Cargo.toml @@ -14,11 +14,11 @@ resolver = "2" dioxus-fullstack = { workspace = true } dioxus-lib.workspace = true dioxus-router = { workspace = true, features = ["fullstack"] } -dioxus-ssr = { workspace = true, features = ["incremental"], optional = true } +dioxus-ssr = { workspace = true, optional = true } +dioxus-isrg = { workspace = true, optional = true } axum = { workspace = true, features = ["ws", "macros"], optional = true } tower-http = { workspace = true, features = ["fs"], optional = true } dioxus-devtools = { workspace = true, optional = true } -# dioxus-cli-config = { workspace = true, features = ["read-config"], optional = true } dioxus-web = { workspace = true, features = ["hydrate"], optional = true } tokio = { workspace = true, optional = true } http = { workspace = true, optional = true } From 8a52fdd5d648baab64105c4d5ab30d9058a8e52b Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 21 Aug 2024 23:09:15 -0700 Subject: [PATCH 036/139] fix weird rsx spacing --- examples/errors.rs | 2 +- packages/core/src/events.rs | 10 +++++----- packages/core/src/fragment.rs | 4 ++-- packages/core/src/global_context.rs | 2 +- packages/core/src/runtime.rs | 2 +- packages/core/src/virtual_dom.rs | 4 ++-- packages/hooks/src/use_collection.rs | 2 +- packages/hooks/src/use_coroutine.rs | 2 +- packages/router/examples/simple_routes.rs | 2 +- packages/rsx/src/lib.rs | 2 +- packages/rsx/tests/hotreload_pattern.rs | 18 +++++++++--------- packages/rsx/tests/parsing.rs | 2 +- packages/signals/src/write.rs | 2 +- packages/web/examples/hydrate.rs | 2 +- packages/web/tests/hydrate.rs | 2 +- 15 files changed, 29 insertions(+), 29 deletions(-) diff --git a/examples/errors.rs b/examples/errors.rs index 12b7fbaa8e..4727cb5e03 100644 --- a/examples/errors.rs +++ b/examples/errors.rs @@ -81,7 +81,7 @@ fn ParseNumberWithShow() -> Element { let request_data = "0.5"; let data: i32 = request_data.parse() // You can attach rsx to results that can be displayed in the Error Boundary - .show(|_| rsx!{ + .show(|_| rsx! { div { background_color: "red", border: "black", diff --git a/packages/core/src/events.rs b/packages/core/src/events.rs index bff80468a0..c32cffabdd 100644 --- a/packages/core/src/events.rs +++ b/packages/core/src/events.rs @@ -10,7 +10,7 @@ use std::{cell::RefCell, marker::PhantomData, rc::Rc}; /// /// ```rust, no_run /// # use dioxus::prelude::*; -/// rsx!{ +/// rsx! { /// MyComponent { onclick: move |evt| tracing::debug!("clicked") } /// }; /// @@ -20,7 +20,7 @@ use std::{cell::RefCell, marker::PhantomData, rc::Rc}; /// } /// /// fn MyComponent(cx: MyProps) -> Element { -/// rsx!{ +/// rsx! { /// button { /// onclick: move |evt| cx.onclick.call(evt), /// } @@ -37,7 +37,7 @@ pub type EventHandler = Callback; /// # Example /// /// ```rust, ignore -/// rsx!{ +/// rsx! { /// MyComponent { onclick: move |evt| { /// tracing::debug!("clicked"); /// 42 @@ -50,7 +50,7 @@ pub type EventHandler = Callback; /// } /// /// fn MyComponent(cx: MyProps) -> Element { -/// rsx!{ +/// rsx! { /// button { /// onclick: move |evt| println!("number: {}", cx.onclick.call(evt)), /// } @@ -65,7 +65,7 @@ pub struct Callback { /// # use dioxus::prelude::*; /// #[component] /// fn Child(onclick: EventHandler) -> Element { - /// rsx!{ + /// rsx! { /// button { /// // Diffing Child will not rerun this component, it will just update the callback in place so that if this callback is called, it will run the latest version of the callback /// onclick: move |evt| onclick(evt), diff --git a/packages/core/src/fragment.rs b/packages/core/src/fragment.rs index 7b1681a0f1..8a79f0318f 100644 --- a/packages/core/src/fragment.rs +++ b/packages/core/src/fragment.rs @@ -14,7 +14,7 @@ use crate::innerlude::*; /// ```rust /// # use dioxus::prelude::*; /// let value = 1; -/// rsx!{ +/// rsx! { /// Fragment { key: "{value}" } /// }; /// ``` @@ -76,7 +76,7 @@ impl FragmentBuilder { /// /// #[component] /// fn CustomCard(children: Element) -> Element { -/// rsx!{ +/// rsx! { /// div { /// h1 {"Title card"} /// {children} diff --git a/packages/core/src/global_context.rs b/packages/core/src/global_context.rs index 92a4dfd13b..45067e375f 100644 --- a/packages/core/src/global_context.rs +++ b/packages/core/src/global_context.rs @@ -351,7 +351,7 @@ pub fn schedule_update_any() -> Arc { /// window.scroll_with_x_and_y(original_scroll_position(), 0.0); /// }); /// -/// rsx!{ +/// rsx! { /// div { /// id: "my_element", /// "hello" diff --git a/packages/core/src/runtime.rs b/packages/core/src/runtime.rs index 3c72fdb40c..8fcbde2825 100644 --- a/packages/core/src/runtime.rs +++ b/packages/core/src/runtime.rs @@ -420,7 +420,7 @@ impl Runtime { /// } /// /// fn app() -> Element { -/// rsx!{ Component { runtime: Runtime::current().unwrap() } } +/// rsx! { Component { runtime: Runtime::current().unwrap() } } /// } /// /// // In a dynamic library diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index 8d520a25bb..6ce5718f2b 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -269,7 +269,7 @@ impl VirtualDom { /// } /// /// fn Example(cx: SomeProps) -> Element { - /// rsx!{ div { "hello {cx.name}" } } + /// rsx! { div { "hello {cx.name}" } } /// } /// /// let dom = VirtualDom::new_with_props(Example, SomeProps { name: "world" }); @@ -285,7 +285,7 @@ impl VirtualDom { /// # name: &'static str /// # } /// # fn Example(cx: SomeProps) -> Element { - /// # rsx!{ div { "hello {cx.name}" } } + /// # rsx! { div { "hello {cx.name}" } } /// # } /// let mut dom = VirtualDom::new_with_props(Example, SomeProps { name: "jane" }); /// dom.rebuild_in_place(); diff --git a/packages/hooks/src/use_collection.rs b/packages/hooks/src/use_collection.rs index 7b38f6aedc..cd61090c7a 100644 --- a/packages/hooks/src/use_collection.rs +++ b/packages/hooks/src/use_collection.rs @@ -40,7 +40,7 @@ static TodoList: Component = |cx| { let todos = use_map(|| HashMap::new()); let input = use_signal(|| None); - rsx!{ + rsx! { div { button { "Add todo" diff --git a/packages/hooks/src/use_coroutine.rs b/packages/hooks/src/use_coroutine.rs index 347ec38fd3..ad6bd52d39 100644 --- a/packages/hooks/src/use_coroutine.rs +++ b/packages/hooks/src/use_coroutine.rs @@ -61,7 +61,7 @@ use std::future::Future; /// }); /// /// -/// rsx!{ +/// rsx! { /// button { /// onclick: move |_| chat_client.send(Action::Start), /// "Start Chat Service" diff --git a/packages/router/examples/simple_routes.rs b/packages/router/examples/simple_routes.rs index 53c82dbebb..3ee695dee6 100644 --- a/packages/router/examples/simple_routes.rs +++ b/packages/router/examples/simple_routes.rs @@ -92,7 +92,7 @@ fn Route1(user_id: usize, dynamic: usize, query: String) -> Element { fn Route2(user_id: usize) -> Element { rsx! { pre { "Route2{{\n\tuser_id:{user_id}\n}}" } - {(0..user_id).map(|i| rsx!{ p { "{i}" } })} + {(0..user_id).map(|i| rsx! { p { "{i}" } })} p { "Footer" } Link { to: Route::Route3 { diff --git a/packages/rsx/src/lib.rs b/packages/rsx/src/lib.rs index 4f4f61a4d7..11cc0dc2b3 100644 --- a/packages/rsx/src/lib.rs +++ b/packages/rsx/src/lib.rs @@ -1,7 +1,7 @@ #![doc(html_logo_url = "https://avatars.githubusercontent.com/u/79236386")] #![doc(html_favicon_url = "https://avatars.githubusercontent.com/u/79236386")] -//! Parse the root tokens in the rsx!{ } macro +//! Parse the root tokens in the rsx! { } macro //! ========================================= //! //! This parsing path emerges directly from the macro call, with `RsxRender` being the primary entrance into parsing. diff --git a/packages/rsx/tests/hotreload_pattern.rs b/packages/rsx/tests/hotreload_pattern.rs index 65dc3dce6a..61cddc81fb 100644 --- a/packages/rsx/tests/hotreload_pattern.rs +++ b/packages/rsx/tests/hotreload_pattern.rs @@ -502,7 +502,7 @@ fn template_generates() { "width2": 100, "height2": "100px", p { "hello world" } - {(0..10).map(|i| rsx!{"{i}"})} + {(0..10).map(|i| rsx! {"{i}"})} } div { width: 120, @@ -537,9 +537,9 @@ fn diffs_complex() { "width2": 100, "height2": "100px", p { "hello world" } - {(0..10).map(|i| rsx!{"{i}"})}, - {(0..10).map(|i| rsx!{"{i}"})}, - {(0..11).map(|i| rsx!{"{i}"})}, + {(0..10).map(|i| rsx! {"{i}"})}, + {(0..10).map(|i| rsx! {"{i}"})}, + {(0..11).map(|i| rsx! {"{i}"})}, Comp {} } }; @@ -553,9 +553,9 @@ fn diffs_complex() { "height2": "100px", p { "hello world" } Comp {} - {(0..10).map(|i| rsx!{"{i}"})}, - {(0..10).map(|i| rsx!{"{i}"})}, - {(0..11).map(|i| rsx!{"{i}"})}, + {(0..10).map(|i| rsx! {"{i}"})}, + {(0..10).map(|i| rsx! {"{i}"})}, + {(0..11).map(|i| rsx! {"{i}"})}, } }; @@ -571,12 +571,12 @@ fn remove_node() { quote! { svg { Comp {} - {(0..10).map(|i| rsx!{"{i}"})}, + {(0..10).map(|i| rsx! {"{i}"})}, } }, quote! { div { - {(0..10).map(|i| rsx!{"{i}"})}, + {(0..10).map(|i| rsx! {"{i}"})}, } }, ) diff --git a/packages/rsx/tests/parsing.rs b/packages/rsx/tests/parsing.rs index edf9d60eb8..6e03ef8f93 100644 --- a/packages/rsx/tests/parsing.rs +++ b/packages/rsx/tests/parsing.rs @@ -98,7 +98,7 @@ fn complex_kitchen_sink() { } })} } - div { class: "px-4", {is_current.then(|| rsx!{ children })} } + div { class: "px-4", {is_current.then(|| rsx! { children })} } } // No nesting diff --git a/packages/signals/src/write.rs b/packages/signals/src/write.rs index 9b1bf01034..46d4dda4a9 100644 --- a/packages/signals/src/write.rs +++ b/packages/signals/src/write.rs @@ -17,7 +17,7 @@ pub type WritableRef<'a, T: Writable, O = ::Target> = T::Mut<'a, /// } /// /// fn MyComponent(mut count: Signal) -> Element { -/// rsx!{ +/// rsx! { /// button { /// onclick: move |_| { /// // You can use any methods from the Writable trait on Signals diff --git a/packages/web/examples/hydrate.rs b/packages/web/examples/hydrate.rs index b9c8e46f39..1cd0d9ae3b 100644 --- a/packages/web/examples/hydrate.rs +++ b/packages/web/examples/hydrate.rs @@ -12,7 +12,7 @@ fn app() -> Element { "asd" Bapp {} } - {(0..10).map(|f| rsx!{ + {(0..10).map(|f| rsx! { div { "thing {f}" } diff --git a/packages/web/tests/hydrate.rs b/packages/web/tests/hydrate.rs index ffb678b329..a409a4a754 100644 --- a/packages/web/tests/hydrate.rs +++ b/packages/web/tests/hydrate.rs @@ -35,7 +35,7 @@ fn rehydrates() { }, "listener test" } - {false.then(|| rsx!{ "hello" })} + {false.then(|| rsx! { "hello" })} } } } From 92f2f62611474a0f2bdf4fe05608c6c3b0a2e866 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 21 Aug 2024 23:21:17 -0700 Subject: [PATCH 037/139] refactor document a bit --- packages/desktop/src/protocol.rs | 6 +-- packages/document/src/document.rs | 73 +++++++++++++++++++++++++++ packages/document/src/eval.rs | 68 ++----------------------- packages/document/src/lib.rs | 82 +++---------------------------- packages/document/src/meta.rs | 0 packages/document/src/script.rs | 0 packages/document/src/title.rs | 0 packages/signals/src/lib.rs | 2 - 8 files changed, 84 insertions(+), 147 deletions(-) create mode 100644 packages/document/src/document.rs create mode 100644 packages/document/src/meta.rs create mode 100644 packages/document/src/script.rs create mode 100644 packages/document/src/title.rs diff --git a/packages/desktop/src/protocol.rs b/packages/desktop/src/protocol.rs index 109e70277a..8cdaaeba5c 100644 --- a/packages/desktop/src/protocol.rs +++ b/packages/desktop/src/protocol.rs @@ -1,5 +1,5 @@ use crate::{assets::*, webview::WebviewEdits}; -use dioxus_document::NATIVE_EVAL_JS; +// use dioxus_document::NATIVE_EVAL_JS; use dioxus_interpreter_js::unified_bindings::SLEDGEHAMMER_JS; use dioxus_interpreter_js::NATIVE_JS; use serde::Deserialize; @@ -222,10 +222,6 @@ fn module_loader(root_id: &str, headless: bool) -> String { window.interpreter.waitForRequest({headless}); }} - "# ) } diff --git a/packages/document/src/document.rs b/packages/document/src/document.rs new file mode 100644 index 0000000000..4e7e675f14 --- /dev/null +++ b/packages/document/src/document.rs @@ -0,0 +1,73 @@ +use super::*; +use std::rc::Rc; + +/// A provider for document-related functionality. By default most methods are driven through [`eval`]. +pub trait Document { + /// Create a new evaluator for the document that evaluates JavaScript and facilitates communication between JavaScript and Rust. + // fn new_evaluator(&self, js: String) -> GenerationalBox>; + fn eval(&self, js: String) -> Result { + todo!() + } + + /// Set the title of the document + fn set_title(&self, title: String) { + _ = self.eval(format!("document.title = {title:?};")); + } + + /// Create a new meta tag + fn create_meta(&self, props: MetaProps) { + let attributes = props.attributes(); + let js = create_element_in_head("meta", &attributes, None); + _ = self.eval(js); + } + + /// Create a new script tag + fn create_script(&self, props: ScriptProps) { + let attributes = props.attributes(); + let js = create_element_in_head("script", &attributes, props.script_contents()); + _ = self.eval(js); + } + + /// Create a new style tag + fn create_style(&self, props: StyleProps) { + let attributes = props.attributes(); + let js = create_element_in_head("style", &attributes, props.style_contents()); + _ = self.eval(js); + } + + /// Create a new link tag + fn create_link(&self, props: head::LinkProps) { + let attributes = props.attributes(); + let js = create_element_in_head("link", &attributes, None); + _ = self.eval(js); + } + + /// Get a reference to the document as `dyn Any` + fn as_any(&self) -> &dyn std::any::Any; +} + +fn create_element_in_head( + tag: &str, + attributes: &[(&str, String)], + children: Option, +) -> String { + fn format_attributes(attributes: &[(&str, String)]) -> String { + let mut formatted = String::from("["); + for (key, value) in attributes { + formatted.push_str(&format!("[{key:?}, {value:?}],")); + } + if formatted.ends_with(',') { + formatted.pop(); + } + formatted.push(']'); + formatted + } + + let helpers: &str = todo!(); + // let helpers = include_str!("../js/head.js"); + let attributes = format_attributes(attributes); + let children = children + .map(|c| format!("\"{c}\"")) + .unwrap_or("null".to_string()); + format!(r#"{helpers};window.createElementInHead("{tag}", {attributes}, {children});"#) +} diff --git a/packages/document/src/eval.rs b/packages/document/src/eval.rs index e238cd87c0..ffa3816b69 100644 --- a/packages/document/src/eval.rs +++ b/packages/document/src/eval.rs @@ -12,34 +12,17 @@ use std::task::{Context, Poll}; use super::{document, Document}; -// type EvalCreator = Rc UseEval>; - -pub const NATIVE_EVAL_JS: &str = include_str!("../../html/src/js/native_eval.js"); - -// /// Get a struct that can execute any JavaScript. -// /// -// /// # Safety -// /// -// /// Please be very careful with this function. A script with too many dynamic -// /// parts is practically asking for a hacker to find an XSS vulnerability in -// /// it. **This applies especially to web targets, where the JavaScript context -// /// has access to most, if not all of your application data.** -// #[must_use] -// pub fn eval_provider() -> EvalCreator { -// let doc = document(); - -// Rc::new(move |script: &str| UseEval::new(doc.new_evaluator(script.to_string()))) -// as Rc UseEval> -// } - #[doc = include_str!("../docs/eval.md")] #[doc(alias = "javascript")] -pub fn eval(script: &str) -> UseEval { +pub fn eval(script: &str) -> Eval { todo!() // let document = use_hook(document); // UseEval::new(document.new_evaluator(script.to_string())) } +#[derive(Clone, Copy)] +pub struct Eval {} + /// A wrapper around the target platform's evaluator that lets you send and receive data from JavaScript spawned by [`eval`]. /// #[doc = include_str!("../docs/eval.md")] @@ -48,49 +31,6 @@ pub struct UseEval { // evaluator: Rc, } -// impl UseEval { -// /// Creates a new UseEval -// pub fn new(evaluator: GenerationalBox>) -> Self { -// Self { evaluator } -// } - -// /// Sends a [`serde_json::Value`] to the evaluated JavaScript. -// pub fn send(&self, data: serde_json::Value) -> Result<(), EvalError> { -// match self.evaluator.try_read() { -// Ok(evaluator) => evaluator.send(data), -// Err(_) => Err(EvalError::Finished), -// } -// } - -// /// Gets an UnboundedReceiver to receive messages from the evaluated JavaScript. -// pub async fn recv(&mut self) -> Result { -// poll_fn(|cx| match self.evaluator.try_write() { -// Ok(mut evaluator) => evaluator.poll_recv(cx), -// Err(_) => Poll::Ready(Err(EvalError::Finished)), -// }) -// .await -// } - -// /// Gets the return value of the evaluated JavaScript. -// pub async fn join(self) -> Result { -// poll_fn(|cx| match self.evaluator.try_write() { -// Ok(mut evaluator) => evaluator.poll_join(cx), -// Err(_) => Poll::Ready(Err(EvalError::Finished)), -// }) -// .await -// } -// } - -impl IntoFuture for UseEval { - type Output = Result; - type IntoFuture = Pin>>; - - fn into_future(self) -> Self::IntoFuture { - todo!() - // Box::pin(self.join()) - } -} - /// Represents an error when evaluating JavaScript #[derive(Debug)] #[non_exhaustive] diff --git a/packages/document/src/lib.rs b/packages/document/src/lib.rs index b0ce8c4356..b7f9aae261 100644 --- a/packages/document/src/lib.rs +++ b/packages/document/src/lib.rs @@ -1,87 +1,17 @@ use std::rc::Rc; +mod document; +mod eval; mod head; -pub use head::*; +mod title; -mod eval; +pub use document::*; pub use eval::*; +pub use head::*; +pub use title::*; /// Get the document provider for the current platform or a no-op provider if the platform doesn't document functionality. pub fn document() -> Rc { dioxus_core::prelude::try_consume_context::>() .expect("A document should exist with this renderer") - // Create a NoOp provider that always logs an error when trying to evaluate - // That way, we can still compile and run the code without a real provider - // .unwrap_or_else(|| Rc::new(NoOpDocument) as Rc) -} - -/// A provider for document-related functionality. By default most methods are driven through [`eval`]. -pub trait Document { - /// Create a new evaluator for the document that evaluates JavaScript and facilitates communication between JavaScript and Rust. - // fn new_evaluator(&self, js: String) -> GenerationalBox>; - fn eval(&self, js: String) -> Result<(), EvalError> { - todo!() - } - - /// Set the title of the document - fn set_title(&self, title: String) { - self.eval(format!("document.title = {title:?};")); - } - - /// Create a new meta tag - fn create_meta(&self, props: MetaProps) { - let attributes = props.attributes(); - let js = create_element_in_head("meta", &attributes, None); - self.eval(js); - } - - /// Create a new script tag - fn create_script(&self, props: ScriptProps) { - let attributes = props.attributes(); - let js = create_element_in_head("script", &attributes, props.script_contents()); - self.eval(js); - } - - /// Create a new style tag - fn create_style(&self, props: StyleProps) { - let attributes = props.attributes(); - let js = create_element_in_head("style", &attributes, props.style_contents()); - self.eval(js); - } - - /// Create a new link tag - fn create_link(&self, props: head::LinkProps) { - let attributes = props.attributes(); - let js = create_element_in_head("link", &attributes, None); - self.eval(js); - } - - /// Get a reference to the document as `dyn Any` - fn as_any(&self) -> &dyn std::any::Any; -} - -fn format_attributes(attributes: &[(&str, String)]) -> String { - let mut formatted = String::from("["); - for (key, value) in attributes { - formatted.push_str(&format!("[{key:?}, {value:?}],")); - } - if formatted.ends_with(',') { - formatted.pop(); - } - formatted.push(']'); - formatted -} - -fn create_element_in_head( - tag: &str, - attributes: &[(&str, String)], - children: Option, -) -> String { - let helpers: &str = todo!(); - // let helpers = include_str!("../js/head.js"); - let attributes = format_attributes(attributes); - let children = children - .map(|c| format!("\"{c}\"")) - .unwrap_or("null".to_string()); - format!(r#"{helpers};window.createElementInHead("{tag}", {attributes}, {children});"#) } diff --git a/packages/document/src/meta.rs b/packages/document/src/meta.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/document/src/script.rs b/packages/document/src/script.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/document/src/title.rs b/packages/document/src/title.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/signals/src/lib.rs b/packages/signals/src/lib.rs index 3ff8a3f494..b8b6a75747 100644 --- a/packages/signals/src/lib.rs +++ b/packages/signals/src/lib.rs @@ -41,5 +41,3 @@ mod props; pub use props::*; pub mod warnings; - -pub mod prelude {} From a1b361aac13be2f7eb110cc15a81788098a3b23b Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 21 Aug 2024 23:58:01 -0700 Subject: [PATCH 038/139] wip: remove query --- Cargo.lock | 1 + packages/desktop/src/app.rs | 21 +-- packages/desktop/src/desktop_context.rs | 1 - packages/desktop/src/document.rs | 90 +++------ packages/desktop/src/element.rs | 5 +- packages/desktop/src/ipc.rs | 3 - packages/desktop/src/launch.rs | 2 +- packages/desktop/src/lib.rs | 1 - packages/desktop/src/query.rs | 240 ------------------------ packages/document/Cargo.toml | 1 + packages/document/src/document.rs | 7 +- packages/document/src/error.rs | 32 ++++ packages/document/src/eval.rs | 72 +++---- packages/document/src/lib.rs | 9 + packages/interpreter/src/js/hash.txt | 2 +- packages/interpreter/src/ts/native.ts | 2 +- 16 files changed, 109 insertions(+), 380 deletions(-) delete mode 100644 packages/desktop/src/query.rs create mode 100644 packages/document/src/error.rs diff --git a/Cargo.lock b/Cargo.lock index 6ff702aa21..734a3ede7e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2523,6 +2523,7 @@ dependencies = [ "dioxus-core", "dioxus-core-macro", "dioxus-core-types", + "futures-channel", "serde", "serde_json", "tracing", diff --git a/packages/desktop/src/app.rs b/packages/desktop/src/app.rs index cf34556236..9e309ecd3d 100644 --- a/packages/desktop/src/app.rs +++ b/packages/desktop/src/app.rs @@ -3,7 +3,6 @@ use crate::{ event_handlers::WindowEventHandlers, file_upload::{DesktopFileUploadForm, FileDialogRequest, NativeFileEngine}, ipc::{IpcMessage, UserWindowEvent}, - query::QueryResult, shortcut::ShortcutRegistry, webview::WebviewInstance, }; @@ -85,9 +84,9 @@ impl App { #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] app.set_menubar_receiver(); - // Allow hotreloading to work - but only in debug mode + // Allow hotreloading and other devtools to work - but only in debug mode #[cfg(all(feature = "devtools", debug_assertions))] - app.connect_hotreload(); + app.connect_devtools(); #[cfg(debug_assertions)] #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] @@ -136,18 +135,10 @@ impl App { } #[cfg(all(feature = "devtools", debug_assertions,))] - pub fn connect_hotreload(&self) { + pub fn connect_devtools(&self) { let proxy = self.shared.proxy.clone(); - - tokio::task::spawn(async move { - // let Some(Ok(mut receiver)) = dioxus_devtools::NativeReceiver::create_from_cli().await - // else { - // return; - // }; - - // while let Some(Ok(msg)) = receiver.next().await { - // _ = proxy.send_event(UserWindowEvent::HotReloadEvent(msg)); - // } + dioxus_devtools::connect(move |msg| { + _ = proxy.send_event(UserWindowEvent::HotReloadEvent(msg)); }); } @@ -267,7 +258,7 @@ impl App { } #[cfg(all(feature = "devtools", debug_assertions,))] - pub fn handle_hot_reload_msg(&mut self, msg: dioxus_devtools::DevserverMsg) { + pub fn handle_devserver_msg(&mut self, msg: dioxus_devtools::DevserverMsg) { use dioxus_devtools::DevserverMsg; match msg { diff --git a/packages/desktop/src/desktop_context.rs b/packages/desktop/src/desktop_context.rs index 415f4b73a1..05ba564b2b 100644 --- a/packages/desktop/src/desktop_context.rs +++ b/packages/desktop/src/desktop_context.rs @@ -3,7 +3,6 @@ use crate::{ assets::AssetHandlerRegistry, file_upload::NativeFileHover, ipc::UserWindowEvent, - query::QueryEngine, shortcut::{HotKey, ShortcutHandle, ShortcutRegistryError}, webview::WebviewInstance, AssetRequest, Config, WryEventHandler, diff --git a/packages/desktop/src/document.rs b/packages/desktop/src/document.rs index cee204ab33..c39c9bff02 100644 --- a/packages/desktop/src/document.rs +++ b/packages/desktop/src/document.rs @@ -1,79 +1,51 @@ -use dioxus_document::{Document, EvalError}; -use generational_box::{AnyStorage, GenerationalBox, UnsyncStorage}; +use std::sync::{Arc, Mutex}; -use crate::{query::Query, DesktopContext}; +use crate::DesktopContext; +use dioxus_document::{Document, Eval, EvalError}; /// Represents the desktop-target's provider of evaluators. pub struct DesktopDocument { - pub(crate) desktop_ctx: DesktopContext, + pub(crate) cx: DesktopContext, } impl DesktopDocument { pub fn new(desktop_ctx: DesktopContext) -> Self { - Self { desktop_ctx } + Self { cx: desktop_ctx } } } impl Document for DesktopDocument { - // fn new_evaluator(&self, js: String) -> GenerationalBox> { - // DesktopEvaluator::create(self.desktop_ctx.clone(), js) - // } + fn eval(&self, js: String) -> Eval { + let (tx, eval) = Eval::from_parts(); + + // Dumb wry has a signature of Fn instead of FnOnce, meaning we need to put the callback in a closure + // that uses rwlock + option to make sure we don't run the callback twice + let _tx = Arc::new(Mutex::new(Some(tx))); + let tx = _tx.clone(); + let callback = move |res| { + if let Some(tx) = tx.lock().unwrap().take() { + let _ = tx.send(Ok(res)); + } + }; + + let res = self.cx.webview.evaluate_script_with_callback(&js, callback); + if let Err(err) = res { + _ = _tx + .lock() + .unwrap() + .take() + .unwrap() + .send(Err(EvalError::Communication(err.to_string()))); + } + + eval + } fn set_title(&self, title: String) { - self.desktop_ctx.window.set_title(&title); + self.cx.window.set_title(&title); } fn as_any(&self) -> &dyn std::any::Any { self } } - -/// Represents a desktop-target's JavaScript evaluator. -pub(crate) struct DesktopEvaluator { - query: Query, -} - -impl DesktopEvaluator { - // /// Creates a new evaluator for desktop-based targets. - // pub fn create(desktop_ctx: DesktopContext, js: String) -> GenerationalBox> { - // let ctx = desktop_ctx.clone(); - // let query = desktop_ctx.query.new_query(&js, ctx); - - // // We create a generational box that is owned by the query slot so that when we drop the query slot, the generational box is also dropped. - // let owner = UnsyncStorage::owner(); - // let query_id = query.id; - // let query = owner.insert(Box::new(DesktopEvaluator { query }) as Box); - // desktop_ctx.query.active_requests.slab.borrow_mut()[query_id].owner = Some(owner); - - // query - // } -} - -// impl Evaluator for DesktopEvaluator { -// fn poll_join( -// &mut self, -// cx: &mut std::task::Context<'_>, -// ) -> std::task::Poll> { -// self.query -// .poll_result(cx) -// .map_err(|e| EvalError::Communication(e.to_string())) -// } - -// /// Sends a message to the evaluated JavaScript. -// fn send(&self, data: serde_json::Value) -> Result<(), EvalError> { -// if let Err(e) = self.query.send(data) { -// return Err(EvalError::Communication(e.to_string())); -// } -// Ok(()) -// } - -// /// Gets an UnboundedReceiver to receive messages from the evaluated JavaScript. -// fn poll_recv( -// &mut self, -// cx: &mut std::task::Context<'_>, -// ) -> std::task::Poll> { -// self.query -// .poll_recv(cx) -// .map_err(|e| EvalError::Communication(e.to_string())) -// } -// } diff --git a/packages/desktop/src/element.rs b/packages/desktop/src/element.rs index 505b45c435..54896c9b50 100644 --- a/packages/desktop/src/element.rs +++ b/packages/desktop/src/element.rs @@ -4,18 +4,17 @@ use dioxus_html::{ MountedResult, RenderedElementBacking, }; -use crate::{desktop_context::DesktopContext, query::QueryEngine}; +use crate::desktop_context::DesktopContext; #[derive(Clone)] /// A mounted element passed to onmounted events pub struct DesktopElement { id: ElementId, webview: DesktopContext, - query: QueryEngine, } impl DesktopElement { - pub(crate) fn new(id: ElementId, webview: DesktopContext, query: QueryEngine) -> Self { + pub(crate) fn new(id: ElementId, webview: DesktopContext) -> Self { Self { id, webview, query } } } diff --git a/packages/desktop/src/ipc.rs b/packages/desktop/src/ipc.rs index e78114f688..e321b2b6c3 100644 --- a/packages/desktop/src/ipc.rs +++ b/packages/desktop/src/ipc.rs @@ -14,9 +14,6 @@ pub enum UserWindowEvent { /// Poll the virtualdom Poll(WindowId), - /// Handle an ipc message eminating from the window.postMessage of a given webview - Ipc { id: WindowId, msg: IpcMessage }, - /// Handle a hotreload event, basically telling us to update our templates #[cfg(all(feature = "devtools", debug_assertions))] HotReloadEvent(dioxus_devtools::DevserverMsg), diff --git a/packages/desktop/src/launch.rs b/packages/desktop/src/launch.rs index 0bef0fc113..9201b5aee5 100644 --- a/packages/desktop/src/launch.rs +++ b/packages/desktop/src/launch.rs @@ -42,7 +42,7 @@ pub fn launch_virtual_dom_blocking(virtual_dom: VirtualDom, desktop_config: Conf UserWindowEvent::MudaMenuEvent(evnt) => app.handle_menu_event(evnt), #[cfg(all(feature = "devtools", debug_assertions))] - UserWindowEvent::HotReloadEvent(msg) => app.handle_hot_reload_msg(msg), + UserWindowEvent::HotReloadEvent(msg) => app.handle_devserver_msg(msg), UserWindowEvent::Ipc { id, msg } => match msg.method() { IpcMethod::Initialize => app.handle_initialize_msg(id), diff --git a/packages/desktop/src/lib.rs b/packages/desktop/src/lib.rs index 87a72858a0..e33d52e0a2 100644 --- a/packages/desktop/src/lib.rs +++ b/packages/desktop/src/lib.rs @@ -19,7 +19,6 @@ mod hooks; mod ipc; mod menubar; mod protocol; -mod query; mod shortcut; mod waker; mod webview; diff --git a/packages/desktop/src/query.rs b/packages/desktop/src/query.rs deleted file mode 100644 index 15d6253804..0000000000 --- a/packages/desktop/src/query.rs +++ /dev/null @@ -1,240 +0,0 @@ -use crate::DesktopContext; -use futures_util::{FutureExt, StreamExt}; -use generational_box::Owner; -use serde::{de::DeserializeOwned, Deserialize}; -use serde_json::Value; -use slab::Slab; -use std::{cell::RefCell, rc::Rc}; -use thiserror::Error; - -/// Tracks what query ids are currently active -pub(crate) struct SharedSlab { - pub slab: Rc>>, -} - -impl Clone for SharedSlab { - fn clone(&self) -> Self { - Self { - slab: self.slab.clone(), - } - } -} - -impl Default for SharedSlab { - fn default() -> Self { - SharedSlab { - slab: Rc::new(RefCell::new(Slab::new())), - } - } -} - -pub(crate) struct QueryEntry { - channel_sender: futures_channel::mpsc::UnboundedSender, - return_sender: Option>>, - pub owner: Option, -} - -/// Handles sending and receiving arbitrary queries from the webview. Queries can be resolved non-sequentially, so we use ids to track them. -#[derive(Clone, Default)] -pub(crate) struct QueryEngine { - pub active_requests: SharedSlab, -} - -impl QueryEngine { - /// Creates a new query and returns a handle to it. The query will be resolved when the webview returns a result with the same id. - pub fn new_query( - &self, - script: &str, - context: DesktopContext, - ) -> Query { - let (tx, rx) = futures_channel::mpsc::unbounded(); - let (return_tx, return_rx) = futures_channel::oneshot::channel(); - let request_id = self.active_requests.slab.borrow_mut().insert(QueryEntry { - channel_sender: tx, - return_sender: Some(return_tx), - owner: None, - }); - - // start the query - // We embed the return of the eval in a function so we can send it back to the main thread - if let Err(err) = context.webview.evaluate_script(&format!( - r#"(function(){{ - let dioxus = window.createQuery({request_id}); - let post_error = function(err) {{ - let returned_value = {{ - "method": "query", - "params": {{ - "id": {request_id}, - "data": {{ - "data": err, - "method": "return_error" - }} - }} - }}; - window.ipc.postMessage( - JSON.stringify(returned_value) - ); - }}; - try {{ - const AsyncFunction = async function () {{}}.constructor; - let promise = (new AsyncFunction("dioxus", {script:?}))(dioxus); - promise - .then((result)=>{{ - let returned_value = {{ - "method": "query", - "params": {{ - "id": {request_id}, - "data": {{ - "data": result, - "method": "return" - }} - }} - }}; - window.ipc.postMessage( - JSON.stringify(returned_value) - ); - }}) - .catch(err => post_error(`Error running JS: ${{err}}`)); - }} catch (error) {{ - post_error(`Invalid JS: ${{error}}`); - }} - }})();"# - )) { - tracing::warn!("Query error: {err}"); - } - - Query { - id: request_id, - receiver: rx, - return_receiver: Some(return_rx), - desktop: context, - phantom: std::marker::PhantomData, - } - } - - /// Send a query channel message to the correct query - pub fn send(&self, data: QueryResult) { - let QueryResult { id, data } = data; - let mut slab = self.active_requests.slab.borrow_mut(); - if let Some(entry) = slab.get_mut(id) { - match data { - QueryResultData::Return { data } => { - if let Some(sender) = entry.return_sender.take() { - let _ = sender.send(Ok(data.unwrap_or_default())); - } - } - QueryResultData::ReturnError { data } => { - if let Some(sender) = entry.return_sender.take() { - let _ = sender.send(Err(data.to_string())); - } - } - QueryResultData::Drop => { - slab.remove(id); - } - QueryResultData::Send { data } => { - let _ = entry.channel_sender.unbounded_send(data); - } - } - } - } -} - -pub(crate) struct Query { - desktop: DesktopContext, - receiver: futures_channel::mpsc::UnboundedReceiver, - return_receiver: Option>>, - pub id: usize, - phantom: std::marker::PhantomData, -} - -impl Query { - /// Resolve the query - pub async fn resolve(mut self) -> Result { - let result = self.result().await?; - V::deserialize(result).map_err(QueryError::Deserialize) - } - - /// Send a message to the query - pub fn send(&self, message: S) -> Result<(), QueryError> { - let queue_id = self.id; - - let data = message.to_string(); - let script = format!(r#"window.getQuery({queue_id}).rustSend({data});"#); - - self.desktop - .webview - .evaluate_script(&script) - .map_err(|e| QueryError::Send(e.to_string()))?; - - Ok(()) - } - - /// Poll the query for a message - pub fn poll_recv( - &mut self, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - self.receiver - .poll_next_unpin(cx) - .map(|result| result.ok_or(QueryError::Recv(String::from("Receive channel closed")))) - } - - /// Receive the result of the query - pub async fn result(&mut self) -> Result { - match self.return_receiver.take() { - Some(receiver) => match receiver.await { - Ok(Ok(data)) => Ok(data), - Ok(Err(err)) => Err(QueryError::Recv(err)), - Err(err) => Err(QueryError::Recv(err.to_string())), - }, - None => Err(QueryError::Finished), - } - } - - /// Poll the query for a result - pub fn poll_result( - &mut self, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - match self.return_receiver.as_mut() { - Some(receiver) => receiver.poll_unpin(cx).map(|result| match result { - Ok(Ok(data)) => Ok(data), - Ok(Err(err)) => Err(QueryError::Recv(err)), - Err(err) => Err(QueryError::Recv(err.to_string())), - }), - None => std::task::Poll::Ready(Err(QueryError::Finished)), - } - } -} - -#[derive(Error, Debug)] -#[non_exhaustive] -pub enum QueryError { - #[error("Error receiving query result: {0}")] - Recv(String), - #[error("Error sending message to query: {0}")] - Send(String), - #[error("Error deserializing query result: {0}")] - Deserialize(serde_json::Error), - #[error("Query has already been resolved")] - Finished, -} - -#[derive(Clone, Debug, Deserialize)] -pub(crate) struct QueryResult { - id: usize, - data: QueryResultData, -} - -#[derive(Clone, Debug, Deserialize)] -#[serde(tag = "method")] -enum QueryResultData { - #[serde(rename = "return")] - Return { data: Option }, - #[serde(rename = "return_error")] - ReturnError { data: Value }, - #[serde(rename = "send")] - Send { data: Value }, - #[serde(rename = "drop")] - Drop, -} diff --git a/packages/document/Cargo.toml b/packages/document/Cargo.toml index 8c424bcce6..3289d9874d 100644 --- a/packages/document/Cargo.toml +++ b/packages/document/Cargo.toml @@ -10,3 +10,4 @@ dioxus-core-macro = { workspace = true } tracing = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } +futures-channel = { workspace = true } diff --git a/packages/document/src/document.rs b/packages/document/src/document.rs index 4e7e675f14..e9466667e7 100644 --- a/packages/document/src/document.rs +++ b/packages/document/src/document.rs @@ -1,13 +1,12 @@ +use error::EvalError; + use super::*; use std::rc::Rc; /// A provider for document-related functionality. By default most methods are driven through [`eval`]. pub trait Document { /// Create a new evaluator for the document that evaluates JavaScript and facilitates communication between JavaScript and Rust. - // fn new_evaluator(&self, js: String) -> GenerationalBox>; - fn eval(&self, js: String) -> Result { - todo!() - } + fn eval(&self, js: String) -> Eval; /// Set the title of the document fn set_title(&self, title: String) { diff --git a/packages/document/src/error.rs b/packages/document/src/error.rs new file mode 100644 index 0000000000..d5bbd8b237 --- /dev/null +++ b/packages/document/src/error.rs @@ -0,0 +1,32 @@ +use std::error::Error; +use std::fmt::Display; + +/// Represents an error when evaluating JavaScript +#[derive(Debug)] +#[non_exhaustive] +pub enum EvalError { + /// The platform does not support evaluating JavaScript. + Unsupported, + + /// The provided JavaScript has already been ran. + Finished, + + /// The provided JavaScript is not valid and can't be ran. + InvalidJs(String), + + /// Represents an error communicating between JavaScript and Rust. + Communication(String), +} + +impl Display for EvalError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + EvalError::Unsupported => write!(f, "EvalError::Unsupported - eval is not supported on the current platform"), + EvalError::Finished => write!(f, "EvalError::Finished - eval has already ran"), + EvalError::InvalidJs(_) => write!(f, "EvalError::InvalidJs - the provided javascript is invalid"), + EvalError::Communication(_) => write!(f, "EvalError::Communication - there was an error trying to communicate with between javascript and rust"), + } + } +} + +impl Error for EvalError {} diff --git a/packages/document/src/eval.rs b/packages/document/src/eval.rs index ffa3816b69..75c720379f 100644 --- a/packages/document/src/eval.rs +++ b/packages/document/src/eval.rs @@ -1,62 +1,32 @@ #![allow(clippy::await_holding_refcell_ref)] #![doc = include_str!("../docs/eval.md")] -use dioxus_core::prelude::*; -// use generational_box::GenerationalBox; -use std::error::Error; -use std::fmt::Display; -use std::future::{poll_fn, Future, IntoFuture}; -use std::pin::Pin; -use std::rc::Rc; -use std::task::{Context, Poll}; +use crate::error::EvalError; -use super::{document, Document}; - -#[doc = include_str!("../docs/eval.md")] -#[doc(alias = "javascript")] -pub fn eval(script: &str) -> Eval { - todo!() - // let document = use_hook(document); - // UseEval::new(document.new_evaluator(script.to_string())) -} - -#[derive(Clone, Copy)] -pub struct Eval {} - -/// A wrapper around the target platform's evaluator that lets you send and receive data from JavaScript spawned by [`eval`]. -/// #[doc = include_str!("../docs/eval.md")] -#[derive(Clone, Copy)] -pub struct UseEval { - // evaluator: Rc, +pub struct Eval { + rx: futures_channel::oneshot::Receiver>, } -/// Represents an error when evaluating JavaScript -#[derive(Debug)] -#[non_exhaustive] -pub enum EvalError { - /// The platform does not support evaluating JavaScript. - Unsupported, - - /// The provided JavaScript has already been ran. - Finished, - - /// The provided JavaScript is not valid and can't be ran. - InvalidJs(String), +impl Eval { + /// Create this eval from a oneshot channel that, when resolved, will return the result of the eval + pub fn new(rx: futures_channel::oneshot::Receiver>) -> Self { + Self { rx } + } - /// Represents an error communicating between JavaScript and Rust. - Communication(String), -} + /// Create this eval and return the tx that will be used to resolve the eval + pub fn from_parts() -> ( + futures_channel::oneshot::Sender>, + Self, + ) { + let (tx, rx) = futures_channel::oneshot::channel(); + (tx, Self::new(rx)) + } -impl Display for EvalError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - EvalError::Unsupported => write!(f, "EvalError::Unsupported - eval is not supported on the current platform"), - EvalError::Finished => write!(f, "EvalError::Finished - eval has already ran"), - EvalError::InvalidJs(_) => write!(f, "EvalError::InvalidJs - the provided javascript is invalid"), - EvalError::Communication(_) => write!(f, "EvalError::Communication - there was an error trying to communicate with between javascript and rust"), - } + /// Poll this eval until it resolves + pub async fn recv(self) -> Result { + self.rx + .await + .map_err(|_| EvalError::Communication("eval channel closed".to_string()))? } } - -impl Error for EvalError {} diff --git a/packages/document/src/lib.rs b/packages/document/src/lib.rs index b7f9aae261..f897d3743e 100644 --- a/packages/document/src/lib.rs +++ b/packages/document/src/lib.rs @@ -1,11 +1,13 @@ use std::rc::Rc; mod document; +mod error; mod eval; mod head; mod title; pub use document::*; +pub use error::*; pub use eval::*; pub use head::*; pub use title::*; @@ -15,3 +17,10 @@ pub fn document() -> Rc { dioxus_core::prelude::try_consume_context::>() .expect("A document should exist with this renderer") } + +/// Evaluate some javascript in the current document +#[doc = include_str!("../docs/eval.md")] +#[doc(alias = "javascript")] +pub fn eval(script: &str) -> Eval { + document().eval(script.to_string()) +} diff --git a/packages/interpreter/src/js/hash.txt b/packages/interpreter/src/js/hash.txt index 2220fd98e4..111177a44a 100644 --- a/packages/interpreter/src/js/hash.txt +++ b/packages/interpreter/src/js/hash.txt @@ -1 +1 @@ -[6449103750905854967, 4461869229701639737, 13069001215487072322, 8716623267269178440, 5336385715226370016, 14456089431355876478, 11703635120274352436, 5052021921702764563, 17534315583914394253, 5638004933879392817] \ No newline at end of file +[6449103750905854967, 4461869229701639737, 13069001215487072322, 8716623267269178440, 5336385715226370016, 14456089431355876478, 6210436390350364769, 5052021921702764563, 17534315583914394253, 5638004933879392817] \ No newline at end of file diff --git a/packages/interpreter/src/ts/native.ts b/packages/interpreter/src/ts/native.ts index 0c60041ede..532a53ca84 100644 --- a/packages/interpreter/src/ts/native.ts +++ b/packages/interpreter/src/ts/native.ts @@ -443,7 +443,7 @@ function getTargetId(target: EventTarget): NodeId | null { // let target_id = find_real_id(target); // if (target_id !== null) { // const send = (event_name) => { -// const message = window.interpreter.serializeIpcMessage("file_diolog", { accept: target.getAttribute("accept"), directory: target.getAttribute("webkitdirectory") === "true", multiple: target.hasAttribute("multiple"), target: parseInt(target_id), bubbles: event_bubbles(event_name), event: event_name }); +// const message = window.interpreter.serializeIpcMessage("file_dialog", { accept: target.getAttribute("accept"), directory: target.getAttribute("webkitdirectory") === "true", multiple: target.hasAttribute("multiple"), target: parseInt(target_id), bubbles: event_bubbles(event_name), event: event_name }); // window.ipc.postMessage(message); // }; // send("change&input"); From 2d10ba2de57f9cf5038cdbc8a2ab0b9e1ba6e2ef Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 22 Aug 2024 00:13:49 -0700 Subject: [PATCH 039/139] wip: work on assets --- packages/desktop/src/app.rs | 12 -- packages/desktop/src/assets.rs | 20 +-- packages/desktop/src/desktop_context.rs | 9 +- packages/desktop/src/element.rs | 207 +++++++++++------------- packages/desktop/src/hooks.rs | 13 +- packages/desktop/src/ipc.rs | 7 +- packages/desktop/src/launch.rs | 2 - packages/desktop/src/webview.rs | 3 +- packages/document/src/error.rs | 4 + packages/document/src/eval.rs | 5 + 10 files changed, 131 insertions(+), 151 deletions(-) diff --git a/packages/desktop/src/app.rs b/packages/desktop/src/app.rs index 9e309ecd3d..7a1f8481d1 100644 --- a/packages/desktop/src/app.rs +++ b/packages/desktop/src/app.rs @@ -245,18 +245,6 @@ impl App { } } - pub fn handle_query_msg(&mut self, msg: IpcMessage, id: WindowId) { - let Ok(result) = serde_json::from_value::(msg.params()) else { - return; - }; - - let Some(view) = self.webviews.get(&id) else { - return; - }; - - view.desktop_context.query.send(result); - } - #[cfg(all(feature = "devtools", debug_assertions,))] pub fn handle_devserver_msg(&mut self, msg: dioxus_devtools::DevserverMsg) { use dioxus_devtools::DevserverMsg; diff --git a/packages/desktop/src/assets.rs b/packages/desktop/src/assets.rs index a02630811f..9e585d1e7e 100644 --- a/packages/desktop/src/assets.rs +++ b/packages/desktop/src/assets.rs @@ -1,4 +1,4 @@ -use dioxus_core::prelude::{Runtime, ScopeId}; +use dioxus_core::prelude::Callback; use rustc_hash::FxHashMap; use std::{cell::RefCell, rc::Rc}; use wry::{http::Request, RequestAsyncResponder}; @@ -7,20 +7,17 @@ use wry::{http::Request, RequestAsyncResponder}; pub type AssetRequest = Request>; pub struct AssetHandler { - f: Box, - scope: ScopeId, + f: Callback<(AssetRequest, RequestAsyncResponder)>, } #[derive(Clone)] pub struct AssetHandlerRegistry { - dom_rt: Rc, handlers: Rc>>, } impl AssetHandlerRegistry { - pub fn new(dom_rt: Rc) -> Self { + pub fn new() -> Self { AssetHandlerRegistry { - dom_rt, handlers: Default::default(), } } @@ -36,21 +33,16 @@ impl AssetHandlerRegistry { responder: RequestAsyncResponder, ) { if let Some(handler) = self.handlers.borrow().get(name) { - // And run the handler in the scope of the component that created it - self.dom_rt - .on_scope(handler.scope, || (handler.f)(request, responder)); + handler.f.call((request, responder)); } } pub fn register_handler( &self, name: String, - f: Box, - scope: ScopeId, + f: Callback<(AssetRequest, RequestAsyncResponder)>, ) { - self.handlers - .borrow_mut() - .insert(name, AssetHandler { f, scope }); + self.handlers.borrow_mut().insert(name, AssetHandler { f }); } pub fn remove_handler(&self, name: &str) -> Option { diff --git a/packages/desktop/src/desktop_context.rs b/packages/desktop/src/desktop_context.rs index 05ba564b2b..9984baaca3 100644 --- a/packages/desktop/src/desktop_context.rs +++ b/packages/desktop/src/desktop_context.rs @@ -11,6 +11,7 @@ use dioxus_core::{ prelude::{current_scope_id, ScopeId}, VirtualDom, }; +use dioxus_document::Eval; use std::rc::{Rc, Weak}; use tao::{ event::Event, @@ -55,8 +56,6 @@ pub struct DesktopService { pub(crate) shared: Rc, - /// The receiver for queries about the current window - pub(super) query: QueryEngine, pub(crate) asset_handlers: AssetHandlerRegistry, pub(crate) file_hover: NativeFileHover, @@ -87,7 +86,6 @@ impl DesktopService { shared, asset_handlers, file_hover, - query: Default::default(), #[cfg(target_os = "ios")] views: Default::default(), } @@ -252,6 +250,11 @@ impl DesktopService { self.asset_handlers.remove_handler(name).map(|_| ()) } + /// Eval a javascript string into the current document + pub fn eval(&self, js: String) -> Eval { + todo!() + } + /// Push an objc view to the window #[cfg(target_os = "ios")] pub fn push_view(&self, view: objc_id::ShareId) { diff --git a/packages/desktop/src/element.rs b/packages/desktop/src/element.rs index 54896c9b50..8d005bb52a 100644 --- a/packages/desktop/src/element.rs +++ b/packages/desktop/src/element.rs @@ -1,7 +1,7 @@ use dioxus_core::ElementId; use dioxus_html::{ geometry::{PixelsRect, PixelsSize, PixelsVector2D}, - MountedResult, RenderedElementBacking, + MountedResult, RenderedElementBacking, ScrollBehavior, }; use crate::desktop_context::DesktopContext; @@ -15,127 +15,116 @@ pub struct DesktopElement { impl DesktopElement { pub(crate) fn new(id: ElementId, webview: DesktopContext) -> Self { - Self { id, webview, query } + Self { id, webview } } } -macro_rules! scripted_getter { - ($meth_name:ident, $script:literal, $output_type:path) => { - fn $meth_name( - &self, - ) -> std::pin::Pin< - Box>>, - > { - let script = format!($script, id = self.id.0); - - let fut = self - .query - .new_query::>(&script, self.webview.clone()) - .resolve(); - Box::pin(async move { - match fut.await { - Ok(Some(res)) => Ok(res), - Ok(None) => MountedResult::Err(dioxus_html::MountedError::OperationFailed( - Box::new(DesktopQueryError::FailedToQuery), - )), - Err(err) => MountedResult::Err(dioxus_html::MountedError::OperationFailed( - Box::new(err), - )), - } - }) - } - }; -} +pub type EvalFuture = std::pin::Pin>>>; impl RenderedElementBacking for DesktopElement { fn as_any(&self) -> &dyn std::any::Any { self } - scripted_getter!( - get_scroll_offset, - "return [window.interpreter.getScrollLeft({id}), window.interpreter.getScrollTop({id})]", - PixelsVector2D - ); - - scripted_getter!( - get_scroll_size, - "return [window.interpreter.getScrollWidth({id}), window.interpreter.getScrollHeight({id})]", - PixelsSize - ); - - scripted_getter!( - get_client_rect, - "return window.interpreter.getClientRect({id});", - PixelsRect - ); - - fn scroll_to( - &self, - behavior: dioxus_html::ScrollBehavior, - ) -> std::pin::Pin>>> { - let script = format!( - "return window.interpreter.scrollTo({}, {});", - self.id.0, - serde_json::to_string(&behavior).expect("Failed to serialize ScrollBehavior") - ); - - let fut = self - .query - .new_query::(&script, self.webview.clone()) - .resolve(); - Box::pin(async move { - match fut.await { - Ok(true) => Ok(()), - Ok(false) => MountedResult::Err(dioxus_html::MountedError::OperationFailed( - Box::new(DesktopQueryError::FailedToQuery), - )), - Err(err) => { - MountedResult::Err(dioxus_html::MountedError::OperationFailed(Box::new(err))) - } - } - }) + fn get_scroll_offset(&self) -> EvalFuture { + let id = self.id.0; + let res = self.webview.eval(format!( + "return [window.interpreter.getScrollLeft({id}), window.interpreter.getScrollTop({id})]" + )); + todo!() + // Box::pin(res.recv_as()) + // Box::pin(async { Err(dioxus_html::MountedError::NotSupported) }) } - fn set_focus( - &self, - focus: bool, - ) -> std::pin::Pin>>> { - let script = format!( - "return window.interpreter.setFocus({}, {});", - self.id.0, focus - ); - - let fut = self - .query - .new_query::(&script, self.webview.clone()) - .resolve(); - - Box::pin(async move { - match fut.await { - Ok(true) => Ok(()), - Ok(false) => MountedResult::Err(dioxus_html::MountedError::OperationFailed( - Box::new(DesktopQueryError::FailedToQuery), - )), - Err(err) => { - MountedResult::Err(dioxus_html::MountedError::OperationFailed(Box::new(err))) - } - } - }) + fn get_scroll_size(&self) -> EvalFuture { + todo!() + // Box::pin(async { Err(dioxus_html::MountedError::NotSupported) }) } -} -#[derive(Debug)] -enum DesktopQueryError { - FailedToQuery, -} + fn get_client_rect(&self) -> EvalFuture { + todo!() + // Box::pin(async { Err(dioxus_html::MountedError::NotSupported) }) + } -impl std::fmt::Display for DesktopQueryError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - DesktopQueryError::FailedToQuery => write!(f, "Failed to query the element"), - } + fn scroll_to(&self, _behavior: ScrollBehavior) -> EvalFuture<()> { + todo!() + // Box::pin(async { Err(dioxus_html::MountedError::NotSupported) }) + } + + fn set_focus(&self, _focus: bool) -> EvalFuture<()> { + todo!() + // Box::pin(async { Err(dioxus_html::MountedError::NotSupported) }) } -} -impl std::error::Error for DesktopQueryError {} + // scripted_getter!( + // get_scroll_offset, + // "return [window.interpreter.getScrollLeft({id}), window.interpreter.getScrollTop({id})]", + // PixelsVector2D + // ); + + // scripted_getter!( + // get_scroll_size, + // "return [window.interpreter.getScrollWidth({id}), window.interpreter.getScrollHeight({id})]", + // PixelsSize + // ); + + // scripted_getter!( + // get_client_rect, + // "return window.interpreter.getClientRect({id});", + // PixelsRect + // ); + + // fn scroll_to( + // &self, + // behavior: dioxus_html::ScrollBehavior, + // ) -> std::pin::Pin>>> { + // let script = format!( + // "return window.interpreter.scrollTo({}, {});", + // self.id.0, + // serde_json::to_string(&behavior).expect("Failed to serialize ScrollBehavior") + // ); + + // let fut = self + // .query + // .new_query::(&script, self.webview.clone()) + // .resolve(); + // Box::pin(async move { + // match fut.await { + // Ok(true) => Ok(()), + // Ok(false) => MountedResult::Err(dioxus_html::MountedError::OperationFailed( + // Box::new(DesktopQueryError::FailedToQuery), + // )), + // Err(err) => { + // MountedResult::Err(dioxus_html::MountedError::OperationFailed(Box::new(err))) + // } + // } + // }) + // } + + // fn set_focus( + // &self, + // focus: bool, + // ) -> std::pin::Pin>>> { + // let script = format!( + // "return window.interpreter.setFocus({}, {});", + // self.id.0, focus + // ); + + // let fut = self + // .query + // .new_query::(&script, self.webview.clone()) + // .resolve(); + + // Box::pin(async move { + // match fut.await { + // Ok(true) => Ok(()), + // Ok(false) => MountedResult::Err(dioxus_html::MountedError::OperationFailed( + // Box::new(DesktopQueryError::FailedToQuery), + // )), + // Err(err) => { + // MountedResult::Err(dioxus_html::MountedError::OperationFailed(Box::new(err))) + // } + // } + // }) + // } +} diff --git a/packages/desktop/src/hooks.rs b/packages/desktop/src/hooks.rs index 39f361950b..f2a86bf94e 100644 --- a/packages/desktop/src/hooks.rs +++ b/packages/desktop/src/hooks.rs @@ -52,13 +52,16 @@ pub fn use_asset_handler( name: &str, handler: impl Fn(AssetRequest, RequestAsyncResponder) + 'static, ) { + let callback = use_callback(move |args: (AssetRequest, RequestAsyncResponder)| { + // + todo!() + }); + use_hook_with_cleanup( || { - crate::window().asset_handlers.register_handler( - name.to_string(), - Box::new(handler), - current_scope_id().unwrap(), - ); + crate::window() + .asset_handlers + .register_handler(name.to_string(), callback); Rc::new(name.to_string()) }, diff --git a/packages/desktop/src/ipc.rs b/packages/desktop/src/ipc.rs index e321b2b6c3..a76bb9e442 100644 --- a/packages/desktop/src/ipc.rs +++ b/packages/desktop/src/ipc.rs @@ -14,6 +14,9 @@ pub enum UserWindowEvent { /// Poll the virtualdom Poll(WindowId), + /// Handle an ipc message eminating from the window.postMessage of a given webview + Ipc { id: WindowId, msg: IpcMessage }, + /// Handle a hotreload event, basically telling us to update our templates #[cfg(all(feature = "devtools", debug_assertions))] HotReloadEvent(dioxus_devtools::DevserverMsg), @@ -41,8 +44,6 @@ pub struct IpcMessage { #[derive(Deserialize, Serialize, Debug, Clone)] pub enum IpcMethod<'a> { FileDialog, - UserEvent, - Query, BrowserOpen, Initialize, Other(&'a str), @@ -52,8 +53,6 @@ impl IpcMessage { pub(crate) fn method(&self) -> IpcMethod { match self.method.as_str() { "file_dialog" => IpcMethod::FileDialog, - "user_event" => IpcMethod::UserEvent, - "query" => IpcMethod::Query, "browser_open" => IpcMethod::BrowserOpen, "initialize" => IpcMethod::Initialize, _ => IpcMethod::Other(&self.method), diff --git a/packages/desktop/src/launch.rs b/packages/desktop/src/launch.rs index 9201b5aee5..1fd0df28b9 100644 --- a/packages/desktop/src/launch.rs +++ b/packages/desktop/src/launch.rs @@ -47,8 +47,6 @@ pub fn launch_virtual_dom_blocking(virtual_dom: VirtualDom, desktop_config: Conf UserWindowEvent::Ipc { id, msg } => match msg.method() { IpcMethod::Initialize => app.handle_initialize_msg(id), IpcMethod::FileDialog => app.handle_file_dialog_msg(msg, id), - IpcMethod::UserEvent => {} - IpcMethod::Query => app.handle_query_msg(msg, id), IpcMethod::BrowserOpen => app.handle_browser_open(msg), IpcMethod::Other(_) => {} }, diff --git a/packages/desktop/src/webview.rs b/packages/desktop/src/webview.rs index 195136eb6f..d7d4d7da24 100644 --- a/packages/desktop/src/webview.rs +++ b/packages/desktop/src/webview.rs @@ -83,13 +83,12 @@ impl WebviewEdits { return Default::default(); }; - let query = desktop_context.query.clone(); let recent_file = desktop_context.file_hover.clone(); // check for a mounted event placeholder and replace it with a desktop specific element let as_any = match data { dioxus_html::EventData::Mounted => { - let element = DesktopElement::new(element, desktop_context.clone(), query); + let element = DesktopElement::new(element, desktop_context.clone()); Rc::new(PlatformEventData::new(Box::new(element))) } dioxus_html::EventData::Drag(ref drag) => { diff --git a/packages/document/src/error.rs b/packages/document/src/error.rs index d5bbd8b237..896e8e3aee 100644 --- a/packages/document/src/error.rs +++ b/packages/document/src/error.rs @@ -16,6 +16,9 @@ pub enum EvalError { /// Represents an error communicating between JavaScript and Rust. Communication(String), + + /// Represents an error deserializing the result of an eval + Deserialization(serde_json::Error), } impl Display for EvalError { @@ -25,6 +28,7 @@ impl Display for EvalError { EvalError::Finished => write!(f, "EvalError::Finished - eval has already ran"), EvalError::InvalidJs(_) => write!(f, "EvalError::InvalidJs - the provided javascript is invalid"), EvalError::Communication(_) => write!(f, "EvalError::Communication - there was an error trying to communicate with between javascript and rust"), + EvalError::Deserialization(_) => write!(f, "EvalError::Deserialization - there was an error trying to deserialize the result of an eval"), } } } diff --git a/packages/document/src/eval.rs b/packages/document/src/eval.rs index 75c720379f..aa57eed36b 100644 --- a/packages/document/src/eval.rs +++ b/packages/document/src/eval.rs @@ -29,4 +29,9 @@ impl Eval { .await .map_err(|_| EvalError::Communication("eval channel closed".to_string()))? } + + pub async fn recv_as(self) -> Result { + let res = self.recv().await?; + serde_json::from_str(&res).map_err(|e| EvalError::Deserialization(e)) + } } From a0f51d5f5ff654cccc5748b21a58957f5fa66bc8 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 22 Aug 2024 00:21:23 -0700 Subject: [PATCH 040/139] clean up desktop to use callback --- examples/dynamic_asset.rs | 13 +++++---- packages/desktop/src/desktop_context.rs | 36 +++---------------------- packages/desktop/src/hooks.rs | 5 ++-- packages/desktop/src/protocol.rs | 4 +-- packages/desktop/src/webview.rs | 2 +- 5 files changed, 14 insertions(+), 46 deletions(-) diff --git a/examples/dynamic_asset.rs b/examples/dynamic_asset.rs index f97456ec24..0b77f86f06 100644 --- a/examples/dynamic_asset.rs +++ b/examples/dynamic_asset.rs @@ -15,18 +15,17 @@ fn main() { fn app() -> Element { use_asset_handler("logos", |request, response| { - todo!() - // // We get the original path - make sure you handle that! - // if request.uri().path() != "/logos/logo.png" { - // return; - // } + // We get the original path - make sure you handle that! + if request.uri().path() != "/logos/logo.png" { + return; + } - // response.respond(Response::new(include_bytes!("./assets/logo.png").to_vec())); + response.respond(Response::new(include_bytes!("./assets/logo.png").to_vec())); }); rsx! { document::Stylesheet { href: STYLE } h1 { "Dynamic Assets" } - img { src: "custom://logos/logo.png" } + img { src: "/logos/logo.png" } } } diff --git a/packages/desktop/src/desktop_context.rs b/packages/desktop/src/desktop_context.rs index 9984baaca3..f1fb09be2c 100644 --- a/packages/desktop/src/desktop_context.rs +++ b/packages/desktop/src/desktop_context.rs @@ -5,12 +5,9 @@ use crate::{ ipc::UserWindowEvent, shortcut::{HotKey, ShortcutHandle, ShortcutRegistryError}, webview::WebviewInstance, - AssetRequest, Config, WryEventHandler, -}; -use dioxus_core::{ - prelude::{current_scope_id, ScopeId}, - VirtualDom, + Config, WryEventHandler, }; +use dioxus_core::{prelude::ScopeId, VirtualDom}; use dioxus_document::Eval; use std::rc::{Rc, Weak}; use tao::{ @@ -18,7 +15,7 @@ use tao::{ event_loop::EventLoopWindowTarget, window::{Fullscreen as WryFullscreen, Window, WindowId}, }; -use wry::{RequestAsyncResponder, WebView}; +use wry::WebView; #[cfg(target_os = "ios")] use tao::platform::ios::WindowExtIOS; @@ -223,33 +220,6 @@ impl DesktopService { self.shared.shortcut_manager.remove_all() } - /// Provide a callback to handle asset loading yourself. - /// If the ScopeId isn't provided, defaults to a global handler. - /// Note that the handler is namespaced by name, not ScopeId. - /// - /// When the component is dropped, the handler is removed. - /// - /// See [`use_asset_handle`](crate::use_asset_handle) for a convenient hook. - pub fn register_asset_handler( - &self, - name: String, - handler: Box, - scope: Option, - ) { - self.asset_handlers.register_handler( - name, - handler, - scope.unwrap_or(current_scope_id().unwrap_or(ScopeId(0))), - ) - } - - /// Removes an asset handler by its identifier. - /// - /// Returns `None` if the handler did not exist. - pub fn remove_asset_handler(&self, name: &str) -> Option<()> { - self.asset_handlers.remove_handler(name).map(|_| ()) - } - /// Eval a javascript string into the current document pub fn eval(&self, js: String) -> Eval { todo!() diff --git a/packages/desktop/src/hooks.rs b/packages/desktop/src/hooks.rs index 082b07756b..cbf9fa8b3d 100644 --- a/packages/desktop/src/hooks.rs +++ b/packages/desktop/src/hooks.rs @@ -5,7 +5,7 @@ use crate::{ ShortcutHandle, ShortcutRegistryError, WryEventHandler, }; use dioxus_core::{ - prelude::{consume_context, current_scope_id, use_hook_with_cleanup, RuntimeGuard}, + prelude::{consume_context, use_hook_with_cleanup, RuntimeGuard}, use_hook, Runtime, }; @@ -65,8 +65,7 @@ pub fn use_asset_handler( mut handler: impl FnMut(AssetRequest, RequestAsyncResponder) + 'static, ) { let callback = use_callback(move |args: (AssetRequest, RequestAsyncResponder)| { - // - todo!() + handler(args.0, args.1); }); use_hook_with_cleanup( diff --git a/packages/desktop/src/protocol.rs b/packages/desktop/src/protocol.rs index 8cdaaeba5c..98777f90f4 100644 --- a/packages/desktop/src/protocol.rs +++ b/packages/desktop/src/protocol.rs @@ -100,8 +100,8 @@ fn serve_asset(request: Request>) -> Result>> { .body(std::fs::read(asset)?)?); } - // todo: we want to move the custom assets onto a different protocol or something - // if let Some(name) = path.iter().next().unwrap().to_str() { + // // todo: we want to move the custom assets onto a different protocol or something + // if let Some(name) = asset.iter().next().unwrap().to_str() { // if asset_handlers.has_handler(name) { // return asset_handlers.handle_request(name, request, responder); // } diff --git a/packages/desktop/src/webview.rs b/packages/desktop/src/webview.rs index d7d4d7da24..e0f6829244 100644 --- a/packages/desktop/src/webview.rs +++ b/packages/desktop/src/webview.rs @@ -188,7 +188,7 @@ impl WebviewInstance { let mut web_context = WebContext::new(cfg.data_dir.clone()); let edit_queue = WryQueue::default(); - let asset_handlers = AssetHandlerRegistry::new(dom.runtime()); + let asset_handlers = AssetHandlerRegistry::new(); let edits = WebviewEdits::new(dom.runtime(), edit_queue.clone()); let file_hover = NativeFileHover::default(); let headless = !cfg.window.window.visible; From 6533740f5f1a361dc84af0aebce53b2e5c53bdcd Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 22 Aug 2024 00:45:02 -0700 Subject: [PATCH 041/139] clean up document a bit --- packages/desktop/src/document.rs | 9 ++ packages/document/src/document.rs | 100 +++++++------- packages/document/src/head.rs | 8 +- packages/document/src/lib.rs | 1 - packages/fullstack/src/document/web.rs | 37 +++--- packages/fullstack/src/launch.rs | 7 +- packages/liveview/src/eval.rs | 16 +++ packages/web/src/bindings.rs | 16 +-- packages/web/src/document.rs | 176 ++++++------------------- 9 files changed, 146 insertions(+), 224 deletions(-) diff --git a/packages/desktop/src/document.rs b/packages/desktop/src/document.rs index c39c9bff02..dd3086b0e8 100644 --- a/packages/desktop/src/document.rs +++ b/packages/desktop/src/document.rs @@ -48,4 +48,13 @@ impl Document for DesktopDocument { fn as_any(&self) -> &dyn std::any::Any { self } + + fn create_head_element( + &self, + name: &str, + attributes: Vec<(&str, String)>, + contents: Option, + ) { + todo!("create the head element stuff") + } } diff --git a/packages/document/src/document.rs b/packages/document/src/document.rs index e9466667e7..a73acadf2c 100644 --- a/packages/document/src/document.rs +++ b/packages/document/src/document.rs @@ -9,64 +9,76 @@ pub trait Document { fn eval(&self, js: String) -> Eval; /// Set the title of the document - fn set_title(&self, title: String) { - _ = self.eval(format!("document.title = {title:?};")); - } + fn set_title(&self, title: String); + + /// Create a new element in the head + fn create_head_element( + &self, + name: &str, + attributes: Vec<(&str, String)>, + contents: Option, + ); - /// Create a new meta tag + /// Create a new meta tag in the head fn create_meta(&self, props: MetaProps) { - let attributes = props.attributes(); - let js = create_element_in_head("meta", &attributes, None); - _ = self.eval(js); + self.create_head_element("meta", props.attributes(), None); } - /// Create a new script tag + /// Create a new script tag in the head fn create_script(&self, props: ScriptProps) { - let attributes = props.attributes(); - let js = create_element_in_head("script", &attributes, props.script_contents()); - _ = self.eval(js); + self.create_head_element("script", props.attributes(), props.script_contents()); } - /// Create a new style tag + /// Create a new style tag in the head fn create_style(&self, props: StyleProps) { - let attributes = props.attributes(); - let js = create_element_in_head("style", &attributes, props.style_contents()); - _ = self.eval(js); + self.create_head_element("style", props.attributes(), props.style_contents()); } - /// Create a new link tag - fn create_link(&self, props: head::LinkProps) { - let attributes = props.attributes(); - let js = create_element_in_head("link", &attributes, None); - _ = self.eval(js); + /// Create a new link tag in the head + fn create_link(&self, props: LinkProps) { + self.create_head_element("link", props.attributes(), None); } /// Get a reference to the document as `dyn Any` fn as_any(&self) -> &dyn std::any::Any; } -fn create_element_in_head( - tag: &str, - attributes: &[(&str, String)], - children: Option, -) -> String { - fn format_attributes(attributes: &[(&str, String)]) -> String { - let mut formatted = String::from("["); - for (key, value) in attributes { - formatted.push_str(&format!("[{key:?}, {value:?}],")); - } - if formatted.ends_with(',') { - formatted.pop(); - } - formatted.push(']'); - formatted - } +// _ = self.eval(format!("document.title = {title:?};")); +// let attributes = props.attributes(); +// let js = create_element_in_head("meta", &attributes, None); +// _ = self.eval(js); +// let attributes = props.attributes(); +// let js = create_element_in_head("script", &attributes, props.script_contents()); +// _ = self.eval(js); +// let attributes = props.attributes(); +// let js = create_element_in_head("style", &attributes, props.style_contents()); +// _ = self.eval(js); +// let attributes = props.attributes(); +// let js = create_element_in_head("link", &attributes, None); +// _ = self.eval(js); - let helpers: &str = todo!(); - // let helpers = include_str!("../js/head.js"); - let attributes = format_attributes(attributes); - let children = children - .map(|c| format!("\"{c}\"")) - .unwrap_or("null".to_string()); - format!(r#"{helpers};window.createElementInHead("{tag}", {attributes}, {children});"#) -} +// fn create_element_in_head( +// tag: &str, +// attributes: &[(&str, String)], +// children: Option, +// ) -> String { +// fn format_attributes(attributes: &[(&str, String)]) -> String { +// let mut formatted = String::from("["); +// for (key, value) in attributes { +// formatted.push_str(&format!("[{key:?}, {value:?}],")); +// } +// if formatted.ends_with(',') { +// formatted.pop(); +// } +// formatted.push(']'); +// formatted +// } + +// let helpers: &str = todo!(); +// // let helpers = include_str!("../js/head.js"); +// let attributes = format_attributes(attributes); +// let children = children +// .map(|c| format!("\"{c}\"")) +// .unwrap_or("null".to_string()); +// format!(r#"{helpers};window.createElementInHead("{tag}", {attributes}, {children});"#) +// } diff --git a/packages/document/src/head.rs b/packages/document/src/head.rs index 7a599371ee..38523bbe2b 100644 --- a/packages/document/src/head.rs +++ b/packages/document/src/head.rs @@ -123,7 +123,7 @@ pub struct MetaProps { } impl MetaProps { - pub(crate) fn attributes(&self) -> Vec<(&'static str, String)> { + pub fn attributes(&self) -> Vec<(&'static str, String)> { let mut attributes = Vec::new(); if let Some(property) = &self.property { attributes.push(("property", property.clone())); @@ -196,7 +196,7 @@ pub struct ScriptProps { } impl ScriptProps { - pub(crate) fn attributes(&self) -> Vec<(&'static str, String)> { + pub fn attributes(&self) -> Vec<(&'static str, String)> { let mut attributes = Vec::new(); if let Some(defer) = &self.defer { attributes.push(("defer", defer.to_string())); @@ -286,7 +286,7 @@ pub struct StyleProps { } impl StyleProps { - pub(crate) fn attributes(&self) -> Vec<(&'static str, String)> { + pub fn attributes(&self) -> Vec<(&'static str, String)> { let mut attributes = Vec::new(); if let Some(href) = &self.href { attributes.push(("href", href.clone())); @@ -374,7 +374,7 @@ pub struct LinkProps { } impl LinkProps { - pub(crate) fn attributes(&self) -> Vec<(&'static str, String)> { + pub fn attributes(&self) -> Vec<(&'static str, String)> { let mut attributes = Vec::new(); if let Some(rel) = &self.rel { attributes.push(("rel", rel.clone())); diff --git a/packages/document/src/lib.rs b/packages/document/src/lib.rs index f897d3743e..01b41d4f4c 100644 --- a/packages/document/src/lib.rs +++ b/packages/document/src/lib.rs @@ -10,7 +10,6 @@ pub use document::*; pub use error::*; pub use eval::*; pub use head::*; -pub use title::*; /// Get the document provider for the current platform or a no-op provider if the platform doesn't document functionality. pub fn document() -> Rc { diff --git a/packages/fullstack/src/document/web.rs b/packages/fullstack/src/document/web.rs index eafeb149ee..ab46d9ded1 100644 --- a/packages/fullstack/src/document/web.rs +++ b/packages/fullstack/src/document/web.rs @@ -11,42 +11,35 @@ fn head_element_written_on_server() -> bool { .unwrap_or_default() } -pub(crate) struct FullstackWebDocument; +pub(crate) struct FullstackWebDocument { + document: WebDocument, +} impl Document for FullstackWebDocument { - fn set_title(&self, title: String) { + fn create_head_element( + &self, + name: &str, + attributes: Vec<(&str, String)>, + contents: Option, + ) { if head_element_written_on_server() { return; } - WebDocument.set_title(title); - } - fn create_meta(&self, props: MetaProps) { - if head_element_written_on_server() { - return; - } - WebDocument.create_meta(props); + self.document + .create_head_element(name, attributes, contents); } - fn create_script(&self, props: ScriptProps) { + fn set_title(&self, title: String) { if head_element_written_on_server() { return; } - WebDocument.create_script(props); - } - fn create_style(&self, props: StyleProps) { - if head_element_written_on_server() { - return; - } - WebDocument.create_style(props); + self.document.set_title(title); } - fn create_link(&self, props: LinkProps) { - if head_element_written_on_server() { - return; - } - WebDocument.create_link(props); + fn eval(&self, js: String) -> dioxus_document::Eval { + self.document.eval(js) } fn as_any(&self) -> &dyn std::any::Any { diff --git a/packages/fullstack/src/launch.rs b/packages/fullstack/src/launch.rs index 6d8db1b97c..a3b1f2d269 100644 --- a/packages/fullstack/src/launch.rs +++ b/packages/fullstack/src/launch.rs @@ -62,9 +62,10 @@ pub fn launch( #[cfg(feature = "document")] let factory = move || { let mut vdom = factory(); - let document = std::rc::Rc::new(crate::document::web::FullstackWebDocument) - as std::rc::Rc; - vdom.provide_root_context(document); + todo!("Fullstack web document???"); + // let document = std::rc::Rc::new(crate::document::web::FullstackWebDocument) + // as std::rc::Rc; + // vdom.provide_root_context(document); vdom }; diff --git a/packages/liveview/src/eval.rs b/packages/liveview/src/eval.rs index 11064bb1cb..c26621f5b0 100644 --- a/packages/liveview/src/eval.rs +++ b/packages/liveview/src/eval.rs @@ -18,9 +18,25 @@ pub struct LiveviewDocument { } impl Document for LiveviewDocument { + fn eval(&self, js: String) -> dioxus_document::Eval { + todo!() + } fn as_any(&self) -> &dyn std::any::Any { self } + + fn set_title(&self, title: String) { + todo!() + } + + fn create_head_element( + &self, + name: &str, + attributes: Vec<(&str, String)>, + contents: Option, + ) { + todo!() + } } /// Represents a liveview-target's JavaScript evaluator. diff --git a/packages/web/src/bindings.rs b/packages/web/src/bindings.rs index 71280c5e55..efb5f062d3 100644 --- a/packages/web/src/bindings.rs +++ b/packages/web/src/bindings.rs @@ -1,20 +1,6 @@ -/// -#[wasm_bindgen::prelude::wasm_bindgen] -pub struct JSOwner { - _owner: Box, -} - -impl JSOwner { - /// - pub fn new(owner: impl std::any::Any) -> Self { - Self { - _owner: Box::new(owner), - } - } -} - use std::any::Any; +use dioxus_html::FileEngine; // use dioxus_html::FileEngine; use futures_channel::oneshot; use js_sys::Uint8Array; diff --git a/packages/web/src/document.rs b/packages/web/src/document.rs index c3ab0ce00c..4d73407113 100644 --- a/packages/web/src/document.rs +++ b/packages/web/src/document.rs @@ -1,152 +1,58 @@ -use crate::bindings::JSOwner; +use std::rc::Rc; + use dioxus_core::ScopeId; -use dioxus_document::{Document, EvalError}; -// use dioxus_html::document::{Document, EvalError, Evaluator}; -// use dioxus_html::document::{Document, EvalError, Evaluator, WeakDioxusChannel, WebDioxusChannel}; -use generational_box::{AnyStorage, GenerationalBox, UnsyncStorage}; -use js_sys::Function; -use serde::Serialize; -use serde_json::Value; -use std::future::Future; -use std::pin::Pin; -use std::{rc::Rc, str::FromStr}; -use wasm_bindgen::prelude::*; +use dioxus_document::{Document, Eval}; /// Provides the WebEvalProvider through [`ScopeId::provide_context`]. pub fn init_document() { - let provider: Rc = Rc::new(WebDocument); + let window = web_sys::window().unwrap(); + let document = window.document().unwrap(); + let provider: Rc = Rc::new(WebDocument { document }); + if ScopeId::ROOT.has_context::>().is_none() { ScopeId::ROOT.provide_context(provider); } } /// The web-target's document provider. -pub struct WebDocument; +pub struct WebDocument { + document: web_sys::Document, +} + impl Document for WebDocument { - // fn new_evaluator(&self, js: String) -> GenerationalBox> { - // WebEvaluator::create(js) - // } + fn eval(&self, js: String) -> Eval { + let (tx, eval) = Eval::from_parts(); + let res = js_sys::eval(&js); + match res { + Ok(ok) => todo!(), + Err(err) => todo!(), + }; + eval + } fn as_any(&self) -> &dyn std::any::Any { self } -} - -/// Required to avoid blocking the Rust WASM thread. -const PROMISE_WRAPPER: &str = r#" - return new Promise(async (resolve, _reject) => { - {JS_CODE} - resolve(null); - }); -"#; - -type NextPoll = Pin>>>; - -// /// Represents a web-target's JavaScript evaluator. -// struct WebEvaluator { -// channels: WeakDioxusChannel, -// next_future: Option, -// result: Option>, -// } - -// impl WebEvaluator { -// /// Creates a new evaluator for web-based targets. -// fn create(js: String) -> GenerationalBox> { -// let owner = UnsyncStorage::owner(); - -// let generational_box = owner.invalid(); - -// // add the drop handler to DioxusChannel so that it gets dropped when the channel is dropped in js -// let channels = WebDioxusChannel::new(JSOwner::new(owner)); -// // The Rust side of the channel is a weak reference to the DioxusChannel -// let weak_channels = channels.weak(); - -// // Wrap the evaluated JS in a promise so that wasm can continue running (send/receive data from js) -// let code = PROMISE_WRAPPER.replace("{JS_CODE}", &js); - -// let result = match Function::new_with_args("dioxus", &code).call1(&JsValue::NULL, &channels) -// { -// Ok(result) => { -// if let Ok(stringified) = js_sys::JSON::stringify(&result) { -// if !stringified.is_undefined() && stringified.is_valid_utf16() { -// let string: String = stringified.into(); -// Value::from_str(&string).map_err(|e| { -// EvalError::Communication(format!("Failed to parse result - {}", e)) -// }) -// } else { -// Err(EvalError::Communication( -// "Failed to stringify result".into(), -// )) -// } -// } else { -// Err(EvalError::Communication( -// "Failed to stringify result".into(), -// )) -// } -// } -// Err(err) => Err(EvalError::InvalidJs( -// err.as_string().unwrap_or("unknown".to_string()), -// )), -// }; - -// generational_box.set(Box::new(Self { -// channels: weak_channels, -// result: Some(result), -// next_future: None, -// }) as Box); - -// generational_box -// } -// } - -// impl Evaluator for WebEvaluator { -// /// Runs the evaluated JavaScript. -// fn poll_join( -// &mut self, -// _cx: &mut std::task::Context<'_>, -// ) -> std::task::Poll> { -// if let Some(result) = self.result.take() { -// std::task::Poll::Ready(result) -// } else { -// std::task::Poll::Ready(Err(EvalError::Finished)) -// } -// } - -// /// Sends a message to the evaluated JavaScript. -// fn send(&self, data: serde_json::Value) -> Result<(), EvalError> { -// let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - -// let data = match data.serialize(&serializer) { -// Ok(d) => d, -// Err(e) => return Err(EvalError::Communication(e.to_string())), -// }; - -// self.channels.rust_send(data); -// Ok(()) -// } + fn set_title(&self, title: String) { + self.document.set_title(&title); + } -// /// Gets an UnboundedReceiver to receive messages from the evaluated JavaScript. -// fn poll_recv( -// &mut self, -// context: &mut std::task::Context<'_>, -// ) -> std::task::Poll> { -// if self.next_future.is_none() { -// let channels: WebDioxusChannel = self.channels.clone().into(); -// let pinned = Box::pin(async move { -// let fut = channels.rust_recv(); -// let data = fut.await; -// serde_wasm_bindgen::from_value::(data) -// .map_err(|err| EvalError::Communication(err.to_string())) -// }); -// self.next_future = Some(pinned); -// } -// let fut = self.next_future.as_mut().unwrap(); -// let mut pinned = std::pin::pin!(fut); -// let result = pinned.as_mut().poll(context); -// if result.is_ready() { -// self.next_future = None; -// } -// result -// } -// } + fn create_head_element( + &self, + name: &str, + attributes: Vec<(&str, String)>, + contents: Option, + ) { + if let Some(head) = self.document.head() { + let element = self.document.create_element(name).unwrap(); + for (name, value) in attributes { + element.set_attribute(name, &value).unwrap(); + } + if let Some(contents) = contents { + element.set_inner_html(&contents); + } + head.append_child(&element.into()).unwrap(); + } + } +} From 97292fb11ca33511c21cd7dbff208fe19696cabd Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 22 Aug 2024 01:02:11 -0700 Subject: [PATCH 042/139] things compile again :) --- packages/desktop/src/element.rs | 13 ++- packages/desktop/src/file_upload.rs | 13 +-- packages/document/src/document.rs | 51 +---------- packages/document/src/eval.rs | 14 ++- packages/html/src/events/mounted.rs | 44 ++++------ packages/liveview/src/element.rs | 130 ++++++++++++++-------------- 6 files changed, 108 insertions(+), 157 deletions(-) diff --git a/packages/desktop/src/element.rs b/packages/desktop/src/element.rs index 8d005bb52a..b288aa6a5a 100644 --- a/packages/desktop/src/element.rs +++ b/packages/desktop/src/element.rs @@ -19,14 +19,13 @@ impl DesktopElement { } } -pub type EvalFuture = std::pin::Pin>>>; - +#[async_trait::async_trait(?Send)] impl RenderedElementBacking for DesktopElement { fn as_any(&self) -> &dyn std::any::Any { self } - fn get_scroll_offset(&self) -> EvalFuture { + async fn get_scroll_offset(&self) -> MountedResult { let id = self.id.0; let res = self.webview.eval(format!( "return [window.interpreter.getScrollLeft({id}), window.interpreter.getScrollTop({id})]" @@ -36,22 +35,22 @@ impl RenderedElementBacking for DesktopElement { // Box::pin(async { Err(dioxus_html::MountedError::NotSupported) }) } - fn get_scroll_size(&self) -> EvalFuture { + async fn get_scroll_size(&self) -> MountedResult { todo!() // Box::pin(async { Err(dioxus_html::MountedError::NotSupported) }) } - fn get_client_rect(&self) -> EvalFuture { + async fn get_client_rect(&self) -> MountedResult { todo!() // Box::pin(async { Err(dioxus_html::MountedError::NotSupported) }) } - fn scroll_to(&self, _behavior: ScrollBehavior) -> EvalFuture<()> { + async fn scroll_to(&self, _behavior: ScrollBehavior) -> MountedResult<()> { todo!() // Box::pin(async { Err(dioxus_html::MountedError::NotSupported) }) } - fn set_focus(&self, _focus: bool) -> EvalFuture<()> { + async fn set_focus(&self, _focus: bool) -> MountedResult<()> { todo!() // Box::pin(async { Err(dioxus_html::MountedError::NotSupported) }) } diff --git a/packages/desktop/src/file_upload.rs b/packages/desktop/src/file_upload.rs index 5b04479207..1fe42aa892 100644 --- a/packages/desktop/src/file_upload.rs +++ b/packages/desktop/src/file_upload.rs @@ -1,23 +1,15 @@ -#![allow(unused)] - use dioxus_html::{ geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint}, input_data::{MouseButton, MouseButtonSet}, point_interaction::{ InteractionElementOffset, InteractionLocation, ModifiersInteraction, PointerInteraction, }, - prelude::{SerializedMouseData, SerializedPointInteraction}, + prelude::SerializedPointInteraction, FileEngine, HasDragData, HasFileData, HasFormData, HasMouseData, }; use serde::Deserialize; -use std::{ - cell::{Cell, RefCell}, - path::PathBuf, - rc::Rc, - str::FromStr, - sync::Arc, -}; +use std::{cell::RefCell, path::PathBuf, rc::Rc, str::FromStr, sync::Arc}; use wry::DragDropEvent; #[derive(Debug, Deserialize)] @@ -163,6 +155,7 @@ impl HasFormData for DesktopFileUploadForm { pub struct NativeFileHover { event: Rc>>, } + impl NativeFileHover { pub fn set(&self, event: DragDropEvent) { self.event.borrow_mut().replace(event); diff --git a/packages/document/src/document.rs b/packages/document/src/document.rs index a73acadf2c..ab45381762 100644 --- a/packages/document/src/document.rs +++ b/packages/document/src/document.rs @@ -1,11 +1,11 @@ -use error::EvalError; - use super::*; -use std::rc::Rc; /// A provider for document-related functionality. By default most methods are driven through [`eval`]. pub trait Document { - /// Create a new evaluator for the document that evaluates JavaScript and facilitates communication between JavaScript and Rust. + /// Get a reference to the document as `dyn Any` + fn as_any(&self) -> &dyn std::any::Any; + + /// Run `eval` against this document, returning an [`Eval`] that can be used to await the result. fn eval(&self, js: String) -> Eval; /// Set the title of the document @@ -38,47 +38,4 @@ pub trait Document { fn create_link(&self, props: LinkProps) { self.create_head_element("link", props.attributes(), None); } - - /// Get a reference to the document as `dyn Any` - fn as_any(&self) -> &dyn std::any::Any; } - -// _ = self.eval(format!("document.title = {title:?};")); -// let attributes = props.attributes(); -// let js = create_element_in_head("meta", &attributes, None); -// _ = self.eval(js); -// let attributes = props.attributes(); -// let js = create_element_in_head("script", &attributes, props.script_contents()); -// _ = self.eval(js); -// let attributes = props.attributes(); -// let js = create_element_in_head("style", &attributes, props.style_contents()); -// _ = self.eval(js); -// let attributes = props.attributes(); -// let js = create_element_in_head("link", &attributes, None); -// _ = self.eval(js); - -// fn create_element_in_head( -// tag: &str, -// attributes: &[(&str, String)], -// children: Option, -// ) -> String { -// fn format_attributes(attributes: &[(&str, String)]) -> String { -// let mut formatted = String::from("["); -// for (key, value) in attributes { -// formatted.push_str(&format!("[{key:?}, {value:?}],")); -// } -// if formatted.ends_with(',') { -// formatted.pop(); -// } -// formatted.push(']'); -// formatted -// } - -// let helpers: &str = todo!(); -// // let helpers = include_str!("../js/head.js"); -// let attributes = format_attributes(attributes); -// let children = children -// .map(|c| format!("\"{c}\"")) -// .unwrap_or("null".to_string()); -// format!(r#"{helpers};window.createElementInHead("{tag}", {attributes}, {children});"#) -// } diff --git a/packages/document/src/eval.rs b/packages/document/src/eval.rs index aa57eed36b..0ebac66b4d 100644 --- a/packages/document/src/eval.rs +++ b/packages/document/src/eval.rs @@ -1,7 +1,10 @@ -#![allow(clippy::await_holding_refcell_ref)] #![doc = include_str!("../docs/eval.md")] use crate::error::EvalError; +use std::{ + future::{Future, IntoFuture}, + pin::Pin, +}; #[doc = include_str!("../docs/eval.md")] pub struct Eval { @@ -35,3 +38,12 @@ impl Eval { serde_json::from_str(&res).map_err(|e| EvalError::Deserialization(e)) } } + +impl IntoFuture for Eval { + type Output = Result; + type IntoFuture = Pin>>; + + fn into_future(self) -> Self::IntoFuture { + Box::pin(self.recv().into_future()) + } +} diff --git a/packages/html/src/events/mounted.rs b/packages/html/src/events/mounted.rs index 2722fba34b..37b583b3a7 100644 --- a/packages/html/src/events/mounted.rs +++ b/packages/html/src/events/mounted.rs @@ -1,47 +1,40 @@ //! Handles querying data from the renderer - -use std::{ - fmt::{Display, Formatter}, - future::Future, - pin::Pin, -}; +use std::fmt::{Display, Formatter}; /// An Element that has been rendered and allows reading and modifying information about it. /// /// Different platforms will have different implementations and different levels of support for this trait. Renderers that do not support specific features will return `None` for those queries. // we can not use async_trait here because it does not create a trait that is object safe +#[async_trait::async_trait(?Send)] pub trait RenderedElementBacking: std::any::Any { /// return self as Any fn as_any(&self) -> &dyn std::any::Any; /// Get the number of pixels that an element's content is scrolled - fn get_scroll_offset(&self) -> Pin>>> { - Box::pin(async { Err(MountedError::NotSupported) }) + async fn get_scroll_offset(&self) -> MountedResult { + Err(MountedError::NotSupported) } /// Get the size of an element's content, including content not visible on the screen due to overflow - #[allow(clippy::type_complexity)] - fn get_scroll_size(&self) -> Pin>>> { - Box::pin(async { Err(MountedError::NotSupported) }) + + async fn get_scroll_size(&self) -> MountedResult { + Err(MountedError::NotSupported) } /// Get the bounding rectangle of the element relative to the viewport (this does not include the scroll position) #[allow(clippy::type_complexity)] - fn get_client_rect(&self) -> Pin>>> { - Box::pin(async { Err(MountedError::NotSupported) }) + async fn get_client_rect(&self) -> MountedResult { + Err(MountedError::NotSupported) } /// Scroll to make the element visible - fn scroll_to( - &self, - _behavior: ScrollBehavior, - ) -> Pin>>> { - Box::pin(async { Err(MountedError::NotSupported) }) + async fn scroll_to(&self, _behavior: ScrollBehavior) -> MountedResult<()> { + Err(MountedError::NotSupported) } /// Set the focus on the element - fn set_focus(&self, _focus: bool) -> Pin>>> { - Box::pin(async { Err(MountedError::NotSupported) }) + async fn set_focus(&self, _focus: bool) -> MountedResult<()> { + Err(MountedError::NotSupported) } } @@ -106,18 +99,15 @@ impl MountedData { /// Scroll to make the element visible #[doc(alias = "scrollIntoView")] - pub fn scroll_to( - &self, - behavior: ScrollBehavior, - ) -> Pin>>> { - self.inner.scroll_to(behavior) + pub async fn scroll_to(&self, behavior: ScrollBehavior) -> MountedResult<()> { + self.inner.scroll_to(behavior).await } /// Set the focus on the element #[doc(alias = "focus")] #[doc(alias = "blur")] - pub fn set_focus(&self, focus: bool) -> Pin>>> { - self.inner.set_focus(focus) + pub async fn set_focus(&self, focus: bool) -> MountedResult<()> { + self.inner.set_focus(focus).await } /// Downcast this event to a concrete event type diff --git a/packages/liveview/src/element.rs b/packages/liveview/src/element.rs index a81bb1a078..7e4593d307 100644 --- a/packages/liveview/src/element.rs +++ b/packages/liveview/src/element.rs @@ -52,71 +52,71 @@ impl RenderedElementBacking for LiveviewElement { self } - scripted_getter!( - get_scroll_offset, - "return [window.interpreter.getScrollLeft({id}), window.interpreter.getScrollTop({id})]", - PixelsVector2D - ); - - scripted_getter!( - get_scroll_size, - "return [window.interpreter.getScrollWidth({id}), window.interpreter.getScrollHeight({id})]", - PixelsSize - ); - - scripted_getter!( - get_client_rect, - "return window.interpreter.getClientRect({id});", - PixelsRect - ); - - fn scroll_to( - &self, - behavior: dioxus_html::ScrollBehavior, - ) -> std::pin::Pin>>> { - let script = format!( - "return window.interpreter.scrollTo({}, {});", - self.id.0, - serde_json::to_string(&behavior).expect("Failed to serialize ScrollBehavior") - ); - - let fut = self.query.new_query::(&script).resolve(); - Box::pin(async move { - match fut.await { - Ok(true) => Ok(()), - Ok(false) => MountedResult::Err(dioxus_html::MountedError::OperationFailed( - Box::new(DesktopQueryError::FailedToQuery), - )), - Err(err) => { - MountedResult::Err(dioxus_html::MountedError::OperationFailed(Box::new(err))) - } - } - }) - } - - fn set_focus( - &self, - focus: bool, - ) -> std::pin::Pin>>> { - let script = format!( - "return window.interpreter.setFocus({}, {});", - self.id.0, focus - ); - - let fut = self.query.new_query::(&script).resolve(); - - Box::pin(async move { - match fut.await { - Ok(true) => Ok(()), - Ok(false) => MountedResult::Err(dioxus_html::MountedError::OperationFailed( - Box::new(DesktopQueryError::FailedToQuery), - )), - Err(err) => { - MountedResult::Err(dioxus_html::MountedError::OperationFailed(Box::new(err))) - } - } - }) - } + // scripted_getter!( + // get_scroll_offset, + // "return [window.interpreter.getScrollLeft({id}), window.interpreter.getScrollTop({id})]", + // PixelsVector2D + // ); + + // scripted_getter!( + // get_scroll_size, + // "return [window.interpreter.getScrollWidth({id}), window.interpreter.getScrollHeight({id})]", + // PixelsSize + // ); + + // scripted_getter!( + // get_client_rect, + // "return window.interpreter.getClientRect({id});", + // PixelsRect + // ); + + // fn scroll_to( + // &self, + // behavior: dioxus_html::ScrollBehavior, + // ) -> std::pin::Pin>>> { + // let script = format!( + // "return window.interpreter.scrollTo({}, {});", + // self.id.0, + // serde_json::to_string(&behavior).expect("Failed to serialize ScrollBehavior") + // ); + + // let fut = self.query.new_query::(&script).resolve(); + // Box::pin(async move { + // match fut.await { + // Ok(true) => Ok(()), + // Ok(false) => MountedResult::Err(dioxus_html::MountedError::OperationFailed( + // Box::new(DesktopQueryError::FailedToQuery), + // )), + // Err(err) => { + // MountedResult::Err(dioxus_html::MountedError::OperationFailed(Box::new(err))) + // } + // } + // }) + // } + + // fn set_focus( + // &self, + // focus: bool, + // ) -> std::pin::Pin>>> { + // let script = format!( + // "return window.interpreter.setFocus({}, {});", + // self.id.0, focus + // ); + + // let fut = self.query.new_query::(&script).resolve(); + + // Box::pin(async move { + // match fut.await { + // Ok(true) => Ok(()), + // Ok(false) => MountedResult::Err(dioxus_html::MountedError::OperationFailed( + // Box::new(DesktopQueryError::FailedToQuery), + // )), + // Err(err) => { + // MountedResult::Err(dioxus_html::MountedError::OperationFailed(Box::new(err))) + // } + // } + // }) + // } } #[derive(Debug)] From 3b47bf7ae0e2f66d9885304622fc69b2814edfdd Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 22 Aug 2024 01:40:39 -0700 Subject: [PATCH 043/139] re-wire up devsocket --- Cargo.lock | 2 + Cargo.toml | 1 + examples/eval.rs | 3 +- packages/desktop/Cargo.toml | 3 +- packages/desktop/src/app.rs | 10 +-- packages/desktop/src/element.rs | 50 +++++++-------- packages/devtools/Cargo.toml | 3 +- packages/devtools/src/lib.rs | 58 ++++++++++------- packages/devtools/tests/roundtrip.rs | 37 +++++++++++ packages/html/src/events/mounted.rs | 42 ++++++++----- packages/liveview/Cargo.toml | 1 + .../liveview/src/adapters/axum_adapter.rs | 3 +- packages/liveview/src/config.rs | 25 +++----- packages/liveview/src/eval.rs | 63 ++----------------- packages/liveview/src/launch.rs | 1 + packages/liveview/src/pool.rs | 4 +- packages/runtime-config/src/lib.rs | 17 +++++ 17 files changed, 175 insertions(+), 148 deletions(-) create mode 100644 packages/devtools/tests/roundtrip.rs diff --git a/Cargo.lock b/Cargo.lock index 3eba7fa6c5..6629f5f44b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2515,6 +2515,7 @@ dependencies = [ "dioxus-hooks", "dioxus-html", "dioxus-interpreter-js", + "dioxus-runtime-config", "dioxus-signals", "dioxus-ssr", "dunce", @@ -2771,6 +2772,7 @@ dependencies = [ "dioxus-document", "dioxus-html", "dioxus-interpreter-js", + "dioxus-runtime-config", "env_logger", "futures-channel", "futures-util", diff --git a/Cargo.toml b/Cargo.toml index accbbbae36..433d04b34a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -107,6 +107,7 @@ dioxus-static-site-generation = { path = "packages/static-generation", version = dioxus_server_macro = { path = "packages/server-macro", version = "0.6.0-alpha.2", default-features = false } dioxus-isrg = { path = "packages/isrg", version = "0.6.0-alpha.2" } lazy-js-bundle = { path = "packages/lazy-js-bundle", version = "0.6.0-alpha.2" } +dioxus-runtime-config = { path = "packages/runtime-config", version = "0.6.0-alpha.2" } manganis = { path = "packages/manganis", version = "0.6.0-alpha.2" } manganis-macro = { path = "packages/manganis-macro", version = "0.6.0-alpha.2" } diff --git a/examples/eval.rs b/examples/eval.rs index c3f0b7fc1d..5452920771 100644 --- a/examples/eval.rs +++ b/examples/eval.rs @@ -20,8 +20,7 @@ fn app() -> Element { // builtin function. This allows you to create a two-way communication channel between Rust and JS. let mut eval = document::eval( r#" - dioxus.send("Hi from JS!"); - return "hi from JS!"; + return "hi from JS!"; "#, ); diff --git a/packages/desktop/Cargo.toml b/packages/desktop/Cargo.toml index 8877740c43..a30e14d6fa 100644 --- a/packages/desktop/Cargo.toml +++ b/packages/desktop/Cargo.toml @@ -19,8 +19,9 @@ dioxus-html = { workspace = true, features = [ dioxus-document = { workspace = true } dioxus-signals = { workspace = true, optional = true } dioxus-interpreter-js = { workspace = true, features = ["binary-protocol", "serialize"] } -generational-box = { workspace = true } dioxus-devtools = { workspace = true, optional = true } +dioxus-runtime-config = { workspace = true } +generational-box = { workspace = true } thiserror = { workspace = true } tracing = { workspace = true } tao = { workspace = true, features = ["rwh_05"] } diff --git a/packages/desktop/src/app.rs b/packages/desktop/src/app.rs index 7a1f8481d1..021b90a18f 100644 --- a/packages/desktop/src/app.rs +++ b/packages/desktop/src/app.rs @@ -136,10 +136,12 @@ impl App { #[cfg(all(feature = "devtools", debug_assertions,))] pub fn connect_devtools(&self) { - let proxy = self.shared.proxy.clone(); - dioxus_devtools::connect(move |msg| { - _ = proxy.send_event(UserWindowEvent::HotReloadEvent(msg)); - }); + if let Some(addr) = dioxus_runtime_config::devserver_addr() { + let proxy = self.shared.proxy.clone(); + dioxus_devtools::connect(addr, move |msg| { + _ = proxy.send_event(UserWindowEvent::HotReloadEvent(msg)); + }); + } } pub fn handle_new_window(&mut self) { diff --git a/packages/desktop/src/element.rs b/packages/desktop/src/element.rs index b288aa6a5a..4e2f8d89ac 100644 --- a/packages/desktop/src/element.rs +++ b/packages/desktop/src/element.rs @@ -25,35 +25,35 @@ impl RenderedElementBacking for DesktopElement { self } - async fn get_scroll_offset(&self) -> MountedResult { - let id = self.id.0; - let res = self.webview.eval(format!( - "return [window.interpreter.getScrollLeft({id}), window.interpreter.getScrollTop({id})]" - )); - todo!() - // Box::pin(res.recv_as()) - // Box::pin(async { Err(dioxus_html::MountedError::NotSupported) }) - } + // async fn get_scroll_offset(&self) -> MountedResult { + // let id = self.id.0; + // let res = self.webview.eval(format!( + // "return [window.interpreter.getScrollLeft({id}), window.interpreter.getScrollTop({id})]" + // )); + // todo!() + // // Box::pin(res.recv_as()) + // // Box::pin(async { Err(dioxus_html::MountedError::NotSupported) }) + // } - async fn get_scroll_size(&self) -> MountedResult { - todo!() - // Box::pin(async { Err(dioxus_html::MountedError::NotSupported) }) - } + // async fn get_scroll_size(&self) -> MountedResult { + // todo!() + // // Box::pin(async { Err(dioxus_html::MountedError::NotSupported) }) + // } - async fn get_client_rect(&self) -> MountedResult { - todo!() - // Box::pin(async { Err(dioxus_html::MountedError::NotSupported) }) - } + // async fn get_client_rect(&self) -> MountedResult { + // todo!() + // // Box::pin(async { Err(dioxus_html::MountedError::NotSupported) }) + // } - async fn scroll_to(&self, _behavior: ScrollBehavior) -> MountedResult<()> { - todo!() - // Box::pin(async { Err(dioxus_html::MountedError::NotSupported) }) - } + // async fn scroll_to(&self, _behavior: ScrollBehavior) -> MountedResult<()> { + // todo!() + // // Box::pin(async { Err(dioxus_html::MountedError::NotSupported) }) + // } - async fn set_focus(&self, _focus: bool) -> MountedResult<()> { - todo!() - // Box::pin(async { Err(dioxus_html::MountedError::NotSupported) }) - } + // async fn set_focus(&self, _focus: bool) -> MountedResult<()> { + // todo!() + // // Box::pin(async { Err(dioxus_html::MountedError::NotSupported) }) + // } // scripted_getter!( // get_scroll_offset, diff --git a/packages/devtools/Cargo.toml b/packages/devtools/Cargo.toml index a26dafb54a..e42a3a2c52 100644 --- a/packages/devtools/Cargo.toml +++ b/packages/devtools/Cargo.toml @@ -15,7 +15,8 @@ dioxus-html = { workspace = true } dioxus-signals = { workspace = true } dioxus-core = { workspace = true, features = ["serialize"] } dioxus-devtools-types = { workspace = true } -serde = { version = "1", features = ["derive"] } +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true } # hot reloading serve tracing = { workspace = true } diff --git a/packages/devtools/src/lib.rs b/packages/devtools/src/lib.rs index 3915c93896..866743723c 100644 --- a/packages/devtools/src/lib.rs +++ b/packages/devtools/src/lib.rs @@ -1,6 +1,10 @@ -pub use dioxus_devtools_types::*; +use std::{ + io::{self, BufRead}, + net::SocketAddr, +}; use dioxus_core::{ScopeId, VirtualDom}; +pub use dioxus_devtools_types::*; use dioxus_signals::Writable; use warnings::Warning; @@ -25,30 +29,42 @@ pub fn apply_changes(dom: &VirtualDom, msg: &HotReloadMsg) { }); } -/// Connect to the devserver and handle its messages -pub fn connect(mut callback: impl FnMut(DevserverMsg) + Send + 'static) { - // Hi! - // - // yes, we read-raw from a tcp socket - // don't think about it too much :) - // - // we just don't want to bring in tls + tokio for just hotreloading +/// Connect to the devserver and handle its messages with a callback. +/// +/// This doesn't use any form of security or protocol, so it's not safe to expose to the internet. +pub fn connect(addr: SocketAddr, mut callback: impl FnMut(DevserverMsg) + Send + 'static) { std::thread::spawn(move || { - let connect = std::net::TcpStream::connect("127.0.0.1:8080"); + let connect = std::net::TcpStream::connect(addr); let Ok(mut stream) = connect else { return; }; - loop {} - - // let mut buf = [0; 1024]; - // loop { - // let len = stream.read(&mut buf).unwrap(); - // if len == 0 { - // break; - // } - // let msg = String::from_utf8_lossy(&buf[..len]); - // callback(serde_json::from_str(&msg).unwrap()); - // } + // Wrap the stream in a BufReader, so we can use the BufRead methods + let mut reader = io::BufReader::new(&mut stream); + + // Loop and read lines from the stream + loop { + let mut buf = String::new(); + let msg = reader.read_line(&mut buf); + + let Ok(amt) = msg else { + break; + }; + + // eof received - connection closed + if amt == 0 { + break; + } + + reader.consume(amt); + + if let Ok(msg) = serde_json::from_str(&buf) { + callback(msg); + } else { + eprintln!("Failed to parse message from devserver: {:?}", buf); + } + + std::thread::sleep(std::time::Duration::from_millis(100)); + } }); } diff --git a/packages/devtools/tests/roundtrip.rs b/packages/devtools/tests/roundtrip.rs new file mode 100644 index 0000000000..45811a39eb --- /dev/null +++ b/packages/devtools/tests/roundtrip.rs @@ -0,0 +1,37 @@ +use dioxus_devtools::*; +use std::io::{BufWriter, Write}; + +#[test] +fn roundtrip() { + println!("Starting server"); + + let callback = |msg: DevserverMsg| { + println!("msg received! {msg:?}"); + }; + + let bind = std::net::TcpListener::bind("127.0.0.1:8080").expect("Failed to bind port"); + + connect("127.0.0.1:8080".parse().unwrap(), callback); + + let (stream, _addr) = bind.accept().unwrap(); + { + println!("Accepted connection"); + + let mut writer = BufWriter::new(stream); + + for _x in 0..3 { + let line = serde_json::to_string(&DevserverMsg::HotReload(HotReloadMsg { + templates: vec![], + assets: vec!["/asd/bcc".into()], + unknown_files: vec![], + })) + .unwrap(); + writer.write(line.as_bytes()).unwrap(); + writer.write("\n".as_bytes()).unwrap(); + writer.flush().unwrap(); + std::thread::sleep(std::time::Duration::from_millis(100)); + } + } + + std::thread::sleep(std::time::Duration::from_secs(1)); +} diff --git a/packages/html/src/events/mounted.rs b/packages/html/src/events/mounted.rs index 37b583b3a7..23db1b12e6 100644 --- a/packages/html/src/events/mounted.rs +++ b/packages/html/src/events/mounted.rs @@ -5,36 +5,41 @@ use std::fmt::{Display, Formatter}; /// /// Different platforms will have different implementations and different levels of support for this trait. Renderers that do not support specific features will return `None` for those queries. // we can not use async_trait here because it does not create a trait that is object safe -#[async_trait::async_trait(?Send)] +// #[async_trait::async_trait(?Send)] pub trait RenderedElementBacking: std::any::Any { /// return self as Any fn as_any(&self) -> &dyn std::any::Any; /// Get the number of pixels that an element's content is scrolled - async fn get_scroll_offset(&self) -> MountedResult { - Err(MountedError::NotSupported) + fn get_scroll_offset(&self) -> MountedResult { + // Err(MountedError::NotSupported) + todo!() } /// Get the size of an element's content, including content not visible on the screen due to overflow - async fn get_scroll_size(&self) -> MountedResult { - Err(MountedError::NotSupported) + fn get_scroll_size(&self) -> MountedResult { + // Err(MountedError::NotSupported) + todo!() } /// Get the bounding rectangle of the element relative to the viewport (this does not include the scroll position) #[allow(clippy::type_complexity)] - async fn get_client_rect(&self) -> MountedResult { - Err(MountedError::NotSupported) + fn get_client_rect(&self) -> MountedResult { + // Err(MountedError::NotSupported) + todo!() } /// Scroll to make the element visible - async fn scroll_to(&self, _behavior: ScrollBehavior) -> MountedResult<()> { - Err(MountedError::NotSupported) + fn scroll_to(&self, _behavior: ScrollBehavior) -> MountedResult<()> { + // Err(MountedError::NotSupported) + todo!() } /// Set the focus on the element - async fn set_focus(&self, _focus: bool) -> MountedResult<()> { - Err(MountedError::NotSupported) + fn set_focus(&self, _focus: bool) -> MountedResult<()> { + // Err(MountedError::NotSupported) + todo!() } } @@ -81,33 +86,38 @@ impl MountedData { #[doc(alias = "scrollTop")] #[doc(alias = "scrollLeft")] pub async fn get_scroll_offset(&self) -> MountedResult { - self.inner.get_scroll_offset().await + // self.inner.get_scroll_offset().await + todo!() } /// Get the size of an element's content, including content not visible on the screen due to overflow #[doc(alias = "scrollWidth")] #[doc(alias = "scrollHeight")] pub async fn get_scroll_size(&self) -> MountedResult { - self.inner.get_scroll_size().await + // self.inner.get_scroll_size().await + todo!() } /// Get the bounding rectangle of the element relative to the viewport (this does not include the scroll position) #[doc(alias = "getBoundingClientRect")] pub async fn get_client_rect(&self) -> MountedResult { - self.inner.get_client_rect().await + // self.inner.get_client_rect().await + todo!() } /// Scroll to make the element visible #[doc(alias = "scrollIntoView")] pub async fn scroll_to(&self, behavior: ScrollBehavior) -> MountedResult<()> { - self.inner.scroll_to(behavior).await + // self.inner.scroll_to(behavior).await + todo!() } /// Set the focus on the element #[doc(alias = "focus")] #[doc(alias = "blur")] pub async fn set_focus(&self, focus: bool) -> MountedResult<()> { - self.inner.set_focus(focus).await + // self.inner.set_focus(focus).await + todo!() } /// Downcast this event to a concrete event type diff --git a/packages/liveview/Cargo.toml b/packages/liveview/Cargo.toml index 30cd4fc0de..6ed232269c 100644 --- a/packages/liveview/Cargo.toml +++ b/packages/liveview/Cargo.toml @@ -30,6 +30,7 @@ dioxus-core-types = { workspace = true } dioxus-document = { workspace = true } dioxus-devtools = { workspace = true, optional = true } dioxus-interpreter-js = { workspace = true, features = ["binary-protocol"] } +dioxus-runtime-config = { workspace = true } generational-box = { workspace = true } # axum diff --git a/packages/liveview/src/adapters/axum_adapter.rs b/packages/liveview/src/adapters/axum_adapter.rs index 638e6cd9cf..c085dbaf6d 100644 --- a/packages/liveview/src/adapters/axum_adapter.rs +++ b/packages/liveview/src/adapters/axum_adapter.rs @@ -46,14 +46,13 @@ impl LiveviewRouter for Router { let view = crate::LiveViewPool::new(); let ws_path = format!("{}/ws", route.trim_start_matches('/')); - let title = crate::app_title(); let index_page_with_glue = move |glue: &str| { Html(format!( r#" - {title} +
{glue} diff --git a/packages/liveview/src/config.rs b/packages/liveview/src/config.rs index 66e6642002..94f6f119ac 100644 --- a/packages/liveview/src/config.rs +++ b/packages/liveview/src/config.rs @@ -2,14 +2,6 @@ use dioxus_core::VirtualDom; use crate::LiveviewRouter; -pub(crate) fn app_title() -> String { - todo!() - // CURRENT_CONFIG - // .as_ref() - // .map(|c| c.web.app.title.clone()) - // .unwrap_or_else(|_| "Dioxus Liveview App".to_string()) -} - /// A configuration for the LiveView server. pub struct Config { router: R, @@ -19,14 +11,13 @@ pub struct Config { impl Default for Config { fn default() -> Self { - let address = todo!(); - todo!(); - // let address = RuntimeCLIArguments::from_cli() - // .map(|args| args.fullstack_address().address()) - // .unwrap_or(std::net::SocketAddr::V4(std::net::SocketAddrV4::new( - // std::net::Ipv4Addr::new(127, 0, 0, 1), - // 8080, - // ))); + let address = dioxus_runtime_config::fullstack_address().unwrap_or_else(|| { + std::net::SocketAddr::V4(std::net::SocketAddrV4::new( + std::net::Ipv4Addr::new(127, 0, 0, 1), + 8080, + )) + }); + Self { router: R::create_default_liveview_router(), address, @@ -65,7 +56,7 @@ impl Config { /// Launch the LiveView server. pub async fn launch(self) { - println!("{} started on http://{}", app_title(), self.address); + println!("Listening on http://{}", self.address); self.router.start(self.address).await } } diff --git a/packages/liveview/src/eval.rs b/packages/liveview/src/eval.rs index c26621f5b0..4b92e4381f 100644 --- a/packages/liveview/src/eval.rs +++ b/packages/liveview/src/eval.rs @@ -1,6 +1,5 @@ use dioxus_core::ScopeId; use dioxus_document::{Document, EvalError}; -use generational_box::{AnyStorage, GenerationalBox, UnsyncStorage}; use std::rc::Rc; use crate::query::{Query, QueryEngine}; @@ -18,17 +17,18 @@ pub struct LiveviewDocument { } impl Document for LiveviewDocument { - fn eval(&self, js: String) -> dioxus_document::Eval { - todo!() - } fn as_any(&self) -> &dyn std::any::Any { self } - fn set_title(&self, title: String) { + fn eval(&self, js: String) -> dioxus_document::Eval { todo!() } + fn set_title(&self, title: String) { + _ = self.eval(format!("window.document.title = '{}';", title)); + } + fn create_head_element( &self, name: &str, @@ -38,56 +38,3 @@ impl Document for LiveviewDocument { todo!() } } - -/// Represents a liveview-target's JavaScript evaluator. -pub(crate) struct LiveviewEvaluator { - query: Query, -} - -// impl LiveviewEvaluator { -// /// Creates a new evaluator for liveview-based targets. -// pub fn create(query_engine: QueryEngine, js: String) -> GenerationalBox> { -// let query = query_engine.new_query(&js); -// // We create a generational box that is owned by the query slot so that when we drop the query slot, the generational box is also dropped. -// let owner = UnsyncStorage::owner(); -// let query_id = query.id; -// let query = owner.insert(Box::new(LiveviewEvaluator { query }) as Box); -// query_engine.active_requests.slab.borrow_mut()[query_id].owner = Some(owner); - -// query -// } -// } - -// impl Evaluator for LiveviewEvaluator { -// /// # Panics -// /// This will panic if the query is currently being awaited. -// fn poll_join( -// &mut self, -// context: &mut std::task::Context<'_>, -// ) -> std::task::Poll> { -// self.query -// .poll_result(context) -// .map_err(|e| EvalError::Communication(e.to_string())) -// } - -// /// Sends a message to the evaluated JavaScript. -// fn send(&self, data: serde_json::Value) -> Result<(), EvalError> { -// if let Err(e) = self.query.send(data) { -// return Err(EvalError::Communication(e.to_string())); -// } -// Ok(()) -// } - -// /// Gets an UnboundedReceiver to receive messages from the evaluated JavaScript. -// /// -// /// # Panics -// /// This will panic if the query is currently being awaited. -// fn poll_recv( -// &mut self, -// context: &mut std::task::Context<'_>, -// ) -> std::task::Poll> { -// self.query -// .poll_recv(context) -// .map_err(|e| EvalError::Communication(e.to_string())) -// } -// } diff --git a/packages/liveview/src/launch.rs b/packages/liveview/src/launch.rs index 766a7a25fe..eb96e23229 100644 --- a/packages/liveview/src/launch.rs +++ b/packages/liveview/src/launch.rs @@ -11,6 +11,7 @@ pub fn launch( ) -> ! { #[cfg(feature = "multi-thread")] let mut builder = tokio::runtime::Builder::new_multi_thread(); + #[cfg(not(feature = "multi-thread"))] let mut builder = tokio::runtime::Builder::new_current_thread(); diff --git a/packages/liveview/src/pool.rs b/packages/liveview/src/pool.rs index 6e4ba69d26..132547c538 100644 --- a/packages/liveview/src/pool.rs +++ b/packages/liveview/src/pool.rs @@ -120,7 +120,9 @@ pub async fn run(mut vdom: VirtualDom, ws: impl LiveViewSocket) -> Result<(), Li #[cfg(all(feature = "devtools", debug_assertions))] let mut hot_reload_rx = { let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); - dioxus_devtools::connect(move |template| _ = tx.send(template)); + if let Some(addr) = dioxus_runtime_config::devserver_addr() { + dioxus_devtools::connect(addr, move |template| _ = tx.send(template)); + } rx }; diff --git a/packages/runtime-config/src/lib.rs b/packages/runtime-config/src/lib.rs index 8b13789179..3ae2c51811 100644 --- a/packages/runtime-config/src/lib.rs +++ b/packages/runtime-config/src/lib.rs @@ -1 +1,18 @@ +use std::net::SocketAddr; +pub const DEVSERVER_ADDR_ENV: &str = "DIOXUS_DEVSERVER_ADDR"; + +/// Get the address of the devserver +pub fn devserver_addr() -> Option { + std::env::var(DEVSERVER_ADDR_ENV) + .ok() + .and_then(|s| s.parse().ok()) +} + +pub const FULLSTACK_ADDRESS_ENV: &str = "DIOXUS_FULLSTACK_ADDRESS"; + +pub fn fullstack_address() -> Option { + std::env::var(FULLSTACK_ADDRESS_ENV) + .ok() + .and_then(|s| s.parse().ok()) +} From 6f5dcea79cc184bf4b17b57b9dfe2bf1f2eb48fd Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 22 Aug 2024 02:17:21 -0700 Subject: [PATCH 044/139] fix utf css --- packages/desktop/src/desktop_context.rs | 2 +- packages/desktop/src/document.rs | 60 +++++++++-------- packages/desktop/src/file_upload.rs | 6 +- packages/desktop/src/protocol.rs | 87 ++++++++----------------- packages/desktop/src/webview.rs | 9 ++- 5 files changed, 68 insertions(+), 96 deletions(-) diff --git a/packages/desktop/src/desktop_context.rs b/packages/desktop/src/desktop_context.rs index f1fb09be2c..5cc7bd3f87 100644 --- a/packages/desktop/src/desktop_context.rs +++ b/packages/desktop/src/desktop_context.rs @@ -222,7 +222,7 @@ impl DesktopService { /// Eval a javascript string into the current document pub fn eval(&self, js: String) -> Eval { - todo!() + dioxus_document::Document::eval(self, js) } /// Push an objc view to the window diff --git a/packages/desktop/src/document.rs b/packages/desktop/src/document.rs index dd3086b0e8..68f993d174 100644 --- a/packages/desktop/src/document.rs +++ b/packages/desktop/src/document.rs @@ -1,36 +1,34 @@ -use std::sync::{Arc, Mutex}; - -use crate::DesktopContext; +use crate::DesktopService; use dioxus_document::{Document, Eval, EvalError}; +use std::sync::{Arc, Mutex}; -/// Represents the desktop-target's provider of evaluators. -pub struct DesktopDocument { - pub(crate) cx: DesktopContext, -} +impl Document for DesktopService { + fn as_any(&self) -> &dyn std::any::Any { + self + } -impl DesktopDocument { - pub fn new(desktop_ctx: DesktopContext) -> Self { - Self { cx: desktop_ctx } + fn set_title(&self, title: String) { + self.window.set_title(&title); } -} -impl Document for DesktopDocument { fn eval(&self, js: String) -> Eval { let (tx, eval) = Eval::from_parts(); // Dumb wry has a signature of Fn instead of FnOnce, meaning we need to put the callback in a closure // that uses rwlock + option to make sure we don't run the callback twice - let _tx = Arc::new(Mutex::new(Some(tx))); - let tx = _tx.clone(); - let callback = move |res| { - if let Some(tx) = tx.lock().unwrap().take() { - let _ = tx.send(Ok(res)); + let tx = Arc::new(Mutex::new(Some(tx))); + let callback = { + let tx = tx.clone(); + move |res| { + if let Some(tx) = tx.lock().unwrap().take() { + let _ = tx.send(Ok(res)); + } } }; - let res = self.cx.webview.evaluate_script_with_callback(&js, callback); + let res = self.webview.evaluate_script_with_callback(&js, callback); if let Err(err) = res { - _ = _tx + _ = tx .lock() .unwrap() .take() @@ -41,20 +39,26 @@ impl Document for DesktopDocument { eval } - fn set_title(&self, title: String) { - self.cx.window.set_title(&title); - } - - fn as_any(&self) -> &dyn std::any::Any { - self - } - fn create_head_element( &self, name: &str, attributes: Vec<(&str, String)>, contents: Option, ) { - todo!("create the head element stuff") + let contents = contents.unwrap_or_default(); + let attr_iter = attributes + .into_iter() + .map(|(name, value)| format!(r#"element.setAttribute("{name}", "{value}");"#)) + .collect::>() + .join(""); + + self.eval(format!( + r#" + let element = document.createElement("{name}"); + {attr_iter} + element.innerHTML = "{contents}"; + document.head.appendChild(element); + "#, + )); } } diff --git a/packages/desktop/src/file_upload.rs b/packages/desktop/src/file_upload.rs index 1fe42aa892..747ec9d340 100644 --- a/packages/desktop/src/file_upload.rs +++ b/packages/desktop/src/file_upload.rs @@ -99,7 +99,7 @@ impl FileDialogRequest { enum Filters { Extension(String), - Mime(String), + Mime, Audio, Video, Image, @@ -109,7 +109,7 @@ impl Filters { fn as_extensions(&self) -> Vec<&str> { match self { Filters::Extension(extension) => vec![extension.as_str()], - Filters::Mime(_) => vec![], + Filters::Mime => vec![], Filters::Audio => vec!["mp3", "wav", "ogg"], Filters::Video => vec!["mp4", "webm"], Filters::Image => vec!["png", "jpg", "jpeg", "gif", "webp"], @@ -128,7 +128,7 @@ impl FromStr for Filters { "audio/*" => Ok(Filters::Audio), "video/*" => Ok(Filters::Video), "image/*" => Ok(Filters::Image), - _ => Ok(Filters::Mime(s.to_string())), + _ => Ok(Filters::Mime), } } } diff --git a/packages/desktop/src/protocol.rs b/packages/desktop/src/protocol.rs index 98777f90f4..c935854589 100644 --- a/packages/desktop/src/protocol.rs +++ b/packages/desktop/src/protocol.rs @@ -1,13 +1,7 @@ use crate::{assets::*, webview::WebviewEdits}; -// use dioxus_document::NATIVE_EVAL_JS; use dioxus_interpreter_js::unified_bindings::SLEDGEHAMMER_JS; use dioxus_interpreter_js::NATIVE_JS; -use serde::Deserialize; -use std::{ - path::{Component, Path, PathBuf}, - process::Command, - sync::OnceLock, -}; +use std::path::{Path, PathBuf}; use wry::{ http::{status::StatusCode, Request, Response}, RequestAsyncResponder, Result, @@ -60,6 +54,14 @@ pub(super) fn desktop_handler( return edit_state.handle_event(request, responder); } + // todo: we want to move the custom assets onto a different protocol or something + if let Some(name) = request.uri().path().split('/').next() { + if asset_handlers.has_handler(name) { + let _name = name.to_string(); + return asset_handlers.handle_request(&_name, request, responder); + } + } + match serve_asset(request) { Ok(res) => responder.respond(res), Err(_e) => responder.respond( @@ -94,19 +96,14 @@ fn serve_asset(request: Request>) -> Result>> { // If the asset exists, then we can serve it! if asset.exists() { + let mime_type = get_mime_from_path(&asset); + println!("mime type: {:?} for {:?}", mime_type, asset); return Ok(Response::builder() - .header("Content-Type", get_mime_from_path(&asset)?) + .header("Content-Type", mime_type?) .header("Access-Control-Allow-Origin", "*") .body(std::fs::read(asset)?)?); } - // // todo: we want to move the custom assets onto a different protocol or something - // if let Some(name) = asset.iter().next().unwrap().to_str() { - // if asset_handlers.has_handler(name) { - // return asset_handlers.handle_request(name, request, responder); - // } - // } - return Ok(Response::builder() .status(StatusCode::NOT_FOUND) .body(String::from("Not Found").into_bytes())?); @@ -158,41 +155,6 @@ fn index_request( .ok() } -fn resolve_resource(path: &Path) -> PathBuf { - let mut base_path = get_asset_root().unwrap_or_else(|| std::env::current_dir().unwrap()); - - // println!("asset path: {:?}, {path:?}", base_path); - base_path.push(path); - // println!("asset path resolved: {:?}", base_path); - - // // Even in dev mode, mobile needs to resolve the asset path from the bundle - // if running_in_dev_mode() || cfg!(any(target_os = "ios", target_os = "android")) { - // base_path.push(path); - // // Special handler for Manganis filesystem fallback. - // // We need this since Manganis provides assets from workspace root. - // if !base_path.exists() { - // let workspace_root = get_workspace_root_from_cargo(); - // let asset_path = workspace_root.join(path); - // return asset_path; - // } - // } else { - // let mut resource_path = PathBuf::new(); - // for component in path.components() { - // // Tauri-bundle inserts special path segments for abnormal component paths - // match component { - // Component::Prefix(_) => {} - // Component::RootDir => resource_path.push("_root_"), - // Component::CurDir => {} - // Component::ParentDir => resource_path.push("_up_"), - // Component::Normal(p) => resource_path.push(p), - // } - // } - // base_path.push(resource_path); - // } - - base_path -} - /// Construct the inline script that boots up the page and bridges the webview with rust code. /// /// The arguments here: @@ -257,34 +219,37 @@ fn get_asset_root() -> Option { } /// Get the mime type from a path-like string -fn get_mime_from_path(trimmed: &Path) -> Result<&'static str> { - if trimmed.extension().is_some_and(|ext| ext == "svg") { +fn get_mime_from_path(asset: &Path) -> Result<&'static str> { + if asset.extension().is_some_and(|ext| ext == "svg") { return Ok("image/svg+xml"); } - match infer::get_from_path(trimmed)?.map(|f| f.mime_type()) { + match infer::get_from_path(asset)?.map(|f| f.mime_type()) { Some(f) if f != "text/plain" => Ok(f), - _ => Ok(get_mime_by_ext(trimmed)), + _other => Ok(get_mime_by_ext(asset)), } } /// Get the mime type from a URI using its extension fn get_mime_by_ext(trimmed: &Path) -> &'static str { match trimmed.extension().and_then(|e| e.to_str()) { + // The common assets are all utf-8 encoded + Some("js") => "text/javascript; charset=utf-8", + Some("css") => "text/css; charset=utf-8", + Some("json") => "application/json; charset=utf-8", + Some("svg") => "image/svg+xml; charset=utf-8", + Some("html") => "text/html; charset=utf-8", + + // the rest... idk? probably not + Some("mjs") => "text/javascript; charset=utf-8", Some("bin") => "application/octet-stream", - Some("css") => "text/css", Some("csv") => "text/csv", - Some("html") => "text/html", Some("ico") => "image/vnd.microsoft.icon", - Some("js") => "text/javascript", - Some("json") => "application/json", Some("jsonld") => "application/ld+json", - Some("mjs") => "text/javascript", Some("rtf") => "application/rtf", - Some("svg") => "image/svg+xml", Some("mp4") => "video/mp4", // Assume HTML when a TLD is found for eg. `dioxus:://dioxuslabs.app` | `dioxus://hello.com` - Some(_) => "text/html", + Some(_) => "text/html; charset=utf-8", // https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types // using octet stream according to this: None => "application/octet-stream", diff --git a/packages/desktop/src/webview.rs b/packages/desktop/src/webview.rs index e0f6829244..0d6077d1c2 100644 --- a/packages/desktop/src/webview.rs +++ b/packages/desktop/src/webview.rs @@ -3,7 +3,7 @@ use crate::file_upload::DesktopFileDragEvent; use crate::file_upload::NativeFileEngine; use crate::menubar::DioxusMenu; use crate::{ - app::SharedContext, assets::AssetHandlerRegistry, document::DesktopDocument, edits::WryQueue, + app::SharedContext, assets::AssetHandlerRegistry, edits::WryQueue, file_upload::NativeFileHover, ipc::UserWindowEvent, protocol, waker::tao_waker, Config, DesktopContext, DesktopService, }; @@ -328,6 +328,8 @@ impl WebviewInstance { None }; + // The context will function as both the document and the context provider + // But we need to disambiguate the types for rust's TypeId to downcast Rc properly let desktop_context = Rc::from(DesktopService::new( webview, window, @@ -335,13 +337,14 @@ impl WebviewInstance { asset_handlers, file_hover, )); + let as_document: Rc = desktop_context.clone() as Rc; // Provide the desktop context to the virtual dom and edit handler edits.set_desktop_context(desktop_context.clone()); - let provider: Rc = Rc::new(DesktopDocument::new(desktop_context.clone())); + dom.in_runtime(|| { ScopeId::ROOT.provide_context(desktop_context.clone()); - ScopeId::ROOT.provide_context(provider); + ScopeId::ROOT.provide_context(as_document); }); WebviewInstance { From 294de5fbe92bb15cf57fd2c3441dc1e6bc3ffd26 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 22 Aug 2024 02:18:28 -0700 Subject: [PATCH 045/139] yeeeet that js out of here --- packages/html/src/js/eval.js | 1 - packages/html/src/js/hash.txt | 1 - packages/html/src/js/head.js | 1 - packages/html/src/js/native_eval.js | 1 - packages/html/src/ts/.gitignore | 3 - packages/html/src/ts/eval.ts | 108 ---------------------------- packages/html/src/ts/head.ts | 19 ----- packages/html/src/ts/native_eval.ts | 86 ---------------------- 8 files changed, 220 deletions(-) delete mode 100644 packages/html/src/js/eval.js delete mode 100644 packages/html/src/js/hash.txt delete mode 100644 packages/html/src/js/head.js delete mode 100644 packages/html/src/js/native_eval.js delete mode 100644 packages/html/src/ts/.gitignore delete mode 100644 packages/html/src/ts/eval.ts delete mode 100644 packages/html/src/ts/head.ts delete mode 100644 packages/html/src/ts/native_eval.ts diff --git a/packages/html/src/js/eval.js b/packages/html/src/js/eval.js deleted file mode 100644 index 216f11c9d3..0000000000 --- a/packages/html/src/js/eval.js +++ /dev/null @@ -1 +0,0 @@ -class Channel{pending;waiting;constructor(){this.pending=[],this.waiting=[]}send(data){if(this.waiting.length>0){this.waiting.shift()(data);return}this.pending.push(data)}async recv(){return new Promise((resolve,_reject)=>{if(this.pending.length>0){resolve(this.pending.shift());return}this.waiting.push(resolve)})}}class WeakDioxusChannel{inner;constructor(channel){this.inner=new WeakRef(channel)}rustSend(data){let channel=this.inner.deref();if(channel)channel.rustSend(data)}async rustRecv(){let channel=this.inner.deref();if(channel)return await channel.rustRecv()}}class DioxusChannel{weak(){return new WeakDioxusChannel(this)}}class WebDioxusChannel extends DioxusChannel{js_to_rust;rust_to_js;owner;constructor(owner){super();this.owner=owner,this.js_to_rust=new Channel,this.rust_to_js=new Channel}weak(){return new WeakDioxusChannel(this)}async recv(){return await this.rust_to_js.recv()}send(data){this.js_to_rust.send(data)}rustSend(data){this.rust_to_js.send(data)}async rustRecv(){return await this.js_to_rust.recv()}}export{WebDioxusChannel,WeakDioxusChannel,DioxusChannel,Channel}; diff --git a/packages/html/src/js/hash.txt b/packages/html/src/js/hash.txt deleted file mode 100644 index 051bb006a9..0000000000 --- a/packages/html/src/js/hash.txt +++ /dev/null @@ -1 +0,0 @@ -[10372071913661173523, 8375185156499858125, 4813754958077120784] \ No newline at end of file diff --git a/packages/html/src/js/head.js b/packages/html/src/js/head.js deleted file mode 100644 index f4d2c362aa..0000000000 --- a/packages/html/src/js/head.js +++ /dev/null @@ -1 +0,0 @@ -var createElementInHead=function(tag,attributes,children){const element=document.createElement(tag);for(let[key,value]of attributes)element.setAttribute(key,value);if(children)element.appendChild(document.createTextNode(children));document.head.appendChild(element)};window.createElementInHead=createElementInHead; diff --git a/packages/html/src/js/native_eval.js b/packages/html/src/js/native_eval.js deleted file mode 100644 index 50c6026aba..0000000000 --- a/packages/html/src/js/native_eval.js +++ /dev/null @@ -1 +0,0 @@ -class Channel{pending;waiting;constructor(){this.pending=[],this.waiting=[]}send(data){if(this.waiting.length>0){this.waiting.shift()(data);return}this.pending.push(data)}async recv(){return new Promise((resolve,_reject)=>{if(this.pending.length>0){resolve(this.pending.shift());return}this.waiting.push(resolve)})}}class WeakDioxusChannel{inner;constructor(channel){this.inner=new WeakRef(channel)}rustSend(data){let channel=this.inner.deref();if(channel)channel.rustSend(data)}async rustRecv(){let channel=this.inner.deref();if(channel)return await channel.rustRecv()}}class DioxusChannel{weak(){return new WeakDioxusChannel(this)}}class QueryParams{id;data;constructor(id,method,data){this.id=id,this.data={method,data}}}window.__msg_queues=window.__msg_queues||[];window.finalizationRegistry=window.finalizationRegistry||new FinalizationRegistry(({id})=>{window.ipc.postMessage(JSON.stringify({method:"query",params:new QueryParams(id,"drop")}))});window.getQuery=function(request_id){return window.__msg_queues[request_id]};window.createQuery=function(request_id){return new NativeDioxusChannel(request_id)};class NativeDioxusChannel extends DioxusChannel{rust_to_js;request_id;constructor(request_id){super();this.rust_to_js=new Channel,this.request_id=request_id,window.__msg_queues[request_id]=this.weak(),window.finalizationRegistry.register(this,{id:request_id})}async recv(){return await this.rust_to_js.recv()}send(data){window.ipc.postMessage(JSON.stringify({method:"query",params:new QueryParams(this.request_id,"send",data)}))}rustSend(data){this.rust_to_js.send(data)}async rustRecv(){}}export{NativeDioxusChannel}; diff --git a/packages/html/src/ts/.gitignore b/packages/html/src/ts/.gitignore deleted file mode 100644 index 68b79b0d33..0000000000 --- a/packages/html/src/ts/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# please dont accidentally run tsc and commit your js in this dir. -*.js - diff --git a/packages/html/src/ts/eval.ts b/packages/html/src/ts/eval.ts deleted file mode 100644 index 44ce99d674..0000000000 --- a/packages/html/src/ts/eval.ts +++ /dev/null @@ -1,108 +0,0 @@ -// Handle communication between rust and evaluating javascript - -export class Channel { - pending: any[]; - waiting: ((data: any) => void)[]; - - constructor() { - this.pending = []; - this.waiting = []; - } - - send(data: any) { - // If there's a waiting callback, call it - if (this.waiting.length > 0) { - this.waiting.shift()(data); - return; - } - // Otherwise queue the data - this.pending.push(data); - } - - async recv(): Promise { - return new Promise((resolve, _reject) => { - // If data already exists, resolve immediately - if (this.pending.length > 0) { - resolve(this.pending.shift()); - return; - } - // Otherwise queue the resolve callback - this.waiting.push(resolve); - }); - } -} - -export class WeakDioxusChannel { - inner: WeakRef; - - constructor(channel: DioxusChannel) { - this.inner = new WeakRef(channel); - } - - // Send data from rust to javascript - rustSend(data: any) { - let channel = this.inner.deref(); - if (channel) { - channel.rustSend(data); - } - } - - // Receive data sent from javascript in rust - async rustRecv(): Promise { - let channel = this.inner.deref(); - if (channel) { - return await channel.rustRecv(); - } - } -} - -export abstract class DioxusChannel { - // Return a weak reference to this channel - weak(): WeakDioxusChannel { - return new WeakDioxusChannel(this); - } - - // Send data from rust to javascript - abstract rustSend(data: any): void; - - // Receive data sent from javascript in rust - abstract rustRecv(): Promise; -} - -export class WebDioxusChannel extends DioxusChannel { - js_to_rust: Channel; - rust_to_js: Channel; - owner: any; - - constructor(owner: any) { - super(); - this.owner = owner; - this.js_to_rust = new Channel(); - this.rust_to_js = new Channel(); - } - - // Return a weak reference to this channel - weak(): WeakDioxusChannel { - return new WeakDioxusChannel(this); - } - - // Receive message from Rust - async recv() { - return await this.rust_to_js.recv(); - } - - // Send message to rust. - send(data: any) { - this.js_to_rust.send(data); - } - - // Send data from rust to javascript - rustSend(data: any) { - this.rust_to_js.send(data); - } - - // Receive data sent from javascript in rust - async rustRecv(): Promise { - return await this.js_to_rust.recv(); - } -} diff --git a/packages/html/src/ts/head.ts b/packages/html/src/ts/head.ts deleted file mode 100644 index d01c0aaffb..0000000000 --- a/packages/html/src/ts/head.ts +++ /dev/null @@ -1,19 +0,0 @@ -// Helper functions for working with the document head - -function createElementInHead( - tag: string, - attributes: [string, string][], - children: string | null -): void { - const element = document.createElement(tag); - for (const [key, value] of attributes) { - element.setAttribute(key, value); - } - if (children) { - element.appendChild(document.createTextNode(children)); - } - document.head.appendChild(element); -} - -// @ts-ignore -window.createElementInHead = createElementInHead; diff --git a/packages/html/src/ts/native_eval.ts b/packages/html/src/ts/native_eval.ts deleted file mode 100644 index b4152074b8..0000000000 --- a/packages/html/src/ts/native_eval.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { Channel, DioxusChannel, WeakDioxusChannel } from "./eval"; - -// In dioxus desktop, eval needs to use the window object to store global state because we evaluate separate snippets of javascript in the browser -declare global { - interface Window { - __msg_queues: WeakDioxusChannel[]; - finalizationRegistry: FinalizationRegistry<{ id: number }>; - - getQuery(request_id: number): WeakDioxusChannel; - - createQuery(request_id: number): NativeDioxusChannel; - } -} - -// A message that can be sent to the desktop renderer about a query -class QueryParams { - id: number; - data: { method: "drop" | "send"; data?: any }; - - constructor(id: number, method: "drop" | "send", data?: any) { - this.id = id; - this.data = { method, data }; - } -} - -window.__msg_queues = window.__msg_queues || []; -// In dioxus desktop, eval is copy so we cannot run a drop handler. Instead, the drop handler is run after the channel is garbage collected in the javascript side -window.finalizationRegistry = - window.finalizationRegistry || - new FinalizationRegistry(({ id }) => { - // @ts-ignore - wry gives us this - window.ipc.postMessage( - JSON.stringify({ - method: "query", - params: new QueryParams(id, "drop"), - }) - ); - }); - -// Get a query from the global state -window.getQuery = function (request_id: number): WeakDioxusChannel { - return window.__msg_queues[request_id]; -}; - -// Create a new query (and insert it into the global state) -window.createQuery = function (request_id: number): NativeDioxusChannel { - return new NativeDioxusChannel(request_id); -}; - -export class NativeDioxusChannel extends DioxusChannel { - rust_to_js: Channel; - request_id: number; - - constructor(request_id: number) { - super(); - this.rust_to_js = new Channel(); - this.request_id = request_id; - - window.__msg_queues[request_id] = this.weak(); - window.finalizationRegistry.register(this, { id: request_id }); - } - - // Receive message from Rust - async recv() { - return await this.rust_to_js.recv(); - } - - // Send message to rust. - send(data: any) { - // @ts-ignore - wry gives us this - window.ipc.postMessage( - JSON.stringify({ - method: "query", - params: new QueryParams(this.request_id, "send", data), - }) - ); - } - - // Send data from rust to javascript - rustSend(data: any) { - this.rust_to_js.send(data); - } - - // Receive data sent from javascript in rust. This is a no-op in the native interpreter because the rust code runs remotely - async rustRecv(): Promise {} -} From d85d3fa34aaf8c777839418bfde905630f5f7547 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 22 Aug 2024 04:58:00 -0700 Subject: [PATCH 046/139] synthetic web system works --- packages/fullstack/Cargo.toml | 1 - packages/web/Cargo.toml | 1 - packages/web/src/bindings.rs | 2 +- packages/web/src/event.rs | 586 ++++--------------------- packages/web/src/event/drag.rs | 106 +++++ packages/web/src/event/ext.rs | 155 +++++++ packages/web/src/event/form.rs | 157 +++++++ packages/web/src/event/image.rs | 35 ++ packages/web/src/event/resize.rs | 0 packages/web/src/event/synthetic.rs | 647 ++++++++++++++++++++++++++++ 10 files changed, 1183 insertions(+), 507 deletions(-) create mode 100644 packages/web/src/event/drag.rs create mode 100644 packages/web/src/event/ext.rs create mode 100644 packages/web/src/event/form.rs create mode 100644 packages/web/src/event/image.rs create mode 100644 packages/web/src/event/resize.rs create mode 100644 packages/web/src/event/synthetic.rs diff --git a/packages/fullstack/Cargo.toml b/packages/fullstack/Cargo.toml index 7bde28a05d..44d663a923 100644 --- a/packages/fullstack/Cargo.toml +++ b/packages/fullstack/Cargo.toml @@ -80,7 +80,6 @@ default = ["panic_hook", "document", "mounted", "devtools"] panic_hook = ["dioxus-web?/panic_hook"] mounted = ["dioxus-web?/mounted"] devtools = ["dep:dioxus-devtools", "dioxus-web?/devtools"] -# file_engine = ["dioxus-web?/file_engine"] document = ["dioxus-web?/document"] web = ["dep:dioxus-web", "dep:web-sys"] desktop = ["dep:dioxus-desktop", "server_fn/reqwest", "dioxus_server_macro/reqwest"] diff --git a/packages/web/Cargo.toml b/packages/web/Cargo.toml index 9134751cc0..0e075a254a 100644 --- a/packages/web/Cargo.toml +++ b/packages/web/Cargo.toml @@ -85,7 +85,6 @@ full = [ "file-engine", "document" ] -# default = ["panic_hook", "mounted", "file_engine", "hot_reload", "document"] panic_hook = ["dep:console_error_panic_hook"] hydrate = ["web-sys/Comment", "ciborium", "dep:serde"] diff --git a/packages/web/src/bindings.rs b/packages/web/src/bindings.rs index efb5f062d3..d9613c70d8 100644 --- a/packages/web/src/bindings.rs +++ b/packages/web/src/bindings.rs @@ -9,8 +9,8 @@ use wasm_bindgen::{prelude::Closure, JsCast}; #[cfg(feature = "file-engine")] use web_sys::{File, FileList, FileReader}; -#[cfg(feature = "file-engine")] /// A file engine for the web platform +#[cfg(feature = "file-engine")] pub struct WebFileEngine { file_reader: FileReader, file_list: FileList, diff --git a/packages/web/src/event.rs b/packages/web/src/event.rs index eec63078b4..305b1a2a7b 100644 --- a/packages/web/src/event.rs +++ b/packages/web/src/event.rs @@ -4,101 +4,88 @@ use dioxus_html::{ point_interaction::{ InteractionElementOffset, InteractionLocation, ModifiersInteraction, PointerInteraction, }, - DragData, FormData, FormValue, HasDragData, HasFileData, HasFormData, HasImageData, - HasMouseData, HtmlEventConverter, ImageData, MountedData, PlatformEventData, ScrollData, + AnimationData, DragData, FormData, FormValue, HasDragData, HasFileData, HasFormData, + HasImageData, HasMouseData, HtmlEventConverter, ImageData, MountedData, PlatformEventData, + ScrollData, WheelEvent, }; +use drag::WebDragData; +use form::WebFormData; +use image::WebImageEvent; use js_sys::Array; +use synthetic::Synthetic; use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue}; use web_sys::{Document, Element, Event, MouseEvent}; -pub(crate) struct WebEventConverter; +mod drag; +mod ext; +mod form; +mod image; +mod resize; +mod synthetic; -#[inline(always)] -fn downcast_event(event: &dioxus_html::PlatformEventData) -> &GenericWebSysEvent { - event - .downcast::() - .expect("event should be a GenericWebSysEvent") -} +pub(crate) struct WebEventConverter; impl HtmlEventConverter for WebEventConverter { #[inline(always)] - fn convert_animation_data( - &self, - event: &dioxus_html::PlatformEventData, - ) -> dioxus_html::AnimationData { - // downcast_event(event).raw.clone().into() - todo!() + fn convert_animation_data(&self, event: &PlatformEventData) -> dioxus_html::AnimationData { + event.synthesize() } #[inline(always)] - fn convert_clipboard_data( - &self, - event: &dioxus_html::PlatformEventData, - ) -> dioxus_html::ClipboardData { - // downcast_event(event).raw.clone().into() - todo!() + fn convert_clipboard_data(&self, event: &PlatformEventData) -> dioxus_html::ClipboardData { + event.synthesize() } #[inline(always)] - fn convert_composition_data( - &self, - event: &dioxus_html::PlatformEventData, - ) -> dioxus_html::CompositionData { - // downcast_event(event).raw.clone().into() - todo!() + fn convert_composition_data(&self, event: &PlatformEventData) -> dioxus_html::CompositionData { + event.synthesize() } #[inline(always)] - fn convert_drag_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::DragData { + fn convert_drag_data(&self, event: &PlatformEventData) -> dioxus_html::DragData { let event = downcast_event(event); DragData::new(WebDragData::new(event.raw.clone().unchecked_into())) } #[inline(always)] - fn convert_focus_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::FocusData { - // downcast_event(event).raw.clone().into() - todo!() + fn convert_focus_data(&self, event: &PlatformEventData) -> dioxus_html::FocusData { + event.synthesize() } #[inline(always)] - fn convert_form_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::FormData { + fn convert_form_data(&self, event: &PlatformEventData) -> dioxus_html::FormData { let event = downcast_event(event); FormData::new(WebFormData::new(event.element.clone(), event.raw.clone())) } #[inline(always)] - fn convert_image_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::ImageData { + fn convert_image_data(&self, event: &PlatformEventData) -> dioxus_html::ImageData { let event = downcast_event(event); let error = event.raw.type_() == "error"; ImageData::new(WebImageEvent::new(event.raw.clone(), error)) } #[inline(always)] - fn convert_keyboard_data( - &self, - event: &dioxus_html::PlatformEventData, - ) -> dioxus_html::KeyboardData { - // downcast_event(event).raw.clone().into() - todo!() + fn convert_keyboard_data(&self, event: &PlatformEventData) -> dioxus_html::KeyboardData { + event.synthesize() } #[inline(always)] - fn convert_media_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::MediaData { - // downcast_event(event).raw.clone().into() - todo!() + fn convert_media_data(&self, event: &PlatformEventData) -> dioxus_html::MediaData { + event.synthesize() } #[allow(unused_variables)] #[inline(always)] - fn convert_mounted_data(&self, event: &dioxus_html::PlatformEventData) -> MountedData { + fn convert_mounted_data(&self, event: &PlatformEventData) -> MountedData { #[cfg(feature = "mounted")] { - // MountedData::from( - // event - // .downcast::() - // .expect("event should be a web_sys::Element"), - // ) - todo!() + MountedData::new(Synthetic::new( + event + .downcast::() + .expect("event should be a web_sys::Element") + .clone(), + )) } #[cfg(not(feature = "mounted"))] { @@ -107,237 +94,87 @@ impl HtmlEventConverter for WebEventConverter { } #[inline(always)] - fn convert_mouse_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::MouseData { - // downcast_event(event).raw.clone().into() - todo!() - } - - #[inline(always)] - fn convert_pointer_data( - &self, - event: &dioxus_html::PlatformEventData, - ) -> dioxus_html::PointerData { - // downcast_event(event).raw.clone().into() - todo!() - } - - #[inline(always)] - fn convert_resize_data( - &self, - event: &dioxus_html::PlatformEventData, - ) -> dioxus_html::ResizeData { - // downcast_event(event).raw.clone().into() - todo!() - } - - #[inline(always)] - fn convert_scroll_data( - &self, - event: &dioxus_html::PlatformEventData, - ) -> dioxus_html::ScrollData { - // ScrollData::from(downcast_event(event).raw.clone()) - todo!() - } - - #[inline(always)] - fn convert_selection_data( - &self, - event: &dioxus_html::PlatformEventData, - ) -> dioxus_html::SelectionData { - // downcast_event(event).raw.clone().into() - todo!() - } - - #[inline(always)] - fn convert_toggle_data( - &self, - event: &dioxus_html::PlatformEventData, - ) -> dioxus_html::ToggleData { - // downcast_event(event).raw.clone().into() - todo!() - } - - #[inline(always)] - fn convert_touch_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::TouchData { - // downcast_event(event).raw.clone().into() - todo!() - } - - #[inline(always)] - fn convert_transition_data( - &self, - event: &dioxus_html::PlatformEventData, - ) -> dioxus_html::TransitionData { - // downcast_event(event).raw.clone().into() - todo!() - } - - #[inline(always)] - fn convert_wheel_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::WheelData { - // downcast_event(event).raw.clone().into() - todo!() - } -} - -/// A extension trait for web-sys events that provides a way to get the event as a web-sys event. -pub trait WebEventExt { - /// Try to downcast this event as a `web-sys` event. - fn try_as_web_event(&self) -> Option; - - /// Downcast this event as a `web-sys` event. - #[inline(always)] - fn as_web_event(&self) -> E - where - E: 'static, - { - self.try_as_web_event().unwrap_or_else(|| { - panic!( - "Error downcasting to `web-sys`, event should be a {}.", - std::any::type_name::() - ) - }) - } -} - -impl WebEventExt for dioxus_html::AnimationData { - #[inline(always)] - fn try_as_web_event(&self) -> Option { - self.downcast::().cloned() - } -} - -impl WebEventExt for dioxus_html::ClipboardData { - #[inline(always)] - fn try_as_web_event(&self) -> Option { - self.downcast::().cloned() - } -} - -impl WebEventExt for dioxus_html::CompositionData { - #[inline(always)] - fn try_as_web_event(&self) -> Option { - self.downcast::().cloned() - } -} - -impl WebEventExt for dioxus_html::DragData { - #[inline(always)] - fn try_as_web_event(&self) -> Option { - self.downcast::() - .map(|data| &data.raw) - .cloned() - } -} - -impl WebEventExt for dioxus_html::FocusData { - #[inline(always)] - fn try_as_web_event(&self) -> Option { - self.downcast::().cloned() - } -} - -impl WebEventExt for dioxus_html::FormData { - #[inline(always)] - fn try_as_web_event(&self) -> Option { - self.downcast::().cloned() - } -} - -impl WebEventExt for dioxus_html::ImageData { - #[inline(always)] - fn try_as_web_event(&self) -> Option { - self.downcast::().cloned() + fn convert_mouse_data(&self, event: &PlatformEventData) -> dioxus_html::MouseData { + let event = event + .downcast::() + .unwrap() + .raw + .unchecked_ref::() + .clone(); + Synthetic::new(event).into() } -} -impl WebEventExt for dioxus_html::KeyboardData { #[inline(always)] - fn try_as_web_event(&self) -> Option { - self.downcast::().cloned() + fn convert_pointer_data(&self, event: &PlatformEventData) -> dioxus_html::PointerData { + event.synthesize() } -} -impl WebEventExt for dioxus_html::MediaData { #[inline(always)] - fn try_as_web_event(&self) -> Option { - self.downcast::().cloned() + fn convert_resize_data(&self, event: &PlatformEventData) -> dioxus_html::ResizeData { + event.synthesize() } -} -impl WebEventExt for MountedData { #[inline(always)] - fn try_as_web_event(&self) -> Option { - self.downcast::().cloned() + fn convert_scroll_data(&self, event: &PlatformEventData) -> dioxus_html::ScrollData { + event.synthesize() } -} -impl WebEventExt for dioxus_html::MouseData { #[inline(always)] - fn try_as_web_event(&self) -> Option { - self.downcast::().cloned() + fn convert_selection_data(&self, event: &PlatformEventData) -> dioxus_html::SelectionData { + event.synthesize() } -} -impl WebEventExt for dioxus_html::PointerData { #[inline(always)] - fn try_as_web_event(&self) -> Option { - self.downcast::().cloned() + fn convert_toggle_data(&self, event: &PlatformEventData) -> dioxus_html::ToggleData { + event.synthesize() } -} -impl WebEventExt for ScrollData { #[inline(always)] - fn try_as_web_event(&self) -> Option { - self.downcast::().cloned() + fn convert_touch_data(&self, event: &PlatformEventData) -> dioxus_html::TouchData { + event.synthesize() } -} -impl WebEventExt for dioxus_html::SelectionData { #[inline(always)] - fn try_as_web_event(&self) -> Option { - self.downcast::().cloned() + fn convert_transition_data(&self, event: &PlatformEventData) -> dioxus_html::TransitionData { + event.synthesize() } -} -impl WebEventExt for dioxus_html::ToggleData { #[inline(always)] - fn try_as_web_event(&self) -> Option { - self.downcast::().cloned() + fn convert_wheel_data(&self, event: &PlatformEventData) -> dioxus_html::WheelData { + let event = event + .downcast::() + .unwrap() + .raw + .unchecked_ref::() + .clone(); + Synthetic::new(event).into() } } -impl WebEventExt for dioxus_html::TouchData { - #[inline(always)] - fn try_as_web_event(&self) -> Option { - self.downcast::().cloned() - } +struct GenericWebSysEvent { + raw: Event, + element: Element, } -impl WebEventExt for dioxus_html::TransitionData { - #[inline(always)] - fn try_as_web_event(&self) -> Option { - self.downcast::().cloned() - } +trait Synthesize { + fn synthesize>>(&self) -> F; } -impl WebEventExt for dioxus_html::WheelData { - #[inline(always)] - fn try_as_web_event(&self) -> Option { - self.downcast::().cloned() - } -} +impl Synthesize for PlatformEventData { + fn synthesize>>(&self) -> F { + let generic = self + .downcast::() + .expect("event should be a GenericWebSysEvent"); -impl WebEventExt for dioxus_html::ResizeData { - #[inline(always)] - fn try_as_web_event(&self) -> Option { - self.downcast::() - .and_then(|e| e.detail().dyn_into::().ok()) + Synthetic::new(generic.raw.clone().unchecked_into()).into() } } -struct GenericWebSysEvent { - raw: Event, - element: Element, +#[inline(always)] +fn downcast_event(event: &PlatformEventData) -> &GenericWebSysEvent { + event + .downcast::() + .expect("event should be a GenericWebSysEvent") } // todo: some of these events are being casted to the wrong event type. @@ -358,262 +195,3 @@ pub(crate) fn load_document() -> Document { .document() .expect("should have access to the Document") } - -#[derive(Clone)] -struct WebImageEvent { - raw: Event, - error: bool, -} - -impl WebImageEvent { - fn new(raw: Event, error: bool) -> Self { - Self { raw, error } - } -} - -impl HasImageData for WebImageEvent { - fn load_error(&self) -> bool { - self.error - } - - fn as_any(&self) -> &dyn Any { - &self.raw as &dyn Any - } -} - -struct WebFormData { - element: Element, - raw: Event, -} - -impl WebFormData { - fn new(element: Element, raw: Event) -> Self { - Self { element, raw } - } -} - -impl HasFormData for WebFormData { - fn value(&self) -> String { - let target = &self.element; - target - .dyn_ref() - .map(|input: &web_sys::HtmlInputElement| { - // todo: special case more input types - match input.type_().as_str() { - "checkbox" => { - match input.checked() { - true => "true".to_string(), - false => "false".to_string(), - } - }, - _ => { - input.value() - } - } - }) - .or_else(|| { - target - .dyn_ref() - .map(|input: &web_sys::HtmlTextAreaElement| input.value()) - }) - // select elements are NOT input events - because - why woudn't they be?? - .or_else(|| { - target - .dyn_ref() - .map(|input: &web_sys::HtmlSelectElement| input.value()) - }) - .or_else(|| { - target - .dyn_ref::() - .unwrap() - .text_content() - }) - .expect("only an InputElement or TextAreaElement or an element with contenteditable=true can have an oninput event listener") - } - - fn values(&self) -> HashMap { - let mut values = HashMap::new(); - - fn insert_value(map: &mut HashMap, key: String, new_value: String) { - map.entry(key.clone()).or_default().0.push(new_value); - } - - // try to fill in form values - if let Some(form) = self.element.dyn_ref::() { - let form_data = get_form_data(form); - for value in form_data.entries().into_iter().flatten() { - if let Ok(array) = value.dyn_into::() { - if let Some(name) = array.get(0).as_string() { - if let Ok(item_values) = array.get(1).dyn_into::() { - item_values - .iter() - .filter_map(|v| v.as_string()) - .for_each(|v| insert_value(&mut values, name.clone(), v)); - } else if let Ok(item_value) = array.get(1).dyn_into::() { - insert_value(&mut values, name, item_value.as_string().unwrap()); - } - } - } - } - } else if let Some(select) = self.element.dyn_ref::() { - // try to fill in select element values - let options = get_select_data(select); - values.insert("options".to_string(), FormValue(options)); - } - - values - } - - fn as_any(&self) -> &dyn Any { - &self.raw as &dyn Any - } -} - -impl HasFileData for WebFormData { - #[cfg(feature = "file-engine")] - fn files(&self) -> Option> { - use crate::bindings::WebFileEngine; - - let files = self - .element - .dyn_ref() - .and_then(|input: &web_sys::HtmlInputElement| { - input.files().and_then(|files| { - #[allow(clippy::arc_with_non_send_sync)] - WebFileEngine::new(files).map(|f| { - std::sync::Arc::new(f) as std::sync::Arc - }) - }) - }); - - files - } -} - -struct WebDragData { - raw: MouseEvent, -} - -impl WebDragData { - fn new(raw: MouseEvent) -> Self { - Self { raw } - } -} - -impl HasDragData for WebDragData { - fn as_any(&self) -> &dyn std::any::Any { - &self.raw as &dyn std::any::Any - } -} - -impl HasMouseData for WebDragData { - fn as_any(&self) -> &dyn std::any::Any { - &self.raw as &dyn std::any::Any - } -} - -impl PointerInteraction for WebDragData { - fn trigger_button(&self) -> Option { - // self.raw.trigger_button() - todo!() - } - - fn held_buttons(&self) -> dioxus_html::input_data::MouseButtonSet { - // self.raw.held_buttons() - todo!() - } -} - -impl ModifiersInteraction for WebDragData { - fn modifiers(&self) -> dioxus_html::prelude::Modifiers { - // self.raw.modifiers() - todo!() - } -} - -impl InteractionElementOffset for WebDragData { - fn coordinates(&self) -> dioxus_html::geometry::Coordinates { - // self.raw.coordinates() - todo!() - } - - fn element_coordinates(&self) -> dioxus_html::geometry::ElementPoint { - // self.raw.element_coordinates() - todo!() - } -} - -impl InteractionLocation for WebDragData { - fn client_coordinates(&self) -> dioxus_html::geometry::ClientPoint { - // self.raw.client_coordinates() - todo!() - } - - fn screen_coordinates(&self) -> dioxus_html::geometry::ScreenPoint { - // self.raw.screen_coordinates() - todo!() - } - - fn page_coordinates(&self) -> dioxus_html::geometry::PagePoint { - // self.raw.page_coordinates() - todo!() - } -} - -impl HasFileData for WebDragData { - #[cfg(feature = "file-engine")] - fn files(&self) -> Option> { - use crate::bindings::WebFileEngine; - - let files = self - .raw - .dyn_ref::() - .and_then(|drag_event| { - drag_event.data_transfer().and_then(|dt| { - dt.files().and_then(|files| { - #[allow(clippy::arc_with_non_send_sync)] - WebFileEngine::new(files).map(|f| { - std::sync::Arc::new(f) as std::sync::Arc - }) - }) - }) - }); - - files - } -} - -// web-sys does not expose the keys api for form data, so we need to manually bind to it -#[wasm_bindgen(inline_js = r#" -export function get_form_data(form) { - let values = new Map(); - const formData = new FormData(form); - - for (let name of formData.keys()) { - values.set(name, formData.getAll(name)); - } - - return values; -} -"#)] -extern "C" { - fn get_form_data(form: &web_sys::HtmlFormElement) -> js_sys::Map; -} - -// web-sys does not expose the keys api for select data, so we need to manually bind to it -#[wasm_bindgen(inline_js = r#" -export function get_select_data(select) { - let values = []; - for (let i = 0; i < select.options.length; i++) { - let option = select.options[i]; - if (option.selected) { - values.push(option.value.toString()); - } - } - - return values; -} -"#)] -extern "C" { - fn get_select_data(select: &web_sys::HtmlSelectElement) -> Vec; -} diff --git a/packages/web/src/event/drag.rs b/packages/web/src/event/drag.rs new file mode 100644 index 0000000000..ff6225924f --- /dev/null +++ b/packages/web/src/event/drag.rs @@ -0,0 +1,106 @@ +use std::{any::Any, collections::HashMap}; + +use dioxus_html::{ + point_interaction::{ + InteractionElementOffset, InteractionLocation, ModifiersInteraction, PointerInteraction, + }, + AnimationData, DragData, FormData, FormValue, HasDragData, HasFileData, HasFormData, + HasImageData, HasMouseData, HtmlEventConverter, ImageData, MountedData, PlatformEventData, + ScrollData, +}; +use js_sys::Array; +use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue}; +use web_sys::{Document, Element, Event, MouseEvent}; + +pub struct WebDragData { + raw: MouseEvent, +} + +impl WebDragData { + pub fn new(raw: MouseEvent) -> Self { + Self { raw } + } +} + +impl HasDragData for WebDragData { + fn as_any(&self) -> &dyn std::any::Any { + &self.raw as &dyn std::any::Any + } +} + +impl HasMouseData for WebDragData { + fn as_any(&self) -> &dyn std::any::Any { + &self.raw as &dyn std::any::Any + } +} + +impl PointerInteraction for WebDragData { + fn trigger_button(&self) -> Option { + // self.raw.trigger_button() + todo!() + } + + fn held_buttons(&self) -> dioxus_html::input_data::MouseButtonSet { + // self.raw.held_buttons() + todo!() + } +} + +impl ModifiersInteraction for WebDragData { + fn modifiers(&self) -> dioxus_html::prelude::Modifiers { + // self.raw.modifiers() + todo!() + } +} + +impl InteractionElementOffset for WebDragData { + fn coordinates(&self) -> dioxus_html::geometry::Coordinates { + // self.raw.coordinates() + todo!() + } + + fn element_coordinates(&self) -> dioxus_html::geometry::ElementPoint { + // self.raw.element_coordinates() + todo!() + } +} + +impl InteractionLocation for WebDragData { + fn client_coordinates(&self) -> dioxus_html::geometry::ClientPoint { + // self.raw.client_coordinates() + todo!() + } + + fn screen_coordinates(&self) -> dioxus_html::geometry::ScreenPoint { + // self.raw.screen_coordinates() + todo!() + } + + fn page_coordinates(&self) -> dioxus_html::geometry::PagePoint { + // self.raw.page_coordinates() + todo!() + } +} + +impl HasFileData for WebDragData { + #[cfg(feature = "file-engine")] + fn files(&self) -> Option> { + use crate::bindings::WebFileEngine; + + let files = self + .raw + .dyn_ref::() + .and_then(|drag_event| { + drag_event.data_transfer().and_then(|dt| { + dt.files().and_then(|files| { + #[allow(clippy::arc_with_non_send_sync)] + WebFileEngine::new(files).map(|f| { + std::sync::Arc::new(f) as std::sync::Arc + }) + }) + }) + }); + + files + } +} diff --git a/packages/web/src/event/ext.rs b/packages/web/src/event/ext.rs new file mode 100644 index 0000000000..d6bce1134b --- /dev/null +++ b/packages/web/src/event/ext.rs @@ -0,0 +1,155 @@ +/// A extension trait for web-sys events that provides a way to get the event as a web-sys event. +pub trait WebEventExt { + /// Try to downcast this event as a `web-sys` event. + fn try_as_web_event(&self) -> Option; + + /// Downcast this event as a `web-sys` event. + #[inline(always)] + fn as_web_event(&self) -> E + where + E: 'static, + { + self.try_as_web_event().unwrap_or_else(|| { + panic!( + "Error downcasting to `web-sys`, event should be a {}.", + std::any::type_name::() + ) + }) + } +} + +// impl WebEventExt for dioxus_html::AnimationData { +// #[inline(always)] +// fn try_as_web_event(&self) -> Option { +// self.downcast::().cloned() +// } +// } + +// impl WebEventExt for dioxus_html::ClipboardData { +// #[inline(always)] +// fn try_as_web_event(&self) -> Option { +// self.downcast::().cloned() +// } +// } + +// impl WebEventExt for dioxus_html::CompositionData { +// #[inline(always)] +// fn try_as_web_event(&self) -> Option { +// self.downcast::().cloned() +// } +// } + +// impl WebEventExt for dioxus_html::DragData { +// #[inline(always)] +// fn try_as_web_event(&self) -> Option { +// self.downcast::() +// .map(|data| &data.raw) +// .cloned() +// } +// } + +// impl WebEventExt for dioxus_html::FocusData { +// #[inline(always)] +// fn try_as_web_event(&self) -> Option { +// self.downcast::().cloned() +// } +// } + +// impl WebEventExt for dioxus_html::FormData { +// #[inline(always)] +// fn try_as_web_event(&self) -> Option { +// self.downcast::().cloned() +// } +// } + +// impl WebEventExt for dioxus_html::ImageData { +// #[inline(always)] +// fn try_as_web_event(&self) -> Option { +// self.downcast::().cloned() +// } +// } + +// impl WebEventExt for dioxus_html::KeyboardData { +// #[inline(always)] +// fn try_as_web_event(&self) -> Option { +// self.downcast::().cloned() +// } +// } + +// impl WebEventExt for dioxus_html::MediaData { +// #[inline(always)] +// fn try_as_web_event(&self) -> Option { +// self.downcast::().cloned() +// } +// } + +// impl WebEventExt for MountedData { +// #[inline(always)] +// fn try_as_web_event(&self) -> Option { +// self.downcast::().cloned() +// } +// } + +// impl WebEventExt for dioxus_html::MouseData { +// #[inline(always)] +// fn try_as_web_event(&self) -> Option { +// self.downcast::().cloned() +// } +// } + +// impl WebEventExt for dioxus_html::PointerData { +// #[inline(always)] +// fn try_as_web_event(&self) -> Option { +// self.downcast::().cloned() +// } +// } + +// impl WebEventExt for ScrollData { +// #[inline(always)] +// fn try_as_web_event(&self) -> Option { +// self.downcast::().cloned() +// } +// } + +// impl WebEventExt for dioxus_html::SelectionData { +// #[inline(always)] +// fn try_as_web_event(&self) -> Option { +// self.downcast::().cloned() +// } +// } + +// impl WebEventExt for dioxus_html::ToggleData { +// #[inline(always)] +// fn try_as_web_event(&self) -> Option { +// self.downcast::().cloned() +// } +// } + +// impl WebEventExt for dioxus_html::TouchData { +// #[inline(always)] +// fn try_as_web_event(&self) -> Option { +// self.downcast::().cloned() +// } +// } + +// impl WebEventExt for dioxus_html::TransitionData { +// #[inline(always)] +// fn try_as_web_event(&self) -> Option { +// self.downcast::().cloned() +// } +// } + +// impl WebEventExt for dioxus_html::WheelData { +// #[inline(always)] +// fn try_as_web_event(&self) -> Option { +// self.downcast::().cloned() +// } +// } + +// impl WebEventExt for dioxus_html::ResizeData { +// #[inline(always)] +// fn try_as_web_event(&self) -> Option { +// self.downcast::() +// .and_then(|e| e.detail().dyn_into::().ok()) +// } +// } diff --git a/packages/web/src/event/form.rs b/packages/web/src/event/form.rs new file mode 100644 index 0000000000..bc239e7419 --- /dev/null +++ b/packages/web/src/event/form.rs @@ -0,0 +1,157 @@ +use std::{any::Any, collections::HashMap}; + +use dioxus_html::{ + point_interaction::{ + InteractionElementOffset, InteractionLocation, ModifiersInteraction, PointerInteraction, + }, + AnimationData, DragData, FormData, FormValue, HasDragData, HasFileData, HasFormData, + HasImageData, HasMouseData, HtmlEventConverter, ImageData, MountedData, PlatformEventData, + ScrollData, +}; +use js_sys::Array; +use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue}; +use web_sys::{Document, Element, Event, MouseEvent}; + +pub struct WebFormData { + pub element: Element, + pub raw: Event, +} + +impl WebFormData { + pub fn new(element: Element, raw: Event) -> Self { + Self { element, raw } + } +} + +impl HasFormData for WebFormData { + fn value(&self) -> String { + let target = &self.element; + target + .dyn_ref() + .map(|input: &web_sys::HtmlInputElement| { + // todo: special case more input types + match input.type_().as_str() { + "checkbox" => { + match input.checked() { + true => "true".to_string(), + false => "false".to_string(), + } + }, + _ => { + input.value() + } + } + }) + .or_else(|| { + target + .dyn_ref() + .map(|input: &web_sys::HtmlTextAreaElement| input.value()) + }) + // select elements are NOT input events - because - why woudn't they be?? + .or_else(|| { + target + .dyn_ref() + .map(|input: &web_sys::HtmlSelectElement| input.value()) + }) + .or_else(|| { + target + .dyn_ref::() + .unwrap() + .text_content() + }) + .expect("only an InputElement or TextAreaElement or an element with contenteditable=true can have an oninput event listener") + } + + fn values(&self) -> HashMap { + let mut values = HashMap::new(); + + fn insert_value(map: &mut HashMap, key: String, new_value: String) { + map.entry(key.clone()).or_default().0.push(new_value); + } + + // try to fill in form values + if let Some(form) = self.element.dyn_ref::() { + let form_data = get_form_data(form); + for value in form_data.entries().into_iter().flatten() { + if let Ok(array) = value.dyn_into::() { + if let Some(name) = array.get(0).as_string() { + if let Ok(item_values) = array.get(1).dyn_into::() { + item_values + .iter() + .filter_map(|v| v.as_string()) + .for_each(|v| insert_value(&mut values, name.clone(), v)); + } else if let Ok(item_value) = array.get(1).dyn_into::() { + insert_value(&mut values, name, item_value.as_string().unwrap()); + } + } + } + } + } else if let Some(select) = self.element.dyn_ref::() { + // try to fill in select element values + let options = get_select_data(select); + values.insert("options".to_string(), FormValue(options)); + } + + values + } + + fn as_any(&self) -> &dyn Any { + &self.raw as &dyn Any + } +} + +impl HasFileData for WebFormData { + #[cfg(feature = "file-engine")] + fn files(&self) -> Option> { + use crate::bindings::WebFileEngine; + + let files = self + .element + .dyn_ref() + .and_then(|input: &web_sys::HtmlInputElement| { + input.files().and_then(|files| { + #[allow(clippy::arc_with_non_send_sync)] + WebFileEngine::new(files).map(|f| { + std::sync::Arc::new(f) as std::sync::Arc + }) + }) + }); + + files + } +} + +// web-sys does not expose the keys api for form data, so we need to manually bind to it +#[wasm_bindgen(inline_js = r#" +export function get_form_data(form) { + let values = new Map(); + const formData = new FormData(form); + + for (let name of formData.keys()) { + values.set(name, formData.getAll(name)); + } + + return values; +} +"#)] +extern "C" { + fn get_form_data(form: &web_sys::HtmlFormElement) -> js_sys::Map; +} + +// web-sys does not expose the keys api for select data, so we need to manually bind to it +#[wasm_bindgen(inline_js = r#" +export function get_select_data(select) { + let values = []; + for (let i = 0; i < select.options.length; i++) { + let option = select.options[i]; + if (option.selected) { + values.push(option.value.toString()); + } + } + + return values; +} +"#)] +extern "C" { + fn get_select_data(select: &web_sys::HtmlSelectElement) -> Vec; +} diff --git a/packages/web/src/event/image.rs b/packages/web/src/event/image.rs new file mode 100644 index 0000000000..988f7d7646 --- /dev/null +++ b/packages/web/src/event/image.rs @@ -0,0 +1,35 @@ +use std::{any::Any, collections::HashMap}; + +use dioxus_html::{ + point_interaction::{ + InteractionElementOffset, InteractionLocation, ModifiersInteraction, PointerInteraction, + }, + AnimationData, DragData, FormData, FormValue, HasDragData, HasFileData, HasFormData, + HasImageData, HasMouseData, HtmlEventConverter, ImageData, MountedData, PlatformEventData, + ScrollData, +}; +use js_sys::Array; +use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue}; +use web_sys::{Document, Element, Event, MouseEvent}; + +#[derive(Clone)] +pub struct WebImageEvent { + raw: Event, + error: bool, +} + +impl WebImageEvent { + pub fn new(raw: Event, error: bool) -> Self { + Self { raw, error } + } +} + +impl HasImageData for WebImageEvent { + fn load_error(&self) -> bool { + self.error + } + + fn as_any(&self) -> &dyn Any { + &self.raw as &dyn Any + } +} diff --git a/packages/web/src/event/resize.rs b/packages/web/src/event/resize.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/web/src/event/synthetic.rs b/packages/web/src/event/synthetic.rs new file mode 100644 index 0000000000..c683a305c4 --- /dev/null +++ b/packages/web/src/event/synthetic.rs @@ -0,0 +1,647 @@ +use dioxus_html::geometry::{self, PixelsSize}; +use dioxus_html::geometry::{ClientPoint, ElementPoint, PagePoint, ScreenPoint}; +use dioxus_html::input_data::{decode_key_location, decode_mouse_button_set, MouseButton}; +use dioxus_html::prelude::*; +use dioxus_html::HasFileData; +use dioxus_html::{events::HasKeyboardData, input_data::MouseButtonSet}; +use dioxus_html::{ + events::{ + AnimationData, CompositionData, KeyboardData, MouseData, PointerData, TouchData, + TransitionData, WheelData, + }, + geometry::WheelDelta, +}; +use keyboard_types::{Code, Key, Modifiers}; +use std::str::FromStr; +use wasm_bindgen::JsCast; +use web_sys::{js_sys, ResizeObserverEntry}; +use web_sys::{ + AnimationEvent, CompositionEvent, CustomEvent, Event, KeyboardEvent, MouseEvent, PointerEvent, + Touch, TouchEvent, TransitionEvent, WheelEvent, +}; + +/// A wrapper for the websys event that allows us to give it the impls from dioxus-html +pub struct Synthetic { + event: T, +} + +impl Synthetic { + pub fn new(event: T) -> Self { + Self { event } + } +} + +impl HasCompositionData for Synthetic { + fn data(&self) -> std::string::String { + self.event.data().unwrap_or_default() + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +impl HasKeyboardData for Synthetic { + fn key(&self) -> Key { + Key::from_str(self.event.key().as_str()).unwrap_or(Key::Unidentified) + } + + fn code(&self) -> Code { + Code::from_str(self.event.code().as_str()).unwrap_or(Code::Unidentified) + } + + fn location(&self) -> keyboard_types::Location { + decode_key_location(self.event.location() as usize) + } + + fn is_auto_repeating(&self) -> bool { + self.event.repeat() + } + + fn is_composing(&self) -> bool { + self.event.is_composing() + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +impl ModifiersInteraction for Synthetic { + fn modifiers(&self) -> Modifiers { + let mut modifiers = Modifiers::empty(); + + if self.event.alt_key() { + modifiers.insert(Modifiers::ALT); + } + if self.event.ctrl_key() { + modifiers.insert(Modifiers::CONTROL); + } + if self.event.meta_key() { + modifiers.insert(Modifiers::META); + } + if self.event.shift_key() { + modifiers.insert(Modifiers::SHIFT); + } + + modifiers + } +} + +impl InteractionLocation for Synthetic { + fn client_coordinates(&self) -> ClientPoint { + ClientPoint::new(self.event.client_x().into(), self.event.client_y().into()) + } + + fn page_coordinates(&self) -> PagePoint { + PagePoint::new(self.event.page_x().into(), self.event.page_y().into()) + } + + fn screen_coordinates(&self) -> ScreenPoint { + ScreenPoint::new(self.event.screen_x().into(), self.event.screen_y().into()) + } +} + +impl InteractionElementOffset for Synthetic { + fn element_coordinates(&self) -> ElementPoint { + ElementPoint::new(self.event.offset_x().into(), self.event.offset_y().into()) + } +} + +impl ModifiersInteraction for Synthetic { + fn modifiers(&self) -> Modifiers { + let mut modifiers = Modifiers::empty(); + + if self.event.alt_key() { + modifiers.insert(Modifiers::ALT); + } + if self.event.ctrl_key() { + modifiers.insert(Modifiers::CONTROL); + } + if self.event.meta_key() { + modifiers.insert(Modifiers::META); + } + if self.event.shift_key() { + modifiers.insert(Modifiers::SHIFT); + } + + modifiers + } +} + +impl PointerInteraction for Synthetic { + fn held_buttons(&self) -> MouseButtonSet { + decode_mouse_button_set(self.event.buttons()) + } + + fn trigger_button(&self) -> Option { + Some(MouseButton::from_web_code(self.event.button())) + } +} + +impl HasMouseData for Synthetic { + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +impl HasFileData for Synthetic {} + +impl HasDragData for Synthetic { + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +impl ModifiersInteraction for Synthetic { + fn modifiers(&self) -> Modifiers { + let mut modifiers = Modifiers::empty(); + + if self.event.alt_key() { + modifiers.insert(Modifiers::ALT); + } + if self.event.ctrl_key() { + modifiers.insert(Modifiers::CONTROL); + } + if self.event.meta_key() { + modifiers.insert(Modifiers::META); + } + if self.event.shift_key() { + modifiers.insert(Modifiers::SHIFT); + } + + modifiers + } +} + +impl HasTouchData for Synthetic { + fn touches(&self) -> Vec { + let touches = TouchEvent::touches(&self.event); + let mut result = Vec::with_capacity(touches.length() as usize); + for i in 0..touches.length() { + let touch = touches.get(i).unwrap(); + result.push(TouchPoint::new(Synthetic::new(touch))); + } + result + } + + fn touches_changed(&self) -> Vec { + let touches = self.event.changed_touches(); + let mut result = Vec::with_capacity(touches.length() as usize); + for i in 0..touches.length() { + let touch = touches.get(i).unwrap(); + result.push(TouchPoint::new(Synthetic::new(touch))); + } + result + } + + fn target_touches(&self) -> Vec { + let touches = self.event.target_touches(); + let mut result = Vec::with_capacity(touches.length() as usize); + for i in 0..touches.length() { + let touch = touches.get(i).unwrap(); + result.push(TouchPoint::new(Synthetic::new(touch))); + } + result + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +impl HasTouchPointData for Synthetic { + fn identifier(&self) -> i32 { + self.event.identifier() + } + + fn radius(&self) -> ScreenPoint { + ScreenPoint::new(self.event.radius_x().into(), self.event.radius_y().into()) + } + + fn rotation(&self) -> f64 { + self.event.rotation_angle() as f64 + } + + fn force(&self) -> f64 { + self.event.force() as f64 + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +impl InteractionLocation for Synthetic { + fn client_coordinates(&self) -> ClientPoint { + ClientPoint::new(self.event.client_x().into(), self.event.client_y().into()) + } + + fn screen_coordinates(&self) -> ScreenPoint { + ScreenPoint::new(self.event.screen_x().into(), self.event.screen_y().into()) + } + + fn page_coordinates(&self) -> PagePoint { + PagePoint::new(self.event.page_x().into(), self.event.page_y().into()) + } +} + +impl HasPointerData for Synthetic { + fn pointer_id(&self) -> i32 { + self.event.pointer_id() + } + + fn width(&self) -> i32 { + self.event.width() + } + + fn height(&self) -> i32 { + self.event.height() + } + + fn pressure(&self) -> f32 { + self.event.pressure() + } + + fn tangential_pressure(&self) -> f32 { + self.event.tangential_pressure() + } + + fn tilt_x(&self) -> i32 { + self.event.tilt_x() + } + + fn tilt_y(&self) -> i32 { + self.event.tilt_y() + } + + fn twist(&self) -> i32 { + self.event.twist() + } + + fn pointer_type(&self) -> String { + self.event.pointer_type() + } + + fn is_primary(&self) -> bool { + self.event.is_primary() + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +impl InteractionLocation for Synthetic { + fn client_coordinates(&self) -> ClientPoint { + ClientPoint::new(self.event.client_x().into(), self.event.client_y().into()) + } + + fn screen_coordinates(&self) -> ScreenPoint { + ScreenPoint::new(self.event.screen_x().into(), self.event.screen_y().into()) + } + + fn page_coordinates(&self) -> PagePoint { + PagePoint::new(self.event.page_x().into(), self.event.page_y().into()) + } +} + +impl InteractionElementOffset for Synthetic { + fn element_coordinates(&self) -> ElementPoint { + ElementPoint::new(self.event.offset_x().into(), self.event.offset_y().into()) + } +} + +impl ModifiersInteraction for Synthetic { + fn modifiers(&self) -> Modifiers { + let mut modifiers = Modifiers::empty(); + + if self.event.alt_key() { + modifiers.insert(Modifiers::ALT); + } + if self.event.ctrl_key() { + modifiers.insert(Modifiers::CONTROL); + } + if self.event.meta_key() { + modifiers.insert(Modifiers::META); + } + if self.event.shift_key() { + modifiers.insert(Modifiers::SHIFT); + } + + modifiers + } +} + +impl PointerInteraction for Synthetic { + fn held_buttons(&self) -> MouseButtonSet { + decode_mouse_button_set(self.event.buttons()) + } + + fn trigger_button(&self) -> Option { + Some(MouseButton::from_web_code(self.event.button())) + } +} + +impl HasWheelData for Synthetic { + fn delta(&self) -> WheelDelta { + WheelDelta::from_web_attributes( + self.event.delta_mode(), + self.event.delta_x(), + self.event.delta_y(), + self.event.delta_z(), + ) + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +impl HasMouseData for Synthetic { + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +impl InteractionLocation for Synthetic { + fn client_coordinates(&self) -> ClientPoint { + ClientPoint::new(self.event.client_x().into(), self.event.client_y().into()) + } + + fn screen_coordinates(&self) -> ScreenPoint { + ScreenPoint::new(self.event.screen_x().into(), self.event.screen_y().into()) + } + + fn page_coordinates(&self) -> PagePoint { + PagePoint::new(self.event.page_x().into(), self.event.page_y().into()) + } +} + +impl InteractionElementOffset for Synthetic { + fn element_coordinates(&self) -> ElementPoint { + ElementPoint::new(self.event.offset_x().into(), self.event.offset_y().into()) + } +} + +impl ModifiersInteraction for Synthetic { + fn modifiers(&self) -> Modifiers { + let mut modifiers = Modifiers::empty(); + + if self.event.alt_key() { + modifiers.insert(Modifiers::ALT); + } + if self.event.ctrl_key() { + modifiers.insert(Modifiers::CONTROL); + } + if self.event.meta_key() { + modifiers.insert(Modifiers::META); + } + if self.event.shift_key() { + modifiers.insert(Modifiers::SHIFT); + } + + modifiers + } +} + +impl PointerInteraction for Synthetic { + fn held_buttons(&self) -> MouseButtonSet { + decode_mouse_button_set(self.event.buttons()) + } + + fn trigger_button(&self) -> Option { + Some(MouseButton::from_web_code(self.event.button())) + } +} + +impl HasAnimationData for Synthetic { + fn animation_name(&self) -> String { + self.event.animation_name() + } + + fn pseudo_element(&self) -> String { + self.event.pseudo_element() + } + + fn elapsed_time(&self) -> f32 { + self.event.elapsed_time() + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +impl HasTransitionData for Synthetic { + fn elapsed_time(&self) -> f32 { + self.event.elapsed_time() + } + + fn property_name(&self) -> String { + self.event.property_name() + } + + fn pseudo_element(&self) -> String { + self.event.pseudo_element() + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +// impl From> for MountedData { +// fn from(value: Synthetic) -> Self { +// todo!() +// } +// } + +// impl From for ResizeData { +// #[inline] +// fn from(e: Event) -> Self { +// >::from(&e) +// } +// } + +// impl From<&Event> for ResizeData { +// #[inline] +// fn from(e: &Event) -> Self { +// let e: &CustomEvent = e.unchecked_ref(); +// let value = e.detail(); +// Self::from(value.unchecked_into::()) +// } +// } +// #[cfg(feature = "mounted")] +// impl From<&web_sys::Element> for MountedData { +// fn from(e: &web_sys::Element) -> Self { +// MountedData::new(e.clone()) +// } +// } + +#[cfg(feature = "mounted")] +impl RenderedElementBacking for Synthetic { + fn as_any(&self) -> &dyn std::any::Any { + self + } + // fn get_scroll_offset( + // &self, + // ) -> std::pin::Pin>>> + // { + // let left = self.event.scroll_left(); + // let top = self.event.scroll_top(); + // let result = Ok(geometry::PixelsVector2D::new(left as f64, top as f64)); + // Box::pin(async { result }) + // } + + // fn get_scroll_size( + // &self, + // ) -> std::pin::Pin>>> + // { + // let width = self.event.scroll_width(); + // let height = self.event.scroll_height(); + // let result = Ok(geometry::PixelsSize::new(width as f64, height as f64)); + // Box::pin(async { result }) + // } + + // fn get_client_rect( + // &self, + // ) -> std::pin::Pin>>> + // { + // let rect = self.event.get_bounding_client_rect(); + // let result = Ok(geometry::PixelsRect::new( + // euclid::Point2D::new(rect.left(), rect.top()), + // euclid::Size2D::new(rect.width(), rect.height()), + // )); + // Box::pin(async { result }) + // } + + // fn as_any(&self) -> &dyn std::any::Any { + // self + // } + + // fn scroll_to( + // &self, + // behavior: ScrollBehavior, + // ) -> std::pin::Pin>>> { + // let options = web_sys::ScrollIntoViewOptions::new(); + // match behavior { + // ScrollBehavior::Instant => { + // options.set_behavior(web_sys::ScrollBehavior::Instant); + // } + // ScrollBehavior::Smooth => { + // options.set_behavior(web_sys::ScrollBehavior::Smooth); + // } + // } + // self.event + // .scroll_into_view_with_scroll_into_view_options(&options); + + // Box::pin(async { Ok(()) }) + // } + + // fn set_focus( + // &self, + // focus: bool, + // ) -> std::pin::Pin>>> { + // #[derive(Debug)] + // struct FocusError(wasm_bindgen::JsValue); + + // impl std::fmt::Display for FocusError { + // fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + // write!(f, "failed to focus element {:?}", self.event.0) + // } + // } + + // impl std::error::Error for FocusError {} + + // let result = self + // .dyn_ref::() + // .ok_or_else(|| MountedError::OperationFailed(Box::new(FocusError(self.event.into())))) + // .and_then(|e| { + // (if focus { e.focus() } else { e.blur() }) + // .map_err(|err| MountedError::OperationFailed(Box::new(FocusError(err)))) + // }); + // Box::pin(async { result }) + // } +} + +fn extract_first_size(resize_observer_output: js_sys::Array) -> ResizeResult { + let first = resize_observer_output.get(0); + let size = first.unchecked_into::(); + + // inline_size matches the width of the element if its writing-mode is horizontal, the height otherwise + let inline_size = size.inline_size(); + // block_size matches the height of the element if its writing-mode is horizontal, the width otherwise + let block_size = size.block_size(); + + Ok(PixelsSize::new(inline_size, block_size)) +} + +impl HasResizeData for Synthetic { + fn as_any(&self) -> &dyn std::any::Any { + self + } + + fn get_border_box_size(&self) -> ResizeResult { + extract_first_size(self.event.border_box_size()) + } + + fn get_content_box_size(&self) -> ResizeResult { + extract_first_size(self.event.content_box_size()) + } +} + +impl HasScrollData for Synthetic { + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +impl HasClipboardData for Synthetic { + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +impl HasFocusData for Synthetic { + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +impl HasToggleData for Synthetic { + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +impl HasSelectionData for Synthetic { + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +impl HasMediaData for Synthetic { + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +impl HasFileData for Synthetic { + #[cfg(feature = "file-engine")] + fn files(&self) -> Option> { + let files = self + .event + .dyn_ref() + .and_then(|input: &web_sys::HtmlInputElement| { + input.files().and_then(|files| { + #[allow(clippy::arc_with_non_send_sync)] + crate::bindings::WebFileEngine::new(files).map(|f| { + std::sync::Arc::new(f) as std::sync::Arc + }) + }) + }); + + files + } +} From 6101b52cc92ccd91a5caf439b2c11f10b2a8e467 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 22 Aug 2024 05:21:08 -0700 Subject: [PATCH 047/139] web crate almost done being cleaned up --- packages/web/Cargo.toml | 8 +- .../web/src/{hot_reload.rs => devtools.rs} | 0 packages/web/src/document.rs | 12 +- packages/web/src/event.rs | 19 +- packages/web/src/event/drag.rs | 53 +- .../web/src/{bindings.rs => event/file.rs} | 1 + packages/web/src/event/form.rs | 11 +- packages/web/src/event/image.rs | 15 +- packages/web/src/event/synthetic.rs | 55 +- packages/web/src/lib.rs | 47 +- packages/web/src/web_sys_bind/events.rs | 675 ------------------ 11 files changed, 79 insertions(+), 817 deletions(-) rename packages/web/src/{hot_reload.rs => devtools.rs} (100%) rename packages/web/src/{bindings.rs => event/file.rs} (99%) delete mode 100644 packages/web/src/web_sys_bind/events.rs diff --git a/packages/web/Cargo.toml b/packages/web/Cargo.toml index 0e075a254a..7fc0ce09c2 100644 --- a/packages/web/Cargo.toml +++ b/packages/web/Cargo.toml @@ -53,6 +53,7 @@ features = [ "DragEvent", "FocusEvent", "HtmlElement", + "HtmlHeadElement", "HtmlFormElement", "HtmlInputElement", "HtmlSelectElement", @@ -89,7 +90,12 @@ panic_hook = ["dep:console_error_panic_hook"] hydrate = ["web-sys/Comment", "ciborium", "dep:serde"] mounted = ["web-sys/Element", "dioxus-html/mounted"] -file-engine = ["dioxus-html/file-engine"] +file-engine = [ + "dioxus-html/file-engine", + "web-sys/File", + "web-sys/FileList", + "web-sys/FileReader" +] devtools = [ "web-sys/MessageEvent", "web-sys/WebSocket", diff --git a/packages/web/src/hot_reload.rs b/packages/web/src/devtools.rs similarity index 100% rename from packages/web/src/hot_reload.rs rename to packages/web/src/devtools.rs diff --git a/packages/web/src/document.rs b/packages/web/src/document.rs index 4d73407113..84c77ff7da 100644 --- a/packages/web/src/document.rs +++ b/packages/web/src/document.rs @@ -22,11 +22,15 @@ pub struct WebDocument { impl Document for WebDocument { fn eval(&self, js: String) -> Eval { let (tx, eval) = Eval::from_parts(); - let res = js_sys::eval(&js); - match res { - Ok(ok) => todo!(), - Err(err) => todo!(), + + // todo: this deserialize is probably wrong. + _ = match js_sys::eval(&js) { + Ok(ok) => tx.send(Ok(serde_wasm_bindgen::from_value(ok).unwrap())), + Err(_err) => tx.send(Err(dioxus_document::EvalError::Communication( + "eval failed".to_string(), + ))), }; + eval } diff --git a/packages/web/src/event.rs b/packages/web/src/event.rs index 305b1a2a7b..1005ade623 100644 --- a/packages/web/src/event.rs +++ b/packages/web/src/event.rs @@ -1,23 +1,16 @@ -use std::{any::Any, collections::HashMap}; - use dioxus_html::{ - point_interaction::{ - InteractionElementOffset, InteractionLocation, ModifiersInteraction, PointerInteraction, - }, - AnimationData, DragData, FormData, FormValue, HasDragData, HasFileData, HasFormData, - HasImageData, HasMouseData, HtmlEventConverter, ImageData, MountedData, PlatformEventData, - ScrollData, WheelEvent, + DragData, FormData, HtmlEventConverter, ImageData, MountedData, PlatformEventData, }; use drag::WebDragData; use form::WebFormData; use image::WebImageEvent; -use js_sys::Array; use synthetic::Synthetic; -use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue}; -use web_sys::{Document, Element, Event, MouseEvent}; +use wasm_bindgen::JsCast; +use web_sys::{Document, Element, Event}; mod drag; mod ext; +mod file; mod form; mod image; mod resize; @@ -155,7 +148,7 @@ struct GenericWebSysEvent { raw: Event, element: Element, } - +/// Converts our Synthetic wrapper into the trait objects dioxus is expectinga trait Synthesize { fn synthesize>>(&self) -> F; } @@ -177,8 +170,6 @@ fn downcast_event(event: &PlatformEventData) -> &GenericWebSysEvent { .expect("event should be a GenericWebSysEvent") } -// todo: some of these events are being casted to the wrong event type. -// We need tests that simulate clicks/etc and make sure every event type works. pub(crate) fn virtual_event_from_websys_event( event: web_sys::Event, target: Element, diff --git a/packages/web/src/event/drag.rs b/packages/web/src/event/drag.rs index ff6225924f..008ff16c86 100644 --- a/packages/web/src/event/drag.rs +++ b/packages/web/src/event/drag.rs @@ -1,24 +1,23 @@ -use std::{any::Any, collections::HashMap}; use dioxus_html::{ point_interaction::{ InteractionElementOffset, InteractionLocation, ModifiersInteraction, PointerInteraction, - }, - AnimationData, DragData, FormData, FormValue, HasDragData, HasFileData, HasFormData, - HasImageData, HasMouseData, HtmlEventConverter, ImageData, MountedData, PlatformEventData, - ScrollData, + }, HasDragData, HasFileData, HasMouseData, }; -use js_sys::Array; -use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue}; -use web_sys::{Document, Element, Event, MouseEvent}; +use wasm_bindgen::{JsCast}; +use web_sys::{MouseEvent}; + +use super::synthetic::Synthetic; pub struct WebDragData { - raw: MouseEvent, + raw: Synthetic, } impl WebDragData { pub fn new(raw: MouseEvent) -> Self { - Self { raw } + Self { + raw: Synthetic::new(raw), + } } } @@ -36,59 +35,51 @@ impl HasMouseData for WebDragData { impl PointerInteraction for WebDragData { fn trigger_button(&self) -> Option { - // self.raw.trigger_button() - todo!() + self.raw.trigger_button() } fn held_buttons(&self) -> dioxus_html::input_data::MouseButtonSet { - // self.raw.held_buttons() - todo!() + self.raw.held_buttons() } } impl ModifiersInteraction for WebDragData { fn modifiers(&self) -> dioxus_html::prelude::Modifiers { - // self.raw.modifiers() - todo!() + self.raw.modifiers() } } impl InteractionElementOffset for WebDragData { fn coordinates(&self) -> dioxus_html::geometry::Coordinates { - // self.raw.coordinates() - todo!() + self.raw.coordinates() } fn element_coordinates(&self) -> dioxus_html::geometry::ElementPoint { - // self.raw.element_coordinates() - todo!() + self.raw.element_coordinates() } } impl InteractionLocation for WebDragData { fn client_coordinates(&self) -> dioxus_html::geometry::ClientPoint { - // self.raw.client_coordinates() - todo!() + self.raw.client_coordinates() } fn screen_coordinates(&self) -> dioxus_html::geometry::ScreenPoint { - // self.raw.screen_coordinates() - todo!() + self.raw.screen_coordinates() } fn page_coordinates(&self) -> dioxus_html::geometry::PagePoint { - // self.raw.page_coordinates() - todo!() + self.raw.page_coordinates() } } impl HasFileData for WebDragData { #[cfg(feature = "file-engine")] fn files(&self) -> Option> { - use crate::bindings::WebFileEngine; + use super::file::WebFileEngine; - let files = self - .raw + self.raw + .event .dyn_ref::() .and_then(|drag_event| { drag_event.data_transfer().and_then(|dt| { @@ -99,8 +90,6 @@ impl HasFileData for WebDragData { }) }) }) - }); - - files + }) } } diff --git a/packages/web/src/bindings.rs b/packages/web/src/event/file.rs similarity index 99% rename from packages/web/src/bindings.rs rename to packages/web/src/event/file.rs index d9613c70d8..79696ea1ed 100644 --- a/packages/web/src/bindings.rs +++ b/packages/web/src/event/file.rs @@ -1,5 +1,6 @@ use std::any::Any; +#[cfg(feature = "file-engine")] use dioxus_html::FileEngine; // use dioxus_html::FileEngine; use futures_channel::oneshot; diff --git a/packages/web/src/event/form.rs b/packages/web/src/event/form.rs index bc239e7419..93c0a4e988 100644 --- a/packages/web/src/event/form.rs +++ b/packages/web/src/event/form.rs @@ -1,16 +1,11 @@ use std::{any::Any, collections::HashMap}; use dioxus_html::{ - point_interaction::{ - InteractionElementOffset, InteractionLocation, ModifiersInteraction, PointerInteraction, - }, - AnimationData, DragData, FormData, FormValue, HasDragData, HasFileData, HasFormData, - HasImageData, HasMouseData, HtmlEventConverter, ImageData, MountedData, PlatformEventData, - ScrollData, + FormValue, HasFileData, HasFormData, }; use js_sys::Array; use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue}; -use web_sys::{Document, Element, Event, MouseEvent}; +use web_sys::{Element, Event}; pub struct WebFormData { pub element: Element, @@ -103,7 +98,7 @@ impl HasFormData for WebFormData { impl HasFileData for WebFormData { #[cfg(feature = "file-engine")] fn files(&self) -> Option> { - use crate::bindings::WebFileEngine; + use super::file::WebFileEngine; let files = self .element diff --git a/packages/web/src/event/image.rs b/packages/web/src/event/image.rs index 988f7d7646..11c9d44787 100644 --- a/packages/web/src/event/image.rs +++ b/packages/web/src/event/image.rs @@ -1,16 +1,7 @@ -use std::{any::Any, collections::HashMap}; +use std::any::Any; -use dioxus_html::{ - point_interaction::{ - InteractionElementOffset, InteractionLocation, ModifiersInteraction, PointerInteraction, - }, - AnimationData, DragData, FormData, FormValue, HasDragData, HasFileData, HasFormData, - HasImageData, HasMouseData, HtmlEventConverter, ImageData, MountedData, PlatformEventData, - ScrollData, -}; -use js_sys::Array; -use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue}; -use web_sys::{Document, Element, Event, MouseEvent}; +use dioxus_html::HasImageData; +use web_sys::Event; #[derive(Clone)] pub struct WebImageEvent { diff --git a/packages/web/src/event/synthetic.rs b/packages/web/src/event/synthetic.rs index c683a305c4..98f3214a32 100644 --- a/packages/web/src/event/synthetic.rs +++ b/packages/web/src/event/synthetic.rs @@ -1,28 +1,22 @@ -use dioxus_html::geometry::{self, PixelsSize}; +use dioxus_html::geometry::PixelsSize; +use dioxus_html::geometry::WheelDelta; use dioxus_html::geometry::{ClientPoint, ElementPoint, PagePoint, ScreenPoint}; use dioxus_html::input_data::{decode_key_location, decode_mouse_button_set, MouseButton}; use dioxus_html::prelude::*; use dioxus_html::HasFileData; use dioxus_html::{events::HasKeyboardData, input_data::MouseButtonSet}; -use dioxus_html::{ - events::{ - AnimationData, CompositionData, KeyboardData, MouseData, PointerData, TouchData, - TransitionData, WheelData, - }, - geometry::WheelDelta, -}; use keyboard_types::{Code, Key, Modifiers}; use std::str::FromStr; use wasm_bindgen::JsCast; use web_sys::{js_sys, ResizeObserverEntry}; use web_sys::{ - AnimationEvent, CompositionEvent, CustomEvent, Event, KeyboardEvent, MouseEvent, PointerEvent, - Touch, TouchEvent, TransitionEvent, WheelEvent, + AnimationEvent, CompositionEvent, Event, KeyboardEvent, MouseEvent, PointerEvent, Touch, + TouchEvent, TransitionEvent, WheelEvent, }; /// A wrapper for the websys event that allows us to give it the impls from dioxus-html pub struct Synthetic { - event: T, + pub event: T, } impl Synthetic { @@ -451,34 +445,6 @@ impl HasTransitionData for Synthetic { } } -// impl From> for MountedData { -// fn from(value: Synthetic) -> Self { -// todo!() -// } -// } - -// impl From for ResizeData { -// #[inline] -// fn from(e: Event) -> Self { -// >::from(&e) -// } -// } - -// impl From<&Event> for ResizeData { -// #[inline] -// fn from(e: &Event) -> Self { -// let e: &CustomEvent = e.unchecked_ref(); -// let value = e.detail(); -// Self::from(value.unchecked_into::()) -// } -// } -// #[cfg(feature = "mounted")] -// impl From<&web_sys::Element> for MountedData { -// fn from(e: &web_sys::Element) -> Self { -// MountedData::new(e.clone()) -// } -// } - #[cfg(feature = "mounted")] impl RenderedElementBacking for Synthetic { fn as_any(&self) -> &dyn std::any::Any { @@ -577,6 +543,15 @@ fn extract_first_size(resize_observer_output: js_sys::Array) -> ResizeResult for ResizeData { +// #[inline] +// fn from(e: &Event) -> Self { +// let e: &CustomEvent = e.unchecked_ref(); +// let value = e.detail(); +// Self::from(value.unchecked_into::()) +// } +// } + impl HasResizeData for Synthetic { fn as_any(&self) -> &dyn std::any::Any { self @@ -636,7 +611,7 @@ impl HasFileData for Synthetic { .and_then(|input: &web_sys::HtmlInputElement| { input.files().and_then(|files| { #[allow(clippy::arc_with_non_send_sync)] - crate::bindings::WebFileEngine::new(files).map(|f| { + super::file::WebFileEngine::new(files).map(|f| { std::sync::Arc::new(f) as std::sync::Arc }) }) diff --git a/packages/web/src/lib.rs b/packages/web/src/lib.rs index 193ce726d5..8fc3b6c971 100644 --- a/packages/web/src/lib.rs +++ b/packages/web/src/lib.rs @@ -28,24 +28,22 @@ use futures_util::{pin_mut, select, FutureExt, StreamExt}; mod cfg; mod dom; - mod event; pub mod launch; mod mutations; -pub use event::*; - -/// -mod bindings; +use event::*; #[cfg(feature = "document")] mod document; + #[cfg(feature = "document")] pub use document::WebDocument; -#[cfg(all(feature = "hot_reload", debug_assertions))] -mod hot_reload; +#[cfg(all(feature = "devtools", debug_assertions))] +mod devtools; mod hydration; + #[allow(unused)] pub use hydration::*; @@ -70,8 +68,8 @@ pub async fn run(mut virtual_dom: VirtualDom, web_config: Config) -> ! { console_error_panic_hook::set_once(); } - #[cfg(all(feature = "hot_reload", debug_assertions))] - let mut hotreload_rx = hot_reload::init(); + #[cfg(all(feature = "devtools", debug_assertions))] + let mut hotreload_rx = devtools::init(); let runtime = virtual_dom.runtime(); @@ -126,7 +124,7 @@ pub async fn run(mut virtual_dom: VirtualDom, web_config: Config) -> ! { loop { // if virtual dom has nothing, wait for it to have something before requesting idle time // if there is work then this future resolves immediately. - #[cfg(all(feature = "hot_reload", debug_assertions))] + #[cfg(all(feature = "devtools", debug_assertions))] let template; #[allow(unused)] let mut hydration_work: Option = None; @@ -140,15 +138,15 @@ pub async fn run(mut virtual_dom: VirtualDom, web_config: Config) -> ! { .flatten(); let mut rx_hydration = hydration_receiver_iter.select_next_some(); - #[cfg(all(feature = "hot_reload", debug_assertions))] + #[cfg(all(feature = "devtools", debug_assertions))] #[allow(unused)] { - let mut hot_reload_next = hotreload_rx.select_next_some(); + let mut devtools_next = hotreload_rx.select_next_some(); select! { _ = work => { template = None; }, - new_template = hot_reload_next => { + new_template = devtools_next => { template = Some(new_template); }, hydration_data = rx_hydration => { @@ -161,7 +159,7 @@ pub async fn run(mut virtual_dom: VirtualDom, web_config: Config) -> ! { } } - #[cfg(not(all(feature = "hot_reload", debug_assertions)))] + #[cfg(not(all(feature = "devtools", debug_assertions)))] #[allow(unused)] { select! { @@ -176,13 +174,13 @@ pub async fn run(mut virtual_dom: VirtualDom, web_config: Config) -> ! { } } - #[cfg(all(feature = "hot_reload", debug_assertions))] + #[cfg(all(feature = "devtools", debug_assertions))] if let Some(hr_msg) = template { // Replace all templates - dioxus_devtools::apply_changes(&mut virtual_dom, &hr_msg); + dioxus_devtools::apply_changes(&virtual_dom, &hr_msg); if !hr_msg.assets.is_empty() { - crate::hot_reload::invalidate_browser_asset_cache(); + crate::devtools::invalidate_browser_asset_cache(); } } @@ -191,23 +189,10 @@ pub async fn run(mut virtual_dom: VirtualDom, web_config: Config) -> ! { websys_dom.rehydrate_streaming(hydration_data, &mut virtual_dom); } - // Todo: This is currently disabled because it has a negative impact on response times for events but it could be re-enabled for tasks - // Jank free rendering - // - // 1. wait for the browser to give us "idle" time - // 2. During idle time, diff the dom - // 3. Stop diffing if the deadline is exceeded - // 4. Wait for the animation frame to patch the dom - - // wait for the mainthread to schedule us in - // let deadline = work_loop.wait_for_idle_time().await; - // run the virtualdom work phase until the frame deadline is reached virtual_dom.render_immediate(&mut websys_dom); - // wait for the animation frame to fire so we can apply our changes - // work_loop.wait_for_raf().await; - + // Flush all pending edits to the dom in one swoop websys_dom.flush_edits(); } } diff --git a/packages/web/src/web_sys_bind/events.rs b/packages/web/src/web_sys_bind/events.rs deleted file mode 100644 index 58e39cc11f..0000000000 --- a/packages/web/src/web_sys_bind/events.rs +++ /dev/null @@ -1,675 +0,0 @@ -use crate::events::HasKeyboardData; -use crate::events::{ - AnimationData, CompositionData, KeyboardData, MouseData, PointerData, TouchData, - TransitionData, WheelData, -}; -use crate::file_data::HasFileData; -use crate::geometry::PixelsSize; -use crate::geometry::{ClientPoint, ElementPoint, PagePoint, ScreenPoint}; -use crate::input_data::{decode_key_location, decode_mouse_button_set, MouseButton}; -use crate::prelude::*; -use keyboard_types::{Code, Key, Modifiers}; -use std::str::FromStr; -use wasm_bindgen::JsCast; -use web_sys::{js_sys, ResizeObserverEntry}; -use web_sys::{ - AnimationEvent, CompositionEvent, CustomEvent, Event, KeyboardEvent, MouseEvent, PointerEvent, - Touch, TouchEvent, TransitionEvent, WheelEvent, -}; - -macro_rules! uncheck_convert { - ($t:ty, $d:ty) => { - impl From for $d { - #[inline] - fn from(e: Event) -> Self { - let e: $t = e.unchecked_into(); - Self::from(e) - } - } - - impl From<&Event> for $d { - #[inline] - fn from(e: &Event) -> Self { - let e: &$t = e.unchecked_ref(); - Self::from(e.clone()) - } - } - }; - ($($t:ty => $d:ty),+ $(,)?) => { - $(uncheck_convert!($t, $d);)+ - }; -} - -uncheck_convert![ - web_sys::CompositionEvent => CompositionData, - web_sys::KeyboardEvent => KeyboardData, - web_sys::MouseEvent => MouseData, - web_sys::TouchEvent => TouchData, - web_sys::PointerEvent => PointerData, - web_sys::WheelEvent => WheelData, - web_sys::AnimationEvent => AnimationData, - web_sys::TransitionEvent => TransitionData, - web_sys::MouseEvent => DragData, - web_sys::FocusEvent => FocusData, -]; - -impl From for ResizeData { - #[inline] - fn from(e: Event) -> Self { - >::from(&e) - } -} - -impl From<&Event> for ResizeData { - #[inline] - fn from(e: &Event) -> Self { - let e: &CustomEvent = e.unchecked_ref(); - let value = e.detail(); - Self::from(value.unchecked_into::()) - } -} - -impl HasCompositionData for CompositionEvent { - fn data(&self) -> std::string::String { - self.data().unwrap_or_default() - } - - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl HasKeyboardData for KeyboardEvent { - fn key(&self) -> Key { - Key::from_str(self.key().as_str()).unwrap_or(Key::Unidentified) - } - - fn code(&self) -> Code { - Code::from_str(self.code().as_str()).unwrap_or(Code::Unidentified) - } - - fn location(&self) -> keyboard_types::Location { - decode_key_location(self.location() as usize) - } - - fn is_auto_repeating(&self) -> bool { - self.repeat() - } - - fn is_composing(&self) -> bool { - self.is_composing() - } - - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl ModifiersInteraction for KeyboardEvent { - fn modifiers(&self) -> Modifiers { - let mut modifiers = Modifiers::empty(); - - if self.alt_key() { - modifiers.insert(Modifiers::ALT); - } - if self.ctrl_key() { - modifiers.insert(Modifiers::CONTROL); - } - if self.meta_key() { - modifiers.insert(Modifiers::META); - } - if self.shift_key() { - modifiers.insert(Modifiers::SHIFT); - } - - modifiers - } -} - -impl InteractionLocation for MouseEvent { - fn client_coordinates(&self) -> ClientPoint { - ClientPoint::new(self.client_x().into(), self.client_y().into()) - } - - fn page_coordinates(&self) -> PagePoint { - PagePoint::new(self.page_x().into(), self.page_y().into()) - } - - fn screen_coordinates(&self) -> ScreenPoint { - ScreenPoint::new(self.screen_x().into(), self.screen_y().into()) - } -} - -impl InteractionElementOffset for MouseEvent { - fn element_coordinates(&self) -> ElementPoint { - ElementPoint::new(self.offset_x().into(), self.offset_y().into()) - } -} - -impl ModifiersInteraction for MouseEvent { - fn modifiers(&self) -> Modifiers { - let mut modifiers = Modifiers::empty(); - - if self.alt_key() { - modifiers.insert(Modifiers::ALT); - } - if self.ctrl_key() { - modifiers.insert(Modifiers::CONTROL); - } - if self.meta_key() { - modifiers.insert(Modifiers::META); - } - if self.shift_key() { - modifiers.insert(Modifiers::SHIFT); - } - - modifiers - } -} - -impl PointerInteraction for MouseEvent { - fn held_buttons(&self) -> crate::input_data::MouseButtonSet { - decode_mouse_button_set(self.buttons()) - } - - fn trigger_button(&self) -> Option { - Some(MouseButton::from_web_code(self.button())) - } -} - -impl HasMouseData for MouseEvent { - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl HasFileData for MouseEvent {} - -impl HasDragData for MouseEvent { - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl ModifiersInteraction for TouchEvent { - fn modifiers(&self) -> Modifiers { - let mut modifiers = Modifiers::empty(); - - if self.alt_key() { - modifiers.insert(Modifiers::ALT); - } - if self.ctrl_key() { - modifiers.insert(Modifiers::CONTROL); - } - if self.meta_key() { - modifiers.insert(Modifiers::META); - } - if self.shift_key() { - modifiers.insert(Modifiers::SHIFT); - } - - modifiers - } -} - -impl crate::events::HasTouchData for TouchEvent { - fn touches(&self) -> Vec { - let touches = TouchEvent::touches(self); - let mut result = Vec::with_capacity(touches.length() as usize); - for i in 0..touches.length() { - let touch = touches.get(i).unwrap(); - result.push(TouchPoint::new(touch)); - } - result - } - - fn touches_changed(&self) -> Vec { - let touches = self.changed_touches(); - let mut result = Vec::with_capacity(touches.length() as usize); - for i in 0..touches.length() { - let touch = touches.get(i).unwrap(); - result.push(TouchPoint::new(touch)); - } - result - } - - fn target_touches(&self) -> Vec { - let touches = self.target_touches(); - let mut result = Vec::with_capacity(touches.length() as usize); - for i in 0..touches.length() { - let touch = touches.get(i).unwrap(); - result.push(TouchPoint::new(touch)); - } - result - } - - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl HasTouchPointData for Touch { - fn identifier(&self) -> i32 { - self.identifier() - } - - fn radius(&self) -> ScreenPoint { - ScreenPoint::new(self.radius_x().into(), self.radius_y().into()) - } - - fn rotation(&self) -> f64 { - self.rotation_angle() as f64 - } - - fn force(&self) -> f64 { - self.force() as f64 - } - - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl InteractionLocation for Touch { - fn client_coordinates(&self) -> ClientPoint { - ClientPoint::new(self.client_x().into(), self.client_y().into()) - } - - fn screen_coordinates(&self) -> ScreenPoint { - ScreenPoint::new(self.screen_x().into(), self.screen_y().into()) - } - - fn page_coordinates(&self) -> PagePoint { - PagePoint::new(self.page_x().into(), self.page_y().into()) - } -} - -impl HasPointerData for PointerEvent { - fn pointer_id(&self) -> i32 { - self.pointer_id() - } - - fn width(&self) -> i32 { - self.width() - } - - fn height(&self) -> i32 { - self.height() - } - - fn pressure(&self) -> f32 { - self.pressure() - } - - fn tangential_pressure(&self) -> f32 { - self.tangential_pressure() - } - - fn tilt_x(&self) -> i32 { - self.tilt_x() - } - - fn tilt_y(&self) -> i32 { - self.tilt_y() - } - - fn twist(&self) -> i32 { - self.twist() - } - - fn pointer_type(&self) -> String { - self.pointer_type() - } - - fn is_primary(&self) -> bool { - self.is_primary() - } - - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl InteractionLocation for PointerEvent { - fn client_coordinates(&self) -> ClientPoint { - ClientPoint::new(self.client_x().into(), self.client_y().into()) - } - - fn screen_coordinates(&self) -> ScreenPoint { - ScreenPoint::new(self.screen_x().into(), self.screen_y().into()) - } - - fn page_coordinates(&self) -> PagePoint { - PagePoint::new(self.page_x().into(), self.page_y().into()) - } -} - -impl InteractionElementOffset for PointerEvent { - fn element_coordinates(&self) -> ElementPoint { - ElementPoint::new(self.offset_x().into(), self.offset_y().into()) - } -} - -impl ModifiersInteraction for PointerEvent { - fn modifiers(&self) -> Modifiers { - let mut modifiers = Modifiers::empty(); - - if self.alt_key() { - modifiers.insert(Modifiers::ALT); - } - if self.ctrl_key() { - modifiers.insert(Modifiers::CONTROL); - } - if self.meta_key() { - modifiers.insert(Modifiers::META); - } - if self.shift_key() { - modifiers.insert(Modifiers::SHIFT); - } - - modifiers - } -} - -impl PointerInteraction for PointerEvent { - fn held_buttons(&self) -> crate::input_data::MouseButtonSet { - decode_mouse_button_set(self.buttons()) - } - - fn trigger_button(&self) -> Option { - Some(MouseButton::from_web_code(self.button())) - } -} - -impl HasWheelData for WheelEvent { - fn delta(&self) -> crate::geometry::WheelDelta { - crate::geometry::WheelDelta::from_web_attributes( - self.delta_mode(), - self.delta_x(), - self.delta_y(), - self.delta_z(), - ) - } - - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl HasMouseData for WheelEvent { - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl InteractionLocation for WheelEvent { - fn client_coordinates(&self) -> ClientPoint { - ClientPoint::new(self.client_x().into(), self.client_y().into()) - } - - fn screen_coordinates(&self) -> ScreenPoint { - ScreenPoint::new(self.screen_x().into(), self.screen_y().into()) - } - - fn page_coordinates(&self) -> PagePoint { - PagePoint::new(self.page_x().into(), self.page_y().into()) - } -} - -impl InteractionElementOffset for WheelEvent { - fn element_coordinates(&self) -> ElementPoint { - ElementPoint::new(self.offset_x().into(), self.offset_y().into()) - } -} - -impl ModifiersInteraction for WheelEvent { - fn modifiers(&self) -> Modifiers { - let mut modifiers = Modifiers::empty(); - - if self.alt_key() { - modifiers.insert(Modifiers::ALT); - } - if self.ctrl_key() { - modifiers.insert(Modifiers::CONTROL); - } - if self.meta_key() { - modifiers.insert(Modifiers::META); - } - if self.shift_key() { - modifiers.insert(Modifiers::SHIFT); - } - - modifiers - } -} - -impl PointerInteraction for WheelEvent { - fn held_buttons(&self) -> crate::input_data::MouseButtonSet { - decode_mouse_button_set(self.buttons()) - } - - fn trigger_button(&self) -> Option { - Some(MouseButton::from_web_code(self.button())) - } -} - -impl HasAnimationData for AnimationEvent { - fn animation_name(&self) -> String { - self.animation_name() - } - - fn pseudo_element(&self) -> String { - self.pseudo_element() - } - - fn elapsed_time(&self) -> f32 { - self.elapsed_time() - } - - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl HasTransitionData for TransitionEvent { - fn elapsed_time(&self) -> f32 { - self.elapsed_time() - } - - fn property_name(&self) -> String { - self.property_name() - } - - fn pseudo_element(&self) -> String { - self.pseudo_element() - } - - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -#[cfg(feature = "mounted")] -impl From<&web_sys::Element> for MountedData { - fn from(e: &web_sys::Element) -> Self { - MountedData::new(e.clone()) - } -} - -#[cfg(feature = "mounted")] -impl crate::RenderedElementBacking for web_sys::Element { - fn get_scroll_offset( - &self, - ) -> std::pin::Pin< - Box< - dyn std::future::Future>, - >, - > { - let left = self.scroll_left(); - let top = self.scroll_top(); - let result = Ok(crate::geometry::PixelsVector2D::new( - left as f64, - top as f64, - )); - Box::pin(async { result }) - } - - fn get_scroll_size( - &self, - ) -> std::pin::Pin< - Box>>, - > { - let width = self.scroll_width(); - let height = self.scroll_height(); - let result = Ok(crate::geometry::PixelsSize::new( - width as f64, - height as f64, - )); - Box::pin(async { result }) - } - - fn get_client_rect( - &self, - ) -> std::pin::Pin< - Box>>, - > { - let rect = self.get_bounding_client_rect(); - let result = Ok(crate::geometry::PixelsRect::new( - euclid::Point2D::new(rect.left(), rect.top()), - euclid::Size2D::new(rect.width(), rect.height()), - )); - Box::pin(async { result }) - } - - fn as_any(&self) -> &dyn std::any::Any { - self - } - - fn scroll_to( - &self, - behavior: crate::ScrollBehavior, - ) -> std::pin::Pin>>> { - let options = web_sys::ScrollIntoViewOptions::new(); - match behavior { - crate::ScrollBehavior::Instant => { - options.set_behavior(web_sys::ScrollBehavior::Instant); - } - crate::ScrollBehavior::Smooth => { - options.set_behavior(web_sys::ScrollBehavior::Smooth); - } - } - self.scroll_into_view_with_scroll_into_view_options(&options); - - Box::pin(async { Ok(()) }) - } - - fn set_focus( - &self, - focus: bool, - ) -> std::pin::Pin>>> { - #[derive(Debug)] - struct FocusError(wasm_bindgen::JsValue); - - impl std::fmt::Display for FocusError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "failed to focus element {:?}", self.0) - } - } - - impl std::error::Error for FocusError {} - - let result = self - .dyn_ref::() - .ok_or_else(|| crate::MountedError::OperationFailed(Box::new(FocusError(self.into())))) - .and_then(|e| { - (if focus { e.focus() } else { e.blur() }) - .map_err(|err| crate::MountedError::OperationFailed(Box::new(FocusError(err)))) - }); - Box::pin(async { result }) - } -} - -fn extract_first_size(resize_observer_output: js_sys::Array) -> ResizeResult { - let first = resize_observer_output.get(0); - let size = first.unchecked_into::(); - - // inline_size matches the width of the element if its writing-mode is horizontal, the height otherwise - let inline_size = size.inline_size(); - // block_size matches the height of the element if its writing-mode is horizontal, the width otherwise - let block_size = size.block_size(); - - Ok(PixelsSize::new(inline_size, block_size)) -} - -impl HasResizeData for ResizeObserverEntry { - fn as_any(&self) -> &dyn std::any::Any { - self - } - - fn get_border_box_size(&self) -> ResizeResult { - extract_first_size(self.border_box_size()) - } - - fn get_content_box_size(&self) -> ResizeResult { - extract_first_size(self.content_box_size()) - } -} - -impl HasScrollData for Event { - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl HasClipboardData for Event { - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl From<&Event> for ClipboardData { - fn from(e: &Event) -> Self { - ClipboardData::new(e.clone()) - } -} - -impl HasFocusData for web_sys::FocusEvent { - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl HasToggleData for web_sys::Event { - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl HasSelectionData for web_sys::Event { - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl HasMediaData for web_sys::Event { - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl HasFileData for web_sys::Event { - #[cfg(feature = "file-engine")] - fn files(&self) -> Option> { - let files = self - .dyn_ref() - .and_then(|input: &web_sys::HtmlInputElement| { - input.files().and_then(|files| { - #[allow(clippy::arc_with_non_send_sync)] - crate::web_sys_bind::file_engine::WebFileEngine::new(files) - .map(|f| std::sync::Arc::new(f) as std::sync::Arc) - }) - }); - - files - } -} From 0da7ea52b5c538e23a9f2fad3833e58217f5ed73 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 22 Aug 2024 05:25:15 -0700 Subject: [PATCH 048/139] desktop mostly cleaned up too --- packages/desktop/src/assets.rs | 6 +++--- packages/desktop/src/bindings.rs | 1 - packages/desktop/src/desktop_context.rs | 6 +++--- packages/desktop/src/lib.rs | 1 - packages/desktop/src/protocol.rs | 2 +- packages/desktop/src/webview.rs | 7 +++---- packages/web/src/event/drag.rs | 8 ++++---- packages/web/src/event/form.rs | 4 +--- packages/web/src/event/resize.rs | 1 + 9 files changed, 16 insertions(+), 20 deletions(-) delete mode 100644 packages/desktop/src/bindings.rs diff --git a/packages/desktop/src/assets.rs b/packages/desktop/src/assets.rs index 9e585d1e7e..3297cf379f 100644 --- a/packages/desktop/src/assets.rs +++ b/packages/desktop/src/assets.rs @@ -11,13 +11,13 @@ pub struct AssetHandler { } #[derive(Clone)] -pub struct AssetHandlerRegistry { +pub struct AssetHandlers { handlers: Rc>>, } -impl AssetHandlerRegistry { +impl AssetHandlers { pub fn new() -> Self { - AssetHandlerRegistry { + AssetHandlers { handlers: Default::default(), } } diff --git a/packages/desktop/src/bindings.rs b/packages/desktop/src/bindings.rs deleted file mode 100644 index 8b13789179..0000000000 --- a/packages/desktop/src/bindings.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/packages/desktop/src/desktop_context.rs b/packages/desktop/src/desktop_context.rs index 5cc7bd3f87..3b1c94df3a 100644 --- a/packages/desktop/src/desktop_context.rs +++ b/packages/desktop/src/desktop_context.rs @@ -1,6 +1,6 @@ use crate::{ app::SharedContext, - assets::AssetHandlerRegistry, + assets::AssetHandlers, file_upload::NativeFileHover, ipc::UserWindowEvent, shortcut::{HotKey, ShortcutHandle, ShortcutRegistryError}, @@ -53,7 +53,7 @@ pub struct DesktopService { pub(crate) shared: Rc, - pub(crate) asset_handlers: AssetHandlerRegistry, + pub(crate) asset_handlers: AssetHandlers, pub(crate) file_hover: NativeFileHover, #[cfg(target_os = "ios")] @@ -74,7 +74,7 @@ impl DesktopService { webview: WebView, window: Window, shared: Rc, - asset_handlers: AssetHandlerRegistry, + asset_handlers: AssetHandlers, file_hover: NativeFileHover, ) -> Self { Self { diff --git a/packages/desktop/src/lib.rs b/packages/desktop/src/lib.rs index e33d52e0a2..77eab0503a 100644 --- a/packages/desktop/src/lib.rs +++ b/packages/desktop/src/lib.rs @@ -6,7 +6,6 @@ mod app; mod assets; -mod bindings; mod config; mod desktop_context; mod document; diff --git a/packages/desktop/src/protocol.rs b/packages/desktop/src/protocol.rs index c935854589..55ac0dd165 100644 --- a/packages/desktop/src/protocol.rs +++ b/packages/desktop/src/protocol.rs @@ -28,7 +28,7 @@ static DEFAULT_INDEX: &str = include_str!("./index.html"); /// - If that doesn't match, tries to serve a file from the filesystem pub(super) fn desktop_handler( request: Request>, - asset_handlers: AssetHandlerRegistry, + asset_handlers: AssetHandlers, responder: RequestAsyncResponder, edit_state: &WebviewEdits, custom_head: Option, diff --git a/packages/desktop/src/webview.rs b/packages/desktop/src/webview.rs index 0d6077d1c2..234921e043 100644 --- a/packages/desktop/src/webview.rs +++ b/packages/desktop/src/webview.rs @@ -3,9 +3,8 @@ use crate::file_upload::DesktopFileDragEvent; use crate::file_upload::NativeFileEngine; use crate::menubar::DioxusMenu; use crate::{ - app::SharedContext, assets::AssetHandlerRegistry, edits::WryQueue, - file_upload::NativeFileHover, ipc::UserWindowEvent, protocol, waker::tao_waker, Config, - DesktopContext, DesktopService, + app::SharedContext, assets::AssetHandlers, edits::WryQueue, file_upload::NativeFileHover, + ipc::UserWindowEvent, protocol, waker::tao_waker, Config, DesktopContext, DesktopService, }; use dioxus_core::{Runtime, ScopeId, VirtualDom}; use dioxus_document::Document; @@ -188,7 +187,7 @@ impl WebviewInstance { let mut web_context = WebContext::new(cfg.data_dir.clone()); let edit_queue = WryQueue::default(); - let asset_handlers = AssetHandlerRegistry::new(); + let asset_handlers = AssetHandlers::new(); let edits = WebviewEdits::new(dom.runtime(), edit_queue.clone()); let file_hover = NativeFileHover::default(); let headless = !cfg.window.window.visible; diff --git a/packages/web/src/event/drag.rs b/packages/web/src/event/drag.rs index 008ff16c86..03f846bba6 100644 --- a/packages/web/src/event/drag.rs +++ b/packages/web/src/event/drag.rs @@ -1,11 +1,11 @@ - use dioxus_html::{ point_interaction::{ InteractionElementOffset, InteractionLocation, ModifiersInteraction, PointerInteraction, - }, HasDragData, HasFileData, HasMouseData, + }, + HasDragData, HasFileData, HasMouseData, }; -use wasm_bindgen::{JsCast}; -use web_sys::{MouseEvent}; +use wasm_bindgen::JsCast; +use web_sys::MouseEvent; use super::synthetic::Synthetic; diff --git a/packages/web/src/event/form.rs b/packages/web/src/event/form.rs index 93c0a4e988..c909e4cd43 100644 --- a/packages/web/src/event/form.rs +++ b/packages/web/src/event/form.rs @@ -1,8 +1,6 @@ use std::{any::Any, collections::HashMap}; -use dioxus_html::{ - FormValue, HasFileData, HasFormData, -}; +use dioxus_html::{FormValue, HasFileData, HasFormData}; use js_sys::Array; use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue}; use web_sys::{Element, Event}; diff --git a/packages/web/src/event/resize.rs b/packages/web/src/event/resize.rs index e69de29bb2..8b13789179 100644 --- a/packages/web/src/event/resize.rs +++ b/packages/web/src/event/resize.rs @@ -0,0 +1 @@ + From c0fe097555c24b9bb1f62937207019537da27cab Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 22 Aug 2024 05:27:43 -0700 Subject: [PATCH 049/139] clean up cli a bit --- Cargo.lock | 1 + packages/cli/Cargo.toml | 2 +- packages/cli/src/assets/mod.rs | 16 ---------------- packages/cli/src/cli/build.rs | 4 +++- packages/cli/src/serve/output.rs | 2 +- packages/cli/src/serve/server.rs | 2 +- packages/cli/src/serve/watcher.rs | 2 +- 7 files changed, 8 insertions(+), 21 deletions(-) delete mode 100644 packages/cli/src/assets/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 6629f5f44b..3a3986d00b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2394,6 +2394,7 @@ dependencies = [ "dioxus-check", "dioxus-core", "dioxus-core-types", + "dioxus-devtools-types", "dioxus-html", "dioxus-rsx", "dirs", diff --git a/packages/cli/Cargo.toml b/packages/cli/Cargo.toml index 1d690cd80e..1f373ca882 100644 --- a/packages/cli/Cargo.toml +++ b/packages/cli/Cargo.toml @@ -92,7 +92,7 @@ dioxus-rsx = { workspace = true, features = ["hot_reload"] } dioxus-html = { workspace = true, features = ["hot-reload-context"] } dioxus-core = { workspace = true, features = ["serialize"] } dioxus-core-types = { workspace = true } -# dioxus-hot-reload = { workspace = true, features = ["serve"] } +dioxus-devtools-types = { workspace = true } ignore = "0.4.22" env_logger = { workspace = true } diff --git a/packages/cli/src/assets/mod.rs b/packages/cli/src/assets/mod.rs deleted file mode 100644 index d67fdf9f3d..0000000000 --- a/packages/cli/src/assets/mod.rs +++ /dev/null @@ -1,16 +0,0 @@ -#![doc = include_str!("../README.md")] -#![deny(missing_docs)] - -#[allow(hidden_glob_reexports)] -mod file; -mod folder; -mod linker_intercept; -mod manifest; - -pub use file::process_file; -pub use folder::process_folder; -pub use linker_intercept::*; -pub use manganis_common::*; -pub use manifest::*; - -pub struct AssetManifest {} diff --git a/packages/cli/src/cli/build.rs b/packages/cli/src/cli/build.rs index 283867c8dc..4a4b6e5044 100644 --- a/packages/cli/src/cli/build.rs +++ b/packages/cli/src/cli/build.rs @@ -1,5 +1,7 @@ -use crate::config::Platform; +use std::str::FromStr; + use crate::{builder::BuildRequest, dioxus_crate::DioxusCrate}; +use crate::{builder::TargetPlatform, config::Platform}; use anyhow::Context; use super::*; diff --git a/packages/cli/src/serve/output.rs b/packages/cli/src/serve/output.rs index 8e84450265..bb2b908409 100644 --- a/packages/cli/src/serve/output.rs +++ b/packages/cli/src/serve/output.rs @@ -18,7 +18,7 @@ use crossterm::{ tty::IsTty, ExecutableCommand, }; -use dioxus_devtools::ClientMsg; +use dioxus_devtools_types::ClientMsg; use futures_util::{future::select_all, Future, FutureExt, StreamExt}; use ratatui::{prelude::*, widgets::*, TerminalOptions, Viewport}; use std::{ diff --git a/packages/cli/src/serve/server.rs b/packages/cli/src/serve/server.rs index 9bcb1487ca..7750e16185 100644 --- a/packages/cli/src/serve/server.rs +++ b/packages/cli/src/serve/server.rs @@ -19,7 +19,7 @@ use axum::{ Router, }; use axum_server::tls_rustls::RustlsConfig; -use dioxus_devtools::{DevserverMsg, HotReloadMsg}; +use dioxus_devtools_types::{DevserverMsg, HotReloadMsg}; use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender}; use futures_util::stream; use futures_util::{stream::FuturesUnordered, StreamExt}; diff --git a/packages/cli/src/serve/watcher.rs b/packages/cli/src/serve/watcher.rs index b684ca1939..cdd57fd3ef 100644 --- a/packages/cli/src/serve/watcher.rs +++ b/packages/cli/src/serve/watcher.rs @@ -4,7 +4,7 @@ use std::{fs, path::PathBuf, time::Duration}; use super::hot_reloading_file_map::HotreloadError; use crate::serve::hot_reloading_file_map::FileMap; use crate::{cli::serve::Serve, dioxus_crate::DioxusCrate}; -use dioxus_devtools::HotReloadMsg; +use dioxus_devtools_types::HotReloadMsg; use dioxus_html::HtmlCtx; use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender}; use futures_util::StreamExt; From 5261e24591e100a0a8707885d66e671800609e51 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 22 Aug 2024 05:53:31 -0700 Subject: [PATCH 050/139] more cli cleanup --- Cargo.lock | 93 +++---- Cargo.toml | 1 + packages/cli/Cargo.toml | 36 +-- packages/cli/src/assets.rs | 2 - packages/cli/src/assets/file.rs | 10 +- packages/cli/src/assets/manifest.rs | 327 +++++++++++------------ packages/cli/src/builder/cargo.rs | 11 +- packages/cli/src/builder/mod.rs | 46 ++-- packages/cli/src/builder/prepare_html.rs | 13 +- packages/cli/src/builder/web.rs | 3 +- packages/cli/src/cli/link.rs | 10 +- packages/fullstack/Cargo.toml | 4 +- packages/runtime-config/src/lib.rs | 1 + 13 files changed, 274 insertions(+), 283 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3a3986d00b..646e7a6ab0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -638,7 +638,7 @@ dependencies = [ "axum-core 0.4.3", "bytes", "futures-util", - "headers 0.4.0", + "headers", "http 1.1.0", "http-body 1.0.1", "http-body-util", @@ -1526,17 +1526,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" -[[package]] -name = "colored" -version = "1.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f741c91823341bebf717d4c71bda820630ce065443b58bd1b7451af008355" -dependencies = [ - "is-terminal", - "lazy_static", - "winapi", -] - [[package]] name = "colored" version = "2.1.0" @@ -2385,7 +2374,6 @@ dependencies = [ "cargo_toml", "chrono", "clap", - "colored 2.1.0", "console", "console-subscriber", "crossterm", @@ -2397,14 +2385,14 @@ dependencies = [ "dioxus-devtools-types", "dioxus-html", "dioxus-rsx", + "dioxus-runtime-config", "dirs", "env_logger", - "fern", "flate2", "fs_extra", "futures-channel", "futures-util", - "headers 0.3.9", + "headers", "html_parser", "hyper 1.4.1", "hyper-rustls", @@ -2413,6 +2401,7 @@ dependencies = [ "krates", "log", "notify", + "object", "once_cell", "open", "prettyplease", @@ -3484,16 +3473,6 @@ dependencies = [ "simd-adler32", ] -[[package]] -name = "fern" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9f0c14694cbd524c8720dd69b0e3179344f04ebb5f90f2e4a440c6ea3b2f1ee" -dependencies = [ - "colored 1.9.4", - "log", -] - [[package]] name = "ff" version = "0.13.0" @@ -4823,21 +4802,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "headers" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270" -dependencies = [ - "base64 0.21.7", - "bytes", - "headers-core 0.2.0", - "http 0.2.12", - "httpdate", - "mime", - "sha1", -] - [[package]] name = "headers" version = "0.4.0" @@ -4846,22 +4810,13 @@ checksum = "322106e6bd0cba2d5ead589ddb8150a13d7c4217cf80d7c4f682ca994ccc6aa9" dependencies = [ "base64 0.21.7", "bytes", - "headers-core 0.3.0", + "headers-core", "http 1.1.0", "httpdate", "mime", "sha1", ] -[[package]] -name = "headers-core" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" -dependencies = [ - "http 0.2.12", -] - [[package]] name = "headers-core" version = "0.3.0" @@ -6570,7 +6525,10 @@ version = "0.36.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9" dependencies = [ + "flate2", "memchr", + "ruzstd", + "wasmparser 0.215.0", ] [[package]] @@ -8157,6 +8115,16 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" +[[package]] +name = "ruzstd" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5022b253619b1ba797f243056276bed8ed1a73b0f5a7ce7225d524067644bf8f" +dependencies = [ + "byteorder", + "twox-hash", +] + [[package]] name = "ryu" version = "1.0.18" @@ -8637,7 +8605,7 @@ version = "4.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e7e46c8c90251d47d08b28b8a419ffb4aede0f87c2eea95e17d1d5bacbf3ef1" dependencies = [ - "colored 2.1.0", + "colored", "log", "time", "windows-sys 0.48.0", @@ -10017,6 +9985,16 @@ dependencies = [ "cipher", ] +[[package]] +name = "twox-hash" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" +dependencies = [ + "cfg-if", + "static_assertions", +] + [[package]] name = "typeid" version = "1.0.1" @@ -10312,7 +10290,7 @@ dependencies = [ "log", "walrus-macro", "wasm-encoder", - "wasmparser", + "wasmparser 0.212.0", ] [[package]] @@ -10533,7 +10511,7 @@ dependencies = [ "leb128", "log", "walrus", - "wasmparser", + "wasmparser 0.212.0", ] [[package]] @@ -10635,6 +10613,15 @@ dependencies = [ "serde", ] +[[package]] +name = "wasmparser" +version = "0.215.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fbde0881f24199b81cf49b6ff8f9c145ac8eb1b7fc439adb5c099734f7d90e" +dependencies = [ + "bitflags 2.6.0", +] + [[package]] name = "web-sys" version = "0.3.70" diff --git a/Cargo.toml b/Cargo.toml index 433d04b34a..38fed8c1ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -118,6 +118,7 @@ warnings = { version = "0.2.0" } # a fork of pretty please for tests prettier-please = { version = "0.3.0", features = ["verbatim"]} +clap = { version = "4.5.7" } askama_escape = "0.10.3" tracing = "0.1.37" tracing-futures = "0.2.5" diff --git a/packages/cli/Cargo.toml b/packages/cli/Cargo.toml index 1f373ca882..2df80a6d9f 100644 --- a/packages/cli/Cargo.toml +++ b/packages/cli/Cargo.toml @@ -11,17 +11,15 @@ rust-version = "1.79.0" [dependencies] # cli core -clap = { version = "4.2", features = ["derive", "cargo"] } +clap = { workspace = true, features = ["derive", "cargo"] } thiserror = { workspace = true } wasm-bindgen-cli-support = "0.2" wasm-bindgen-shared = "0.2" -colored = "2.0.0" # features log = "0.4.14" -fern = { version = "0.6.0", features = ["colored"] } -serde = { version = "1.0.136", features = ["derive"] } -serde_json = "1.0.79" +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true } toml = { workspace = true } fs_extra = "1.2.0" cargo_toml = { workspace = true } @@ -29,7 +27,7 @@ futures-util = { workspace = true, features = ["async-await-macro"] } notify = { workspace = true, features = ["serde"] } html_parser = { workspace = true } cargo_metadata = "0.18.1" -tokio = { version = "1.16.1", features = ["fs", "sync", "rt", "macros", "process", "rt-multi-thread"] } +tokio = { workspace = true, features = ["fs", "sync", "rt", "macros", "process", "rt-multi-thread"] } tokio-stream = "0.1.15" atty = "0.2.14" chrono = { workspace = true } @@ -53,7 +51,7 @@ tower-http = { workspace = true, features = ["full"] } proc-macro2 = { workspace = true, features = ["span-locations"] } syn = { workspace = true, features = ["full", "extra-traits", "visit", "visit-mut"] } -headers = "0.3.7" +headers = "0.4.0" walkdir = "2" # tools download @@ -93,6 +91,7 @@ dioxus-html = { workspace = true, features = ["hot-reload-context"] } dioxus-core = { workspace = true, features = ["serialize"] } dioxus-core-types = { workspace = true } dioxus-devtools-types = { workspace = true } +dioxus-runtime-config = { workspace = true } ignore = "0.4.22" env_logger = { workspace = true } @@ -137,13 +136,9 @@ ansi-to-html = "0.2.1" # # swc = "=0.283.0" # # swc_common = "=0.37.1" -# # Remote assets -# url = { version = "2.4.0", features = ["serde"] } -# reqwest = { version = "0.12.5", features = ["blocking"] } -# tracing = "0.1.37" -# # Extracting data from an executable -# object = {version="0.36.0", features=["wasm"]} +# Extracting data from an executable +object = {version="0.36.0", features=["wasm"]} # [dev-dependencies] # tracing-subscriber = "0.3.18" @@ -159,11 +154,6 @@ ansi-to-html = "0.2.1" # webp = [] # avif = [] - -# on macos, we need to specify the vendored feature on ssl when cross compiling -# [target.'cfg(target_os = "macos")'.dependencies] -# openssl = { version = "0.10", features = ["vendored"] } - [build-dependencies] built = { version = "=0.7.3", features = ["git2"] } @@ -173,6 +163,10 @@ plugin = [] tokio-console = ["dep:console-subscriber"] # when releasing dioxus, we want to enable wasm-opt +# and then also maybe developing it too. +# making this optional cuts workspace deps down from 1000 to 500, so it's very nice for workspace adev +optimization = ["wasm-opt", "asset-opt"] +asset-opt = [] wasm-opt = ["dep:wasm-opt"] [[bin]] @@ -183,13 +177,7 @@ name = "dx" tempfile = "3.3" [package.metadata.binstall] -# temporarily, we're going to use the 0.5.0 download page for all binaries pkg-url = "{ repo }/releases/download/v{ version }/dx-{ target }-v{ version }{ archive-suffix }" - -# the old one... -# pkg-url = "{ repo }/releases/download/v0.5.0/dx-{ target }-v{ version }{ archive-suffix }" - -# pkg-url = "{ repo }/releases/download/v{ version }/dx-{ target }{ archive-suffix }" pkg-fmt = "tgz" [package.metadata.binstall.overrides.x86_64-pc-windows-msvc] diff --git a/packages/cli/src/assets.rs b/packages/cli/src/assets.rs index 2cb4489162..ecab8e588b 100644 --- a/packages/cli/src/assets.rs +++ b/packages/cli/src/assets.rs @@ -26,8 +26,6 @@ pub use folder::process_folder; pub use linker_intercept::*; pub use manifest::*; -pub struct AssetManifest {} - /// The temp file name for passing manganis json from linker to current exec. pub const MG_JSON_OUT: &str = "mg-out"; diff --git a/packages/cli/src/assets/file.rs b/packages/cli/src/assets/file.rs index 1f21eaca37..678cdbeed2 100644 --- a/packages/cli/src/assets/file.rs +++ b/packages/cli/src/assets/file.rs @@ -1,9 +1,9 @@ use anyhow::Context; -use image::{DynamicImage, EncodableLayout}; -use lightningcss::stylesheet::{MinifyOptions, ParserOptions, PrinterOptions, StyleSheet}; -use manganis_common::{ - CssOptions, FileOptions, ImageOptions, ImageType, JsOptions, JsonOptions, ResourceAsset, -}; +// use image::{DynamicImage, EncodableLayout}; +// use lightningcss::stylesheet::{MinifyOptions, ParserOptions, PrinterOptions, StyleSheet}; +// use manganis_common::{ +// CssOptions, FileOptions, ImageOptions, ImageType, JsOptions, JsonOptions, ResourceAsset, +// }; use std::{ io::{BufWriter, Write}, path::Path, diff --git a/packages/cli/src/assets/manifest.rs b/packages/cli/src/assets/manifest.rs index e0da1fc320..912d94d3ea 100644 --- a/packages/cli/src/assets/manifest.rs +++ b/packages/cli/src/assets/manifest.rs @@ -1,7 +1,7 @@ -pub use railwind::warning::Warning as TailwindWarning; +// pub use railwind::warning::Warning as TailwindWarning; use std::path::PathBuf; -use manganis_common::{linker, AssetType}; +// use manganis_common::{linker, AssetType}; use crate::{file::process_file, process_folder}; @@ -33,168 +33,167 @@ fn get_string_manganis(file: &File) -> Option { None } -/// A manifest of all assets collected from dependencies -#[derive(Debug, PartialEq, Default, Clone)] -pub struct AssetManifest { - pub(crate) assets: Vec, -} - -impl AssetManifest { - /// Creates a new asset manifest - pub fn new(assets: Vec) -> Self { - Self { assets } - } - - /// Returns all assets collected from dependencies - pub fn assets(&self) -> &Vec { - &self.assets - } - - #[cfg(feature = "html")] - /// Returns the HTML that should be injected into the head of the page - pub fn head(&self) -> String { - let mut head = String::new(); - for asset in &self.assets { - if let crate::AssetType::Resource(file) = asset { - match file.options() { - crate::FileOptions::Css(css_options) => { - if css_options.preload() { - if let Ok(asset_path) = file.served_location() { - head.push_str(&format!( - "\n" - )) - } - } - } - crate::FileOptions::Image(image_options) => { - if image_options.preload() { - if let Ok(asset_path) = file.served_location() { - head.push_str(&format!( - "\n" - )) - } - } - } - crate::FileOptions::Js(js_options) => { - if js_options.preload() { - if let Ok(asset_path) = file.served_location() { - head.push_str(&format!( - "\n" - )) - } - } - } - _ => {} - } - } - } - head - } -} - -/// An extension trait CLI support for the asset manifest -pub trait AssetManifestExt { - /// Load a manifest from a list of Manganis JSON strings. - /// - /// The asset descriptions are stored inside a manifest file that is produced when the linker is intercepted. - fn load(json: Vec) -> Self; - /// Load a manifest from the assets propogated through object files. - /// - /// The asset descriptions are stored inside a manifest file that is produced when the linker is intercepted. - fn load_from_objects(object_paths: Vec) -> Self; - /// Optimize and copy all assets in the manifest to a folder - fn copy_static_assets_to(&self, location: impl Into) -> anyhow::Result<()>; - /// Collect all tailwind classes and generate string with the output css - fn collect_tailwind_css( - &self, - include_preflight: bool, - warnings: &mut Vec, - ) -> String; -} - -impl AssetManifestExt for AssetManifest { - fn load(json: Vec) -> Self { - let mut all_assets = Vec::new(); - - // Collect all assets for each manganis string found. - for item in json { - let mut assets = deserialize_assets(item.as_str()); - all_assets.append(&mut assets); - } - - // If we don't see any manganis assets used in the binary, just return an empty manifest - if all_assets.is_empty() { - return Self::default(); - }; - - Self::new(all_assets) - } - - fn load_from_objects(object_files: Vec) -> Self { - let json = get_json_from_object_files(object_files); - Self::load(json) - } - - fn copy_static_assets_to(&self, location: impl Into) -> anyhow::Result<()> { - let location = location.into(); - match std::fs::create_dir_all(&location) { - Ok(_) => {} - Err(err) => { - tracing::error!("Failed to create directory for static assets: {}", err); - return Err(err.into()); - } - } - - self.assets().iter().try_for_each(|asset| { - match asset { - AssetType::Resource(file_asset) => { - tracing::info!("Optimizing and bundling {:?}", file_asset); - tracing::trace!("Copying asset from {:?} to {:?}", file_asset, location); - match process_file(file_asset, &location) { - Ok(_) => {} - Err(err) => { - tracing::error!("Failed to copy static asset: {}", err); - return Err(err); - } - } - - // tracing::info!("Copying folder asset {}", folder_asset); - // match process_folder(folder_asset, &location) { - // Ok(_) => {} - // Err(err) => { - // tracing::error!("Failed to copy static asset: {}", err); - // return Err(err); - // } - // } - } - - _ => {} - } - Ok::<(), anyhow::Error>(()) - }) - } - - fn collect_tailwind_css( - self: &AssetManifest, - include_preflight: bool, - warnings: &mut Vec, - ) -> String { - let mut all_classes = String::new(); - - for asset in self.assets() { - if let AssetType::Tailwind(classes) = asset { - all_classes.push_str(classes.classes()); - all_classes.push(' '); - } - } - - let source = railwind::Source::String(all_classes, railwind::CollectionOptions::String); - - let css = railwind::parse_to_string(source, include_preflight, warnings); - - crate::file::minify_css(&css) - } -} +// /// A manifest of all assets collected from dependencies +// #[derive(Debug, PartialEq, Default, Clone)] +// pub struct AssetManifest { +// pub(crate) assets: Vec, +// } + +// impl AssetManifest { +// /// Creates a new asset manifest +// pub fn new(assets: Vec) -> Self { +// Self { assets } +// } + +// /// Returns all assets collected from dependencies +// pub fn assets(&self) -> &Vec { +// &self.assets +// } + +// /// Returns the HTML that should be injected into the head of the page +// pub fn head(&self) -> String { +// let mut head = String::new(); +// for asset in &self.assets { +// if let crate::AssetType::Resource(file) = asset { +// match file.options() { +// crate::FileOptions::Css(css_options) => { +// if css_options.preload() { +// if let Ok(asset_path) = file.served_location() { +// head.push_str(&format!( +// "\n" +// )) +// } +// } +// } +// crate::FileOptions::Image(image_options) => { +// if image_options.preload() { +// if let Ok(asset_path) = file.served_location() { +// head.push_str(&format!( +// "\n" +// )) +// } +// } +// } +// crate::FileOptions::Js(js_options) => { +// if js_options.preload() { +// if let Ok(asset_path) = file.served_location() { +// head.push_str(&format!( +// "\n" +// )) +// } +// } +// } +// _ => {} +// } +// } +// } +// head +// } +// } + +// /// An extension trait CLI support for the asset manifest +// pub trait AssetManifestExt { +// /// Load a manifest from a list of Manganis JSON strings. +// /// +// /// The asset descriptions are stored inside a manifest file that is produced when the linker is intercepted. +// fn load(json: Vec) -> Self; +// /// Load a manifest from the assets propogated through object files. +// /// +// /// The asset descriptions are stored inside a manifest file that is produced when the linker is intercepted. +// fn load_from_objects(object_paths: Vec) -> Self; +// /// Optimize and copy all assets in the manifest to a folder +// fn copy_static_assets_to(&self, location: impl Into) -> anyhow::Result<()>; +// /// Collect all tailwind classes and generate string with the output css +// fn collect_tailwind_css( +// &self, +// include_preflight: bool, +// warnings: &mut Vec, +// ) -> String; +// } + +// impl AssetManifestExt for AssetManifest { +// fn load(json: Vec) -> Self { +// let mut all_assets = Vec::new(); + +// // Collect all assets for each manganis string found. +// for item in json { +// let mut assets = deserialize_assets(item.as_str()); +// all_assets.append(&mut assets); +// } + +// // If we don't see any manganis assets used in the binary, just return an empty manifest +// if all_assets.is_empty() { +// return Self::default(); +// }; + +// Self::new(all_assets) +// } + +// fn load_from_objects(object_files: Vec) -> Self { +// let json = get_json_from_object_files(object_files); +// Self::load(json) +// } + +// fn copy_static_assets_to(&self, location: impl Into) -> anyhow::Result<()> { +// let location = location.into(); +// match std::fs::create_dir_all(&location) { +// Ok(_) => {} +// Err(err) => { +// tracing::error!("Failed to create directory for static assets: {}", err); +// return Err(err.into()); +// } +// } + +// self.assets().iter().try_for_each(|asset| { +// match asset { +// AssetType::Resource(file_asset) => { +// tracing::info!("Optimizing and bundling {:?}", file_asset); +// tracing::trace!("Copying asset from {:?} to {:?}", file_asset, location); +// match process_file(file_asset, &location) { +// Ok(_) => {} +// Err(err) => { +// tracing::error!("Failed to copy static asset: {}", err); +// return Err(err); +// } +// } + +// // tracing::info!("Copying folder asset {}", folder_asset); +// // match process_folder(folder_asset, &location) { +// // Ok(_) => {} +// // Err(err) => { +// // tracing::error!("Failed to copy static asset: {}", err); +// // return Err(err); +// // } +// // } +// } + +// _ => {} +// } +// Ok::<(), anyhow::Error>(()) +// }) +// } + +// // fn collect_tailwind_css( +// // self: &AssetManifest, +// // include_preflight: bool, +// // warnings: &mut Vec, +// // ) -> String { +// // let mut all_classes = String::new(); + +// // for asset in self.assets() { +// // if let AssetType::Tailwind(classes) = asset { +// // all_classes.push_str(classes.classes()); +// // all_classes.push(' '); +// // } +// // } + +// // let source = railwind::Source::String(all_classes, railwind::CollectionOptions::String); + +// // let css = railwind::parse_to_string(source, include_preflight, warnings); + +// // crate::file::minify_css(&css) +// // } +// } fn deserialize_assets(json: &str) -> Vec { let deserializer = serde_json::Deserializer::from_str(json); diff --git a/packages/cli/src/builder/cargo.rs b/packages/cli/src/builder/cargo.rs index 0d1be59c43..72844fc168 100644 --- a/packages/cli/src/builder/cargo.rs +++ b/packages/cli/src/builder/cargo.rs @@ -2,9 +2,9 @@ use super::web::install_web_build_tooling; use super::BuildRequest; use super::BuildResult; use super::TargetPlatform; -use crate::assets::copy_dir_to; use crate::assets::create_assets_head; use crate::assets::{asset_manifest, process_assets}; +use crate::assets::{copy_dir_to, AssetManifest}; use crate::builder::progress::build_cargo; use crate::builder::progress::CargoBuildResult; use crate::builder::progress::Stage; @@ -109,10 +109,6 @@ impl BuildRequest { dioxus_version.push_str(&format!("-{hash}")); } - // todo: put the base path here? - // let _asset_guard = - // AssetConfigDropGuard::new(self.dioxus_crate.dioxus_config.web.app.base_path.as_deref()); - // If this is a web, build make sure we have the web build tooling set up if self.targeting_web() { install_web_build_tooling(&mut progress).await?; @@ -164,12 +160,17 @@ impl BuildRequest { if !out_dir.is_dir() { create_dir_all(&out_dir)?; } + let mut output_path = out_dir.join(file_name); + + // todo: this should not be platform cfged but rather be a target config + // we dont always want to set the .exe extension... if self.targeting_web() { output_path.set_extension("wasm"); } else if cfg!(windows) { output_path.set_extension("exe"); } + if let Some(res_path) = &cargo_build_result.output_location { std::fs::copy(res_path, &output_path)?; } diff --git a/packages/cli/src/builder/mod.rs b/packages/cli/src/builder/mod.rs index 164d4ab1bc..ece0b0a537 100644 --- a/packages/cli/src/builder/mod.rs +++ b/packages/cli/src/builder/mod.rs @@ -59,14 +59,19 @@ impl std::fmt::Display for TargetPlatform { pub struct BuildRequest { /// Whether the build is for serving the application pub serve: bool, + /// The configuration for the crate we are building pub dioxus_crate: DioxusCrate, + /// The target platform for the build pub target_platform: TargetPlatform, + /// The arguments for the build pub build_arguments: Build, + /// The rustc flags to pass to the build pub rust_flags: Vec, + /// The target directory for the build pub target_dir: Option, } @@ -174,21 +179,32 @@ impl BuildResult { tracing::trace!("Proxying fullstack server from port {fullstack_address:?}"); } - todo!("set runtime env vars for the fullstack server") // let arguments = RuntimeCLIArguments::new(serve.address.address(), fullstack_address); - // let executable = self.executable.canonicalize()?; - // let mut cmd = Command::new(executable); - // cmd - // // When building the fullstack server, we need to forward the serve arguments (like port) to the fullstack server through env vars - // // .env( - // // dioxus_cli_config::__private::SERVE_ENV, - // // serde_json::to_string(&arguments).unwrap(), - // // ) - // .env("CARGO_MANIFEST_DIR", config.crate_dir()) - // .stderr(Stdio::piped()) - // .stdout(Stdio::piped()) - // .kill_on_drop(true) - // .current_dir(workspace); - // Ok(Some(cmd.spawn()?)) + let executable = self.executable.canonicalize()?; + let mut cmd = Command::new(executable); + + let mut _cmd = cmd + .env( + dioxus_runtime_config::FULLSTACK_ADDRESS_ENV, + fullstack_address + .as_ref() + .map(|addr| addr.to_string()) + .unwrap_or_else(|| "127.0.0.1:8080".to_string()), + ) + .env( + dioxus_runtime_config::DEVSERVER_ADDR_ENV, + serve.address.address().to_string(), + ) + .env( + dioxus_runtime_config::MOBILE_DEVSERVER_ADDR_ENV, + serve.address.address().to_string(), + ) + .env("CARGO_MANIFEST_DIR", config.crate_dir()) + .stderr(Stdio::piped()) + .stdout(Stdio::piped()) + .kill_on_drop(true) + .current_dir(workspace); + + Ok(Some(_cmd.spawn()?)) } } diff --git a/packages/cli/src/builder/prepare_html.rs b/packages/cli/src/builder/prepare_html.rs index b2277dfe30..c338a62995 100644 --- a/packages/cli/src/builder/prepare_html.rs +++ b/packages/cli/src/builder/prepare_html.rs @@ -1,11 +1,11 @@ //! Build the HTML file to load a web application. The index.html file may be created from scratch or modified from the `index.html` file in the crate root. use super::{BuildRequest, UpdateBuildProgress}; -use crate::builder::progress::MessageSource; use crate::builder::Stage; use crate::Result; +use crate::{assets::AssetManifest, builder::progress::MessageSource}; use futures_channel::mpsc::UnboundedSender; -use manganis_cli_support::AssetManifest; + use std::fmt::Write; use std::path::{Path, PathBuf}; use tracing::Level; @@ -167,12 +167,11 @@ impl BuildRequest { } }; match variant { - ResourceType::Style => format!( - " document::Link {{ rel: \"stylesheet\", href: asset!(css(\"{}\")) }}", - path.display() - ), + ResourceType::Style => { + format!(" Stylesheet {{ href: asset!(\"{}\") }}", path.display()) + } ResourceType::Script => { - format!(" Script {{ src: asset!(file(\"{}\")) }}", path.display()) + format!(" Script {{ src: asset!(\"{}\") }}", path.display()) } } }) diff --git a/packages/cli/src/builder/web.rs b/packages/cli/src/builder/web.rs index 0830762b52..7318058c09 100644 --- a/packages/cli/src/builder/web.rs +++ b/packages/cli/src/builder/web.rs @@ -1,12 +1,11 @@ use super::BuildRequest; use super::BuildResult; -use crate::assets::pre_compress_folder; +use crate::assets::{pre_compress_folder, AssetManifest}; use crate::builder::progress::Stage; use crate::builder::progress::UpdateBuildProgress; use crate::builder::progress::UpdateStage; use crate::error::{Error, Result}; use futures_channel::mpsc::UnboundedSender; -use manganis_cli_support::AssetManifest; use std::path::Path; use tokio::process::Command; use wasm_bindgen_cli_support::Bindgen; diff --git a/packages/cli/src/cli/link.rs b/packages/cli/src/cli/link.rs index 730c5639e4..fe3a2ac27d 100644 --- a/packages/cli/src/cli/link.rs +++ b/packages/cli/src/cli/link.rs @@ -1,4 +1,7 @@ -use crate::{assets, error::Result}; +use crate::{ + assets::{self, get_json_from_object_files, linker_intercept}, + error::Result, +}; use clap::Parser; use std::{fs, path::PathBuf}; @@ -12,14 +15,13 @@ pub struct LinkCommand { impl LinkCommand { pub fn link(self) -> Result<()> { - let Some((link_args, object_files)) = manganis_cli_support::linker_intercept(self.args) - else { + let Some((link_args, object_files)) = linker_intercept(self.args) else { tracing::warn!("Invalid linker arguments."); return Ok(()); }; // Parse object files, deserialize JSON, & create a file to propagate JSON. - let json = manganis_cli_support::get_json_from_object_files(object_files); + let json = get_json_from_object_files(object_files); let parsed = serde_json::to_string(&json).unwrap(); let out_dir = PathBuf::from(link_args.first().unwrap()); diff --git a/packages/fullstack/Cargo.toml b/packages/fullstack/Cargo.toml index 44d663a923..8c67f6d9d9 100644 --- a/packages/fullstack/Cargo.toml +++ b/packages/fullstack/Cargo.toml @@ -44,7 +44,7 @@ once_cell = { workspace = true } tokio-util = { version = "0.7.8", features = ["rt"], optional = true } async-trait = { version = "0.1.58", optional = true } -serde = "1.0.159" +serde = { workspace = true } tokio-stream = { version = "0.1.12", features = ["sync"], optional = true } futures-util = { workspace = true } futures-channel = { workspace = true } @@ -62,7 +62,7 @@ tower = { workspace = true, features = ["util"], optional = true } tower-layer = { version = "0.3.2", optional = true } parking_lot = { version = "0.12.1", features = ["send_guard"], optional = true } web-sys = { version = "0.3.61", optional = true, features = ["Window", "Document", "Element", "HtmlDocument", "Storage", "console"] } -clap = { version = "4.5.7", optional = true, features = ["derive"] } +clap = { workspace = true, optional = true, features = ["derive"] } aws-lc-rs = { version = "1.8.1", optional = true } dioxus-devtools = { workspace = true, optional = true } diff --git a/packages/runtime-config/src/lib.rs b/packages/runtime-config/src/lib.rs index 3ae2c51811..0b585406ec 100644 --- a/packages/runtime-config/src/lib.rs +++ b/packages/runtime-config/src/lib.rs @@ -1,6 +1,7 @@ use std::net::SocketAddr; pub const DEVSERVER_ADDR_ENV: &str = "DIOXUS_DEVSERVER_ADDR"; +pub const MOBILE_DEVSERVER_ADDR_ENV: &str = "DIOXUS_MOBILE_DEVSERVER_ADDR"; /// Get the address of the devserver pub fn devserver_addr() -> Option { From 12fa005d118f03a23636766596a938a529b51837 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 22 Aug 2024 06:38:48 -0700 Subject: [PATCH 051/139] cli builds again --- Cargo.lock | 5 + Cargo.toml | 2 + packages/cli/Cargo.toml | 2 +- packages/cli/src/assets.rs | 146 +++--- packages/cli/src/assets/file.rs | 528 ++++++++++---------- packages/cli/src/assets/folder.rs | 110 ++-- packages/cli/src/assets/linker_intercept.rs | 1 + packages/cli/src/assets/manifest.rs | 140 +++--- packages/cli/src/builder/assets.rs | 82 +++ packages/cli/src/builder/cargo.rs | 93 +--- packages/cli/src/builder/fullstack.rs | 111 ++-- packages/cli/src/builder/mod.rs | 149 +++--- packages/cli/src/builder/platform.rs | 37 ++ packages/cli/src/builder/prepare_html.rs | 28 +- packages/cli/src/builder/web.rs | 120 ++--- packages/cli/src/cli/build.rs | 79 +-- packages/cli/src/serve/builder.rs | 20 +- packages/cli/src/serve/mod.rs | 2 + packages/cli/src/serve/output.rs | 14 +- packages/cli/src/serve/watcher.rs | 4 +- packages/manganis-core/Cargo.toml | 6 + packages/manganis-core/src/lib.rs | 69 +++ packages/manganis/src/common/linker.rs | 70 --- packages/runtime-config/src/lib.rs | 7 +- 24 files changed, 951 insertions(+), 874 deletions(-) create mode 100644 packages/cli/src/builder/assets.rs create mode 100644 packages/cli/src/builder/platform.rs create mode 100644 packages/manganis-core/Cargo.toml create mode 100644 packages/manganis-core/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 646e7a6ab0..17687c4fb9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2400,6 +2400,7 @@ dependencies = [ "ignore", "krates", "log", + "manganis-core", "notify", "object", "once_cell", @@ -5905,6 +5906,10 @@ dependencies = [ "serde", ] +[[package]] +name = "manganis-core" +version = "0.6.0-alpha.2" + [[package]] name = "manganis-macro" version = "0.6.0-alpha.2" diff --git a/Cargo.toml b/Cargo.toml index 38fed8c1ef..2278c775e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -69,6 +69,7 @@ members = [ "packages/devtools", "packages/devtools-types", "packages/isrg", + "packages/manganis-core", ] [workspace.package] @@ -111,6 +112,7 @@ dioxus-runtime-config = { path = "packages/runtime-config", version = "0.6.0-alp manganis = { path = "packages/manganis", version = "0.6.0-alpha.2" } manganis-macro = { path = "packages/manganis-macro", version = "0.6.0-alpha.2" } +manganis-core = { path = "packages/manganis-core", version = "0.6.0-alpha.2" } warnings = { version = "0.2.0" } diff --git a/packages/cli/Cargo.toml b/packages/cli/Cargo.toml index 2df80a6d9f..13bbe04b14 100644 --- a/packages/cli/Cargo.toml +++ b/packages/cli/Cargo.toml @@ -103,7 +103,7 @@ ratatui = { version = "0.27.0", features = ["crossterm", "unstable"] } crossterm = { version = "0.27.0", features = ["event-stream"] } ansi-to-tui = "=5.0.0-rc.1" ansi-to-html = "0.2.1" - +manganis-core = { workspace = true } diff --git a/packages/cli/src/assets.rs b/packages/cli/src/assets.rs index ecab8e588b..876196f50d 100644 --- a/packages/cli/src/assets.rs +++ b/packages/cli/src/assets.rs @@ -21,85 +21,85 @@ mod folder; mod linker_intercept; mod manifest; -pub use file::process_file; -pub use folder::process_folder; +// pub use file::process_file; +// pub use folder::process_folder; pub use linker_intercept::*; pub use manifest::*; /// The temp file name for passing manganis json from linker to current exec. pub const MG_JSON_OUT: &str = "mg-out"; -pub fn asset_manifest(build: &BuildRequest) -> AssetManifest { - let file_path = build.target_out_dir().join(MG_JSON_OUT); - let read = fs::read_to_string(&file_path).unwrap_or_default(); - _ = fs::remove_file(file_path); - let json: Vec = serde_json::from_str(&read).unwrap_or_default(); - - AssetManifest::load(json) -} - -/// Create a head file that contains all of the imports for assets that the user project uses -pub fn create_assets_head(build: &BuildRequest, manifest: &AssetManifest) -> Result<()> { - let out_dir = build.target_out_dir(); - std::fs::create_dir_all(&out_dir)?; - let mut file = File::create(out_dir.join("__assets_head.html"))?; - file.write_all(manifest.head().as_bytes())?; - Ok(()) -} - -/// Process any assets collected from the binary -pub(crate) fn process_assets( - build: &BuildRequest, - manifest: &AssetManifest, - progress: &mut UnboundedSender, -) -> anyhow::Result<()> { - let static_asset_output_dir = build.target_out_dir(); - - std::fs::create_dir_all(&static_asset_output_dir) - .context("Failed to create static asset output directory")?; - - let assets_finished = Arc::new(AtomicUsize::new(0)); - let assets = manifest.assets(); - let asset_count = assets.len(); - assets.par_iter().try_for_each_init( - || progress.clone(), - move |progress, asset| { - // if let AssetType::File(file_asset) = asset { - // match process_file(file_asset, &static_asset_output_dir) { - // Ok(_) => { - // // Update the progress - // _ = progress.start_send(UpdateBuildProgress { - // stage: Stage::OptimizingAssets, - // update: UpdateStage::AddMessage(BuildMessage { - // level: Level::INFO, - // message: MessageType::Text(format!( - // "Optimized static asset {}", - // file_asset - // )), - // source: MessageSource::Build, - // }), - // }); - // let assets_finished = - // assets_finished.fetch_add(1, std::sync::atomic::Ordering::SeqCst); - // _ = progress.start_send(UpdateBuildProgress { - // stage: Stage::OptimizingAssets, - // update: UpdateStage::SetProgress( - // assets_finished as f64 / asset_count as f64, - // ), - // }); - // } - // Err(err) => { - // tracing::error!("Failed to copy static asset: {}", err); - // return Err(err); - // } - // } - // } - Ok::<(), anyhow::Error>(()) - }, - )?; - - Ok(()) -} +// pub fn asset_manifest(build: &BuildRequest) -> AssetManifest { +// let file_path = build.target_out_dir().join(MG_JSON_OUT); +// let read = fs::read_to_string(&file_path).unwrap_or_default(); +// _ = fs::remove_file(file_path); +// let json: Vec = serde_json::from_str(&read).unwrap_or_default(); + +// AssetManifest::load(json) +// } + +// /// Create a head file that contains all of the imports for assets that the user project uses +// pub fn create_assets_head(build: &BuildRequest, manifest: &AssetManifest) -> Result<()> { +// let out_dir = build.target_out_dir(); +// std::fs::create_dir_all(&out_dir)?; +// let mut file = File::create(out_dir.join("__assets_head.html"))?; +// file.write_all(manifest.head().as_bytes())?; +// Ok(()) +// } + +// /// Process any assets collected from the binary +// pub(crate) fn process_assets( +// build: &BuildRequest, +// manifest: &AssetManifest, +// progress: &mut UnboundedSender, +// ) -> anyhow::Result<()> { +// let static_asset_output_dir = build.target_out_dir(); + +// std::fs::create_dir_all(&static_asset_output_dir) +// .context("Failed to create static asset output directory")?; + +// let assets_finished = Arc::new(AtomicUsize::new(0)); +// let assets = manifest.assets(); +// let asset_count = assets.len(); +// assets.par_iter().try_for_each_init( +// || progress.clone(), +// move |progress, asset| { +// // if let AssetType::File(file_asset) = asset { +// // match process_file(file_asset, &static_asset_output_dir) { +// // Ok(_) => { +// // // Update the progress +// // _ = progress.start_send(UpdateBuildProgress { +// // stage: Stage::OptimizingAssets, +// // update: UpdateStage::AddMessage(BuildMessage { +// // level: Level::INFO, +// // message: MessageType::Text(format!( +// // "Optimized static asset {}", +// // file_asset +// // )), +// // source: MessageSource::Build, +// // }), +// // }); +// // let assets_finished = +// // assets_finished.fetch_add(1, std::sync::atomic::Ordering::SeqCst); +// // _ = progress.start_send(UpdateBuildProgress { +// // stage: Stage::OptimizingAssets, +// // update: UpdateStage::SetProgress( +// // assets_finished as f64 / asset_count as f64, +// // ), +// // }); +// // } +// // Err(err) => { +// // tracing::error!("Failed to copy static asset: {}", err); +// // return Err(err); +// // } +// // } +// // } +// Ok::<(), anyhow::Error>(()) +// }, +// )?; + +// Ok(()) +// } pub(crate) fn copy_dir_to( src_dir: PathBuf, diff --git a/packages/cli/src/assets/file.rs b/packages/cli/src/assets/file.rs index 678cdbeed2..a453632161 100644 --- a/packages/cli/src/assets/file.rs +++ b/packages/cli/src/assets/file.rs @@ -13,267 +13,267 @@ use std::{ // use swc_common::{sync::Lrc, FileName}; // use swc_common::{SourceMap, GLOBALS}; -pub trait Process { - fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()>; -} - -/// Process a specific file asset -pub fn process_file(file: &ResourceAsset, output_folder: &Path) -> anyhow::Result<()> { - todo!() - // let location = file.location(); - // let source = location.source(); - // let output_path = output_folder.join(location.unique_name()); - // file.options().process(source, &output_path) -} - -impl Process for FileOptions { - fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { - if output_path.exists() { - return Ok(()); - } - match self { - Self::Other { .. } => { - let bytes = source.read_to_bytes()?; - std::fs::write(output_path, bytes).with_context(|| { - format!( - "Failed to write file to output location: {}", - output_path.display() - ) - })?; - } - Self::Css(options) => { - options.process(source, output_path)?; - } - Self::Js(options) => { - options.process(source, output_path)?; - } - Self::Json(options) => { - options.process(source, output_path)?; - } - Self::Image(options) => { - options.process(source, output_path)?; - } - _ => todo!(), - } - - Ok(()) - } -} - -impl Process for ImageOptions { - fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { - let mut image = image::ImageReader::new(std::io::Cursor::new(&*source.read_to_bytes()?)) - .with_guessed_format()? - .decode()?; - - if let Some(size) = self.size() { - image = image.resize_exact(size.0, size.1, image::imageops::FilterType::Lanczos3); - } - - match self.ty() { - ImageType::Png => { - compress_png(image, output_path); - } - ImageType::Jpg => { - compress_jpg(image, output_path)?; - } - ImageType::Avif => { - if let Err(error) = image.save(output_path) { - tracing::error!("Failed to save avif image: {} with path {}. You must have the avif feature enabled to use avif assets", error, output_path.display()); - } - } - ImageType::Webp => { - if let Err(err) = image.save(output_path) { - tracing::error!("Failed to save webp image: {}. You must have the avif feature enabled to use webp assets", err); - } - } - } - - Ok(()) - } -} - -fn compress_jpg(image: DynamicImage, output_location: &Path) -> anyhow::Result<()> { - let mut comp = mozjpeg::Compress::new(mozjpeg::ColorSpace::JCS_EXT_RGBX); - let width = image.width() as usize; - let height = image.height() as usize; - - comp.set_size(width, height); - let mut comp = comp.start_compress(Vec::new())?; // any io::Write will work - - comp.write_scanlines(image.to_rgba8().as_bytes())?; - - let jpeg_bytes = comp.finish()?; - - let file = std::fs::File::create(output_location)?; - let w = &mut BufWriter::new(file); - w.write_all(&jpeg_bytes)?; - Ok(()) -} - -fn compress_png(image: DynamicImage, output_location: &Path) { - // Image loading/saving is outside scope of this library - let width = image.width() as usize; - let height = image.height() as usize; - let bitmap: Vec<_> = image - .into_rgba8() - .pixels() - .map(|px| imagequant::RGBA::new(px[0], px[1], px[2], px[3])) - .collect(); - - // Configure the library - let mut liq = imagequant::new(); - liq.set_speed(5).unwrap(); - liq.set_quality(0, 99).unwrap(); - - // Describe the bitmap - let mut img = liq.new_image(&bitmap[..], width, height, 0.0).unwrap(); - - // The magic happens in quantize() - let mut res = match liq.quantize(&mut img) { - Ok(res) => res, - Err(err) => panic!("Quantization failed, because: {err:?}"), - }; - - let (palette, pixels) = res.remapped(&mut img).unwrap(); - - let file = std::fs::File::create(output_location).unwrap(); - let w = &mut BufWriter::new(file); - - let mut encoder = png::Encoder::new(w, width as u32, height as u32); - encoder.set_color(png::ColorType::Rgba); - let mut flattened_palette = Vec::new(); - let mut alpha_palette = Vec::new(); - for px in palette { - flattened_palette.push(px.r); - flattened_palette.push(px.g); - flattened_palette.push(px.b); - alpha_palette.push(px.a); - } - encoder.set_palette(flattened_palette); - encoder.set_trns(alpha_palette); - encoder.set_depth(png::BitDepth::Eight); - encoder.set_color(png::ColorType::Indexed); - encoder.set_compression(png::Compression::Best); - let mut writer = encoder.write_header().unwrap(); - writer.write_image_data(&pixels).unwrap(); - writer.finish().unwrap(); -} - -impl Process for CssOptions { - fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { - let css = source.read_to_string()?; - - let css = if self.minify() { minify_css(&css) } else { css }; - - std::fs::write(output_path, css).with_context(|| { - format!( - "Failed to write css to output location: {}", - output_path.display() - ) - })?; - - Ok(()) - } -} - -pub(crate) fn minify_css(css: &str) -> String { - let mut stylesheet = StyleSheet::parse(css, ParserOptions::default()).unwrap(); - stylesheet.minify(MinifyOptions::default()).unwrap(); - let printer = PrinterOptions { - minify: true, - ..Default::default() - }; - let res = stylesheet.to_css(printer).unwrap(); - res.code -} - -pub(crate) fn minify_js(source: &ResourceAsset) -> anyhow::Result { - todo!("disabled swc due to semver issues") - // let cm = Arc::::default(); - - // let js = source.read_to_string()?; - // let c = swc::Compiler::new(cm.clone()); - // let output = GLOBALS - // .set(&Default::default(), || { - // try_with_handler(cm.clone(), Default::default(), |handler| { - // // let filename = Lrc::new(match source { - // // manganis_common::ResourceAsset::Local(path) => { - // // FileName::Real(path.canonicalized.clone()) - // // } - // // manganis_common::ResourceAsset::Remote(url) => FileName::Url(url.clone()), - // // }); - // let filename = todo!(); - // let fm = cm.new_source_file(filename, js.to_string()); - - // c.minify( - // fm, - // handler, - // &JsMinifyOptions { - // compress: BoolOrDataConfig::from_bool(true), - // mangle: BoolOrDataConfig::from_bool(true), - // ..Default::default() - // }, - // ) - // .context("failed to minify javascript") - // }) - // }) - // .map(|output| output.code); - - // match output { - // Ok(output) => Ok(output), - // Err(err) => { - // tracing::error!("Failed to minify javascript: {}", err); - // Ok(js) - // } - // } -} - -impl Process for JsOptions { - fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { - let js = if self.minify() { - minify_js(source)? - } else { - source.read_to_string()? - }; - - std::fs::write(output_path, js).with_context(|| { - format!( - "Failed to write js to output location: {}", - output_path.display() - ) - })?; - - Ok(()) - } -} - -pub(crate) fn minify_json(source: &str) -> anyhow::Result { - // First try to parse the json - let json: serde_json::Value = serde_json::from_str(source)?; - // Then print it in a minified format - let json = serde_json::to_string(&json)?; - Ok(json) -} - -impl Process for JsonOptions { - fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { - let source = source.read_to_string()?; - let json = match minify_json(&source) { - Ok(json) => json, - Err(err) => { - tracing::error!("Failed to minify json: {}", err); - source - } - }; - - std::fs::write(output_path, json).with_context(|| { - format!( - "Failed to write json to output location: {}", - output_path.display() - ) - })?; - - Ok(()) - } -} +// pub trait Process { +// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()>; +// } + +// /// Process a specific file asset +// pub fn process_file(file: &ResourceAsset, output_folder: &Path) -> anyhow::Result<()> { +// todo!() +// // let location = file.location(); +// // let source = location.source(); +// // let output_path = output_folder.join(location.unique_name()); +// // file.options().process(source, &output_path) +// } + +// impl Process for FileOptions { +// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { +// if output_path.exists() { +// return Ok(()); +// } +// match self { +// Self::Other { .. } => { +// let bytes = source.read_to_bytes()?; +// std::fs::write(output_path, bytes).with_context(|| { +// format!( +// "Failed to write file to output location: {}", +// output_path.display() +// ) +// })?; +// } +// Self::Css(options) => { +// options.process(source, output_path)?; +// } +// Self::Js(options) => { +// options.process(source, output_path)?; +// } +// Self::Json(options) => { +// options.process(source, output_path)?; +// } +// Self::Image(options) => { +// options.process(source, output_path)?; +// } +// _ => todo!(), +// } + +// Ok(()) +// } +// } + +// impl Process for ImageOptions { +// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { +// let mut image = image::ImageReader::new(std::io::Cursor::new(&*source.read_to_bytes()?)) +// .with_guessed_format()? +// .decode()?; + +// if let Some(size) = self.size() { +// image = image.resize_exact(size.0, size.1, image::imageops::FilterType::Lanczos3); +// } + +// match self.ty() { +// ImageType::Png => { +// compress_png(image, output_path); +// } +// ImageType::Jpg => { +// compress_jpg(image, output_path)?; +// } +// ImageType::Avif => { +// if let Err(error) = image.save(output_path) { +// tracing::error!("Failed to save avif image: {} with path {}. You must have the avif feature enabled to use avif assets", error, output_path.display()); +// } +// } +// ImageType::Webp => { +// if let Err(err) = image.save(output_path) { +// tracing::error!("Failed to save webp image: {}. You must have the avif feature enabled to use webp assets", err); +// } +// } +// } + +// Ok(()) +// } +// } + +// fn compress_jpg(image: DynamicImage, output_location: &Path) -> anyhow::Result<()> { +// let mut comp = mozjpeg::Compress::new(mozjpeg::ColorSpace::JCS_EXT_RGBX); +// let width = image.width() as usize; +// let height = image.height() as usize; + +// comp.set_size(width, height); +// let mut comp = comp.start_compress(Vec::new())?; // any io::Write will work + +// comp.write_scanlines(image.to_rgba8().as_bytes())?; + +// let jpeg_bytes = comp.finish()?; + +// let file = std::fs::File::create(output_location)?; +// let w = &mut BufWriter::new(file); +// w.write_all(&jpeg_bytes)?; +// Ok(()) +// } + +// fn compress_png(image: DynamicImage, output_location: &Path) { +// // Image loading/saving is outside scope of this library +// let width = image.width() as usize; +// let height = image.height() as usize; +// let bitmap: Vec<_> = image +// .into_rgba8() +// .pixels() +// .map(|px| imagequant::RGBA::new(px[0], px[1], px[2], px[3])) +// .collect(); + +// // Configure the library +// let mut liq = imagequant::new(); +// liq.set_speed(5).unwrap(); +// liq.set_quality(0, 99).unwrap(); + +// // Describe the bitmap +// let mut img = liq.new_image(&bitmap[..], width, height, 0.0).unwrap(); + +// // The magic happens in quantize() +// let mut res = match liq.quantize(&mut img) { +// Ok(res) => res, +// Err(err) => panic!("Quantization failed, because: {err:?}"), +// }; + +// let (palette, pixels) = res.remapped(&mut img).unwrap(); + +// let file = std::fs::File::create(output_location).unwrap(); +// let w = &mut BufWriter::new(file); + +// let mut encoder = png::Encoder::new(w, width as u32, height as u32); +// encoder.set_color(png::ColorType::Rgba); +// let mut flattened_palette = Vec::new(); +// let mut alpha_palette = Vec::new(); +// for px in palette { +// flattened_palette.push(px.r); +// flattened_palette.push(px.g); +// flattened_palette.push(px.b); +// alpha_palette.push(px.a); +// } +// encoder.set_palette(flattened_palette); +// encoder.set_trns(alpha_palette); +// encoder.set_depth(png::BitDepth::Eight); +// encoder.set_color(png::ColorType::Indexed); +// encoder.set_compression(png::Compression::Best); +// let mut writer = encoder.write_header().unwrap(); +// writer.write_image_data(&pixels).unwrap(); +// writer.finish().unwrap(); +// } + +// impl Process for CssOptions { +// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { +// let css = source.read_to_string()?; + +// let css = if self.minify() { minify_css(&css) } else { css }; + +// std::fs::write(output_path, css).with_context(|| { +// format!( +// "Failed to write css to output location: {}", +// output_path.display() +// ) +// })?; + +// Ok(()) +// } +// } + +// pub(crate) fn minify_css(css: &str) -> String { +// let mut stylesheet = StyleSheet::parse(css, ParserOptions::default()).unwrap(); +// stylesheet.minify(MinifyOptions::default()).unwrap(); +// let printer = PrinterOptions { +// minify: true, +// ..Default::default() +// }; +// let res = stylesheet.to_css(printer).unwrap(); +// res.code +// } + +// pub(crate) fn minify_js(source: &ResourceAsset) -> anyhow::Result { +// todo!("disabled swc due to semver issues") +// // let cm = Arc::::default(); + +// // let js = source.read_to_string()?; +// // let c = swc::Compiler::new(cm.clone()); +// // let output = GLOBALS +// // .set(&Default::default(), || { +// // try_with_handler(cm.clone(), Default::default(), |handler| { +// // // let filename = Lrc::new(match source { +// // // manganis_common::ResourceAsset::Local(path) => { +// // // FileName::Real(path.canonicalized.clone()) +// // // } +// // // manganis_common::ResourceAsset::Remote(url) => FileName::Url(url.clone()), +// // // }); +// // let filename = todo!(); +// // let fm = cm.new_source_file(filename, js.to_string()); + +// // c.minify( +// // fm, +// // handler, +// // &JsMinifyOptions { +// // compress: BoolOrDataConfig::from_bool(true), +// // mangle: BoolOrDataConfig::from_bool(true), +// // ..Default::default() +// // }, +// // ) +// // .context("failed to minify javascript") +// // }) +// // }) +// // .map(|output| output.code); + +// // match output { +// // Ok(output) => Ok(output), +// // Err(err) => { +// // tracing::error!("Failed to minify javascript: {}", err); +// // Ok(js) +// // } +// // } +// } + +// impl Process for JsOptions { +// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { +// let js = if self.minify() { +// minify_js(source)? +// } else { +// source.read_to_string()? +// }; + +// std::fs::write(output_path, js).with_context(|| { +// format!( +// "Failed to write js to output location: {}", +// output_path.display() +// ) +// })?; + +// Ok(()) +// } +// } + +// pub(crate) fn minify_json(source: &str) -> anyhow::Result { +// // First try to parse the json +// let json: serde_json::Value = serde_json::from_str(source)?; +// // Then print it in a minified format +// let json = serde_json::to_string(&json)?; +// Ok(json) +// } + +// impl Process for JsonOptions { +// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { +// let source = source.read_to_string()?; +// let json = match minify_json(&source) { +// Ok(json) => json, +// Err(err) => { +// tracing::error!("Failed to minify json: {}", err); +// source +// } +// }; + +// std::fs::write(output_path, json).with_context(|| { +// format!( +// "Failed to write json to output location: {}", +// output_path.display() +// ) +// })?; + +// Ok(()) +// } +// } diff --git a/packages/cli/src/assets/folder.rs b/packages/cli/src/assets/folder.rs index ddb4c1b900..0b4d2232a4 100644 --- a/packages/cli/src/assets/folder.rs +++ b/packages/cli/src/assets/folder.rs @@ -1,59 +1,59 @@ use std::path::Path; -use manganis_common::{FileOptions, FolderAsset}; +// use manganis_common::{FileOptions, FolderAsset}; use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; -use crate::file::Process; - -/// Process a folder, optimizing and copying all assets into the output folder -pub fn process_folder(folder: &FolderAsset, output_folder: &Path) -> anyhow::Result<()> { - // Push the unique name of the folder to the output folder - let output_folder = output_folder.join(folder.unique_name()); - - if output_folder.exists() { - return Ok(()); - } - - // .location() - // // .source() - // .as_path() - let folder = folder.path(); - - // Optimize and copy all assets in the folder in parallel - process_folder_inner(folder, &output_folder) -} - -fn process_folder_inner(folder: &Path, output_folder: &Path) -> anyhow::Result<()> { - // Create the folder - std::fs::create_dir_all(output_folder)?; - - // Then optimize children - let files: Vec<_> = std::fs::read_dir(folder) - .into_iter() - .flatten() - .flatten() - .collect(); - - files.par_iter().try_for_each(|file| { - let file = file.path(); - let metadata = file.metadata()?; - let output_path = output_folder.join(file.strip_prefix(folder)?); - if metadata.is_dir() { - process_folder_inner(&file, &output_path) - } else { - process_file_minimal(&file, &output_path) - } - })?; - - Ok(()) -} - -/// Optimize a file without changing any of its contents significantly (e.g. by changing the extension) -fn process_file_minimal(input_path: &Path, output_path: &Path) -> anyhow::Result<()> { - todo!() - // let options = - // FileOptions::default_for_extension(input_path.extension().and_then(|e| e.to_str())); - // let source = input_path.to_path_buf(); - // options.process(&source, output_path)?; - // Ok(()) -} +// use crate::file::Process; + +// /// Process a folder, optimizing and copying all assets into the output folder +// pub fn process_folder(folder: &FolderAsset, output_folder: &Path) -> anyhow::Result<()> { +// // Push the unique name of the folder to the output folder +// let output_folder = output_folder.join(folder.unique_name()); + +// if output_folder.exists() { +// return Ok(()); +// } + +// // .location() +// // // .source() +// // .as_path() +// let folder = folder.path(); + +// // Optimize and copy all assets in the folder in parallel +// process_folder_inner(folder, &output_folder) +// } + +// fn process_folder_inner(folder: &Path, output_folder: &Path) -> anyhow::Result<()> { +// // Create the folder +// std::fs::create_dir_all(output_folder)?; + +// // Then optimize children +// let files: Vec<_> = std::fs::read_dir(folder) +// .into_iter() +// .flatten() +// .flatten() +// .collect(); + +// files.par_iter().try_for_each(|file| { +// let file = file.path(); +// let metadata = file.metadata()?; +// let output_path = output_folder.join(file.strip_prefix(folder)?); +// if metadata.is_dir() { +// process_folder_inner(&file, &output_path) +// } else { +// process_file_minimal(&file, &output_path) +// } +// })?; + +// Ok(()) +// } + +// /// Optimize a file without changing any of its contents significantly (e.g. by changing the extension) +// fn process_file_minimal(input_path: &Path, output_path: &Path) -> anyhow::Result<()> { +// todo!() +// // let options = +// // FileOptions::default_for_extension(input_path.extension().and_then(|e| e.to_str())); +// // let source = input_path.to_path_buf(); +// // options.process(&source, output_path)?; +// // Ok(()) +// } diff --git a/packages/cli/src/assets/linker_intercept.rs b/packages/cli/src/assets/linker_intercept.rs index 39e15d76bc..bd8e65000a 100644 --- a/packages/cli/src/assets/linker_intercept.rs +++ b/packages/cli/src/assets/linker_intercept.rs @@ -106,6 +106,7 @@ where let exec_path = std::env::current_exe().unwrap(); let mut cmd = std::process::Command::new("cargo"); + cmd.arg("rustc"); cmd.args(args); cmd.arg("--"); diff --git a/packages/cli/src/assets/manifest.rs b/packages/cli/src/assets/manifest.rs index 912d94d3ea..4be9ddb278 100644 --- a/packages/cli/src/assets/manifest.rs +++ b/packages/cli/src/assets/manifest.rs @@ -1,20 +1,19 @@ -// pub use railwind::warning::Warning as TailwindWarning; +use manganis_core::LinkSection; +use object::{File, Object, ObjectSection}; +use std::fs; use std::path::PathBuf; +// pub use railwind::warning::Warning as TailwindWarning; +// use crate::{file::process_file, process_folder}; // use manganis_common::{linker, AssetType}; -use crate::{file::process_file, process_folder}; - -use object::{File, Object, ObjectSection}; -use std::fs; - // get the text containing all the asset descriptions // in the "link section" of the binary fn get_string_manganis(file: &File) -> Option { for section in file.sections() { if let Ok(section_name) = section.name() { // Check if the link section matches the asset section for one of the platforms we support. This may not be the current platform if the user is cross compiling - if linker::LinkSection::ALL + if LinkSection::ALL .iter() .any(|x| x.link_section == section_name) { @@ -33,63 +32,69 @@ fn get_string_manganis(file: &File) -> Option { None } -// /// A manifest of all assets collected from dependencies -// #[derive(Debug, PartialEq, Default, Clone)] -// pub struct AssetManifest { -// pub(crate) assets: Vec, -// } +/// A manifest of all assets collected from dependencies +#[derive(Debug, PartialEq, Default, Clone)] +pub struct AssetManifest { + pub(crate) assets: Vec, +} -// impl AssetManifest { -// /// Creates a new asset manifest -// pub fn new(assets: Vec) -> Self { -// Self { assets } -// } +#[derive(Debug, PartialEq, Clone)] +pub enum AssetType { + File(PathBuf), + Folder(PathBuf), +} -// /// Returns all assets collected from dependencies -// pub fn assets(&self) -> &Vec { -// &self.assets -// } +impl AssetManifest { + /// Creates a new asset manifest + pub fn new(assets: Vec) -> Self { + Self { assets } + } -// /// Returns the HTML that should be injected into the head of the page -// pub fn head(&self) -> String { -// let mut head = String::new(); -// for asset in &self.assets { -// if let crate::AssetType::Resource(file) = asset { -// match file.options() { -// crate::FileOptions::Css(css_options) => { -// if css_options.preload() { -// if let Ok(asset_path) = file.served_location() { -// head.push_str(&format!( -// "\n" -// )) -// } -// } -// } -// crate::FileOptions::Image(image_options) => { -// if image_options.preload() { -// if let Ok(asset_path) = file.served_location() { -// head.push_str(&format!( -// "\n" -// )) -// } -// } -// } -// crate::FileOptions::Js(js_options) => { -// if js_options.preload() { -// if let Ok(asset_path) = file.served_location() { -// head.push_str(&format!( -// "\n" -// )) -// } -// } -// } -// _ => {} -// } -// } -// } -// head -// } -// } + /// Returns all assets collected from dependencies + pub fn assets(&self) -> &Vec { + &self.assets + } + + // /// Returns the HTML that should be injected into the head of the page + // pub fn head(&self) -> String { + // let mut head = String::new(); + // for asset in &self.assets { + // if let crate::AssetType::Resource(file) = asset { + // match file.options() { + // crate::FileOptions::Css(css_options) => { + // if css_options.preload() { + // if let Ok(asset_path) = file.served_location() { + // head.push_str(&format!( + // "\n" + // )) + // } + // } + // } + // crate::FileOptions::Image(image_options) => { + // if image_options.preload() { + // if let Ok(asset_path) = file.served_location() { + // head.push_str(&format!( + // "\n" + // )) + // } + // } + // } + // crate::FileOptions::Js(js_options) => { + // if js_options.preload() { + // if let Ok(asset_path) = file.served_location() { + // head.push_str(&format!( + // "\n" + // )) + // } + // } + // } + // _ => {} + // } + // } + // } + // head + // } +} // /// An extension trait CLI support for the asset manifest // pub trait AssetManifestExt { @@ -196,12 +201,13 @@ fn get_string_manganis(file: &File) -> Option { // } fn deserialize_assets(json: &str) -> Vec { - let deserializer = serde_json::Deserializer::from_str(json); - deserializer - .into_iter::() - .flat_map(|x| x.ok()) - // .map(|x| x.unwrap()) - .collect() + todo!() + // let deserializer = serde_json::Deserializer::from_str(json); + // deserializer + // .into_iter::() + // .flat_map(|x| x.ok()) + // // .map(|x| x.unwrap()) + // .collect() } /// Extract JSON Manganis strings from a list of object files. diff --git a/packages/cli/src/builder/assets.rs b/packages/cli/src/builder/assets.rs new file mode 100644 index 0000000000..53403c4af6 --- /dev/null +++ b/packages/cli/src/builder/assets.rs @@ -0,0 +1,82 @@ +use super::web::install_web_build_tooling; +use super::BuildRequest; +use super::TargetPlatform; +// use crate::assets::create_assets_head; +// use crate::assets::{asset_manifest, process_assets}; +use crate::assets::{copy_dir_to, AssetManifest}; +// use crate::assets::{copy_dir_to, AssetManifest}; +use crate::builder::progress::build_cargo; +use crate::builder::progress::CargoBuildResult; +use crate::builder::progress::Stage; +use crate::builder::progress::UpdateBuildProgress; +use crate::builder::progress::UpdateStage; +use crate::config::Platform; +use crate::link::LinkCommand; +use crate::Result; +use anyhow::Context; +use futures_channel::mpsc::UnboundedSender; +use std::fs::create_dir_all; +use std::path::PathBuf; +use tokio::process::Command; + +type ProgressChannel = UnboundedSender; + +impl BuildRequest { + pub async fn collect_assets( + &self, + cargo_args: Vec, + progress: &mut UnboundedSender, + ) -> anyhow::Result> { + todo!("collect assets is disabled currently") + // // If this is the server build, the client build already copied any assets we need + // if self.target_platform == TargetPlatform::Server { + // return Ok(None); + // } + // // If assets are skipped, we don't need to collect them + // if self.build_arguments.skip_assets { + // return Ok(None); + // } + + // // Start Manganis linker intercept. + // let linker_args = vec![format!("{}", self.target_out_dir().display())]; + + // // Don't block the main thread - manganis should not be running its own std process but it's + // // fine to wrap it here at the top + // let build = self.clone(); + // let mut progress = progress.clone(); + // tokio::task::spawn_blocking(move || { + // manganis_cli_support::start_linker_intercept( + // &LinkCommand::command_name(), + // cargo_args, + // Some(linker_args), + // )?; + // let assets = asset_manifest(&build); + // // Collect assets from the asset manifest the linker intercept created + // process_assets(&build, &assets, &mut progress)?; + // // Create the __assets_head.html file for bundling + // create_assets_head(&build, &assets)?; + + // Ok(Some(assets)) + // }) + // .await + // .unwrap() + } + + pub fn copy_assets_dir(&self) -> anyhow::Result<()> { + tracing::info!("Copying public assets to the output directory..."); + let out_dir = self.target_out_dir(); + let asset_dir = self.dioxus_crate.asset_dir(); + + if asset_dir.is_dir() { + // Only pre-compress the assets from the web build. Desktop assets are not served, so they don't need to be pre_compressed + let pre_compress = self.targeting_web() + && self + .dioxus_crate + .should_pre_compress_web_assets(self.build_arguments.release); + + copy_dir_to(asset_dir, out_dir, pre_compress)?; + } + + Ok(()) + } +} diff --git a/packages/cli/src/builder/cargo.rs b/packages/cli/src/builder/cargo.rs index 72844fc168..3b0b9aaf55 100644 --- a/packages/cli/src/builder/cargo.rs +++ b/packages/cli/src/builder/cargo.rs @@ -1,10 +1,10 @@ use super::web::install_web_build_tooling; use super::BuildRequest; -use super::BuildResult; use super::TargetPlatform; -use crate::assets::create_assets_head; -use crate::assets::{asset_manifest, process_assets}; -use crate::assets::{copy_dir_to, AssetManifest}; +// use crate::assets::create_assets_head; +// use crate::assets::{asset_manifest, process_assets}; +use crate::assets::AssetManifest; +// use crate::assets::{copy_dir_to, AssetManifest}; use crate::builder::progress::build_cargo; use crate::builder::progress::CargoBuildResult; use crate::builder::progress::Stage; @@ -15,9 +15,11 @@ use crate::link::LinkCommand; use crate::Result; use anyhow::Context; use futures_channel::mpsc::UnboundedSender; -// use manganis_cli_support::AssetManifest; use std::fs::create_dir_all; use std::path::PathBuf; +use tokio::process::Command; + +type ProgressChannel = UnboundedSender; impl BuildRequest { /// Create a list of arguments for cargo builds @@ -78,8 +80,8 @@ impl BuildRequest { } /// Create a build command for cargo - fn prepare_build_command(&self) -> Result<(tokio::process::Command, Vec)> { - let mut cmd = tokio::process::Command::new("cargo"); + fn prepare_build_command(&self) -> Result<(Command, Vec)> { + let mut cmd = Command::new("cargo"); cmd.arg("rustc"); if let Some(target_dir) = &self.target_dir { cmd.env("CARGO_TARGET_DIR", target_dir); @@ -96,10 +98,7 @@ impl BuildRequest { Ok((cmd, cargo_args)) } - pub(crate) async fn build( - &self, - mut progress: UnboundedSender, - ) -> Result { + pub(crate) async fn build(self, mut progress: ProgressChannel) -> Result { tracing::info!("🚅 Running build [Desktop] command..."); // Set up runtime guards @@ -122,8 +121,7 @@ impl BuildRequest { let cargo_result = build_cargo(crate_count, cmd, &mut progress).await?; // Post process the build result - let build_result = self - .post_process_build(cargo_args, &cargo_result, &mut progress) + self.post_process_build(cargo_args, &cargo_result, &mut progress) .await .context("Failed to post process build")?; @@ -137,7 +135,7 @@ impl BuildRequest { update: UpdateStage::Start, }); - Ok(build_result) + Ok(self) } async fn post_process_build( @@ -145,7 +143,7 @@ impl BuildRequest { cargo_args: Vec, cargo_build_result: &CargoBuildResult, progress: &mut UnboundedSender, - ) -> Result { + ) -> Result<()> { _ = progress.start_send(UpdateBuildProgress { stage: Stage::OptimizingAssets, update: UpdateStage::Start, @@ -177,75 +175,12 @@ impl BuildRequest { self.copy_assets_dir()?; - // Create the build result - let build_result = BuildResult { - executable: output_path, - target_platform: self.target_platform, - }; - // If this is a web build, run web post processing steps if self.targeting_web() { - self.post_process_web_build(&build_result, assets.as_ref(), progress) + self.post_process_web_build(assets.as_ref(), progress) .await?; } - Ok(build_result) - } - - async fn collect_assets( - &self, - cargo_args: Vec, - progress: &mut UnboundedSender, - ) -> anyhow::Result> { - todo!("collect assets is disabled currently") - // // If this is the server build, the client build already copied any assets we need - // if self.target_platform == TargetPlatform::Server { - // return Ok(None); - // } - // // If assets are skipped, we don't need to collect them - // if self.build_arguments.skip_assets { - // return Ok(None); - // } - - // // Start Manganis linker intercept. - // let linker_args = vec![format!("{}", self.target_out_dir().display())]; - - // // Don't block the main thread - manganis should not be running its own std process but it's - // // fine to wrap it here at the top - // let build = self.clone(); - // let mut progress = progress.clone(); - // tokio::task::spawn_blocking(move || { - // manganis_cli_support::start_linker_intercept( - // &LinkCommand::command_name(), - // cargo_args, - // Some(linker_args), - // )?; - // let assets = asset_manifest(&build); - // // Collect assets from the asset manifest the linker intercept created - // process_assets(&build, &assets, &mut progress)?; - // // Create the __assets_head.html file for bundling - // create_assets_head(&build, &assets)?; - - // Ok(Some(assets)) - // }) - // .await - // .unwrap() - } - - pub fn copy_assets_dir(&self) -> anyhow::Result<()> { - tracing::info!("Copying public assets to the output directory..."); - let out_dir = self.target_out_dir(); - let asset_dir = self.dioxus_crate.asset_dir(); - - if asset_dir.is_dir() { - // Only pre-compress the assets from the web build. Desktop assets are not served, so they don't need to be pre_compressed - let pre_compress = self.targeting_web() - && self - .dioxus_crate - .should_pre_compress_web_assets(self.build_arguments.release); - - copy_dir_to(asset_dir, out_dir, pre_compress)?; - } Ok(()) } diff --git a/packages/cli/src/builder/fullstack.rs b/packages/cli/src/builder/fullstack.rs index 4cfdea9a47..443e928d9a 100644 --- a/packages/cli/src/builder/fullstack.rs +++ b/packages/cli/src/builder/fullstack.rs @@ -6,62 +6,13 @@ use crate::dioxus_crate::DioxusCrate; use crate::builder::BuildRequest; use std::io::Write; -use super::TargetPlatform; - -static CLIENT_PROFILE: &str = "dioxus-client"; -static SERVER_PROFILE: &str = "dioxus-server"; - -// The `opt-level=2` increases build times, but can noticeably decrease time -// between saving changes and being able to interact with an app. The "overall" -// time difference (between having and not having the optimization) can be -// almost imperceptible (~1 s) but also can be very noticeable (~6 s) — depends -// on setup (hardware, OS, browser, idle load). -// Find or create the client and server profiles in the .cargo/config.toml file -fn initialize_profiles(config: &DioxusCrate) -> crate::Result<()> { - let config_path = config.workspace_dir().join(".cargo/config.toml"); - let mut config = match std::fs::read_to_string(&config_path) { - Ok(config) => config.parse::().map_err(|e| { - crate::Error::Other(anyhow::anyhow!("Failed to parse .cargo/config.toml: {}", e)) - })?, - Err(_) => Default::default(), - }; - - if let Item::Table(table) = config - .as_table_mut() - .entry("profile") - .or_insert(Item::Table(Default::default())) - { - if let toml_edit::Entry::Vacant(entry) = table.entry(CLIENT_PROFILE) { - let mut client = toml_edit::Table::new(); - client.insert("inherits", Item::Value("dev".into())); - client.insert("opt-level", Item::Value(2.into())); - entry.insert(Item::Table(client)); - } - - if let toml_edit::Entry::Vacant(entry) = table.entry(SERVER_PROFILE) { - let mut server = toml_edit::Table::new(); - server.insert("inherits", Item::Value("dev".into())); - server.insert("opt-level", Item::Value(2.into())); - entry.insert(Item::Table(server)); - } - } - - // Write the config back to the file - if let Some(parent) = config_path.parent() { - std::fs::create_dir_all(parent)?; - } - let file = std::fs::File::create(config_path)?; - let mut buf_writer = std::io::BufWriter::new(file); - write!(buf_writer, "{}", config)?; - - Ok(()) -} +use super::{BuildReason, TargetPlatform}; impl BuildRequest { pub(crate) fn new_fullstack( config: DioxusCrate, build_arguments: Build, - serve: bool, + serve: BuildReason, ) -> Result, crate::Error> { initialize_profiles(&config)?; @@ -72,7 +23,7 @@ impl BuildRequest { } fn new_with_target_directory_rust_flags_and_features( - serve: bool, + serve: BuildReason, config: &DioxusCrate, build: &Build, feature: Option, @@ -87,16 +38,17 @@ impl BuildRequest { // Add the server flags to the build arguments Self { - serve, + reason: serve, build_arguments: build.clone(), dioxus_crate: config, rust_flags: Default::default(), target_dir: None, target_platform, + executable: None, } } - fn new_server(serve: bool, config: &DioxusCrate, build: &Build) -> Self { + fn new_server(serve: BuildReason, config: &DioxusCrate, build: &Build) -> Self { let mut build = build.clone(); if build.profile.is_none() { build.profile = Some(CLIENT_PROFILE.to_string()); @@ -111,7 +63,7 @@ impl BuildRequest { ) } - fn new_client(serve: bool, config: &DioxusCrate, build: &Build) -> Self { + fn new_client(serve: BuildReason, config: &DioxusCrate, build: &Build) -> Self { let mut build = build.clone(); if build.profile.is_none() { build.profile = Some(SERVER_PROFILE.to_string()); @@ -126,3 +78,52 @@ impl BuildRequest { ) } } + +static CLIENT_PROFILE: &str = "dioxus-client"; +static SERVER_PROFILE: &str = "dioxus-server"; + +// The `opt-level=2` increases build times, but can noticeably decrease time +// between saving changes and being able to interact with an app. The "overall" +// time difference (between having and not having the optimization) can be +// almost imperceptible (~1 s) but also can be very noticeable (~6 s) — depends +// on setup (hardware, OS, browser, idle load). +// Find or create the client and server profiles in the .cargo/config.toml file +fn initialize_profiles(config: &DioxusCrate) -> crate::Result<()> { + let config_path = config.workspace_dir().join(".cargo/config.toml"); + let mut config = match std::fs::read_to_string(&config_path) { + Ok(config) => config.parse::().map_err(|e| { + crate::Error::Other(anyhow::anyhow!("Failed to parse .cargo/config.toml: {}", e)) + })?, + Err(_) => Default::default(), + }; + + if let Item::Table(table) = config + .as_table_mut() + .entry("profile") + .or_insert(Item::Table(Default::default())) + { + if let toml_edit::Entry::Vacant(entry) = table.entry(CLIENT_PROFILE) { + let mut client = toml_edit::Table::new(); + client.insert("inherits", Item::Value("dev".into())); + client.insert("opt-level", Item::Value(2.into())); + entry.insert(Item::Table(client)); + } + + if let toml_edit::Entry::Vacant(entry) = table.entry(SERVER_PROFILE) { + let mut server = toml_edit::Table::new(); + server.insert("inherits", Item::Value("dev".into())); + server.insert("opt-level", Item::Value(2.into())); + entry.insert(Item::Table(server)); + } + } + + // Write the config back to the file + if let Some(parent) = config_path.parent() { + std::fs::create_dir_all(parent)?; + } + let file = std::fs::File::create(config_path)?; + let mut buf_writer = std::io::BufWriter::new(file); + write!(buf_writer, "{}", config)?; + + Ok(()) +} diff --git a/packages/cli/src/builder/mod.rs b/packages/cli/src/builder/mod.rs index ece0b0a537..3e71c765a4 100644 --- a/packages/cli/src/builder/mod.rs +++ b/packages/cli/src/builder/mod.rs @@ -4,61 +4,32 @@ use crate::{build::Build, config}; use crate::{cli::serve::ServeArguments, config::Platform}; use futures_util::stream::select_all; use futures_util::StreamExt; -use std::net::SocketAddr; -use std::str::FromStr; +pub use platform::TargetPlatform; +use std::{net::SocketAddr, path::Path}; use std::{path::PathBuf, process::Stdio}; use tokio::process::{Child, Command}; +mod assets; mod cargo; mod fullstack; +mod platform; mod prepare_html; mod progress; mod web; + pub use progress::{ BuildMessage, MessageSource, MessageType, Stage, UpdateBuildProgress, UpdateStage, }; -/// The target platform for the build -/// This is very similar to the Platform enum, but we need to be able to differentiate between the -/// server and web targets for the fullstack platform -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum TargetPlatform { - Web, - Desktop, - Server, - Liveview, -} - -impl FromStr for TargetPlatform { - type Err = (); - - fn from_str(s: &str) -> Result { - match s { - "web" => Ok(Self::Web), - "desktop" => Ok(Self::Desktop), - "axum" | "server" => Ok(Self::Server), - "liveview" => Ok(Self::Liveview), - _ => Err(()), - } - } -} - -impl std::fmt::Display for TargetPlatform { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - TargetPlatform::Web => write!(f, "web"), - TargetPlatform::Desktop => write!(f, "desktop"), - TargetPlatform::Server => write!(f, "server"), - TargetPlatform::Liveview => write!(f, "liveview"), - } - } -} - /// A request for a project to be built +/// +/// As the build progresses, we'll fill in fields like assets, executable, entitlements, etc +/// +/// This request will be then passed to the bundler to create a final bundled app #[derive(Clone)] pub struct BuildRequest { /// Whether the build is for serving the application - pub serve: bool, + pub reason: BuildReason, /// The configuration for the crate we are building pub dioxus_crate: DioxusCrate, @@ -74,11 +45,22 @@ pub struct BuildRequest { /// The target directory for the build pub target_dir: Option, + + /// The output executable location + pub executable: Option, +} + +/// The reason for the build - this will determine how we prep the output +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum BuildReason { + Serve, + Build, + Bundle, } impl BuildRequest { pub fn create( - serve: bool, + serve: BuildReason, dioxus_crate: &DioxusCrate, build_arguments: impl Into, ) -> crate::Result> { @@ -86,15 +68,20 @@ impl BuildRequest { let platform = build_arguments.platform(); let single_platform = |platform| { let dioxus_crate = dioxus_crate.clone(); - vec![Self { - serve, + + let request = Self { + reason: serve, dioxus_crate, build_arguments: build_arguments.clone(), target_platform: platform, rust_flags: Default::default(), target_dir: Default::default(), - }] + executable: Default::default(), + }; + + vec![request] }; + Ok(match platform { Platform::Liveview => single_platform(TargetPlatform::Liveview), Platform::Web => single_platform(TargetPlatform::Web), @@ -108,7 +95,7 @@ impl BuildRequest { pub(crate) async fn build_all_parallel( build_requests: Vec, - ) -> Result> { + ) -> Result> { let multi_platform_build = build_requests.len() > 1; let mut build_progress = Vec::new(); let mut set = tokio::task::JoinSet::new(); @@ -155,56 +142,58 @@ impl BuildRequest { pub fn targeting_web(&self) -> bool { self.target_platform == TargetPlatform::Web } -} -#[derive(Debug, Clone)] -pub(crate) struct BuildResult { - pub executable: PathBuf, - pub target_platform: TargetPlatform, -} - -impl BuildResult { /// Open the executable if this is a native build pub fn open( &self, config: &DioxusCrate, serve: &ServeArguments, fullstack_address: Option, - workspace: &std::path::Path, + workspace: &Path, ) -> std::io::Result> { if self.target_platform == TargetPlatform::Web { return Ok(None); } + if self.target_platform == TargetPlatform::Server { tracing::trace!("Proxying fullstack server from port {fullstack_address:?}"); } - // let arguments = RuntimeCLIArguments::new(serve.address.address(), fullstack_address); - let executable = self.executable.canonicalize()?; - let mut cmd = Command::new(executable); - - let mut _cmd = cmd - .env( - dioxus_runtime_config::FULLSTACK_ADDRESS_ENV, - fullstack_address - .as_ref() - .map(|addr| addr.to_string()) - .unwrap_or_else(|| "127.0.0.1:8080".to_string()), - ) - .env( - dioxus_runtime_config::DEVSERVER_ADDR_ENV, - serve.address.address().to_string(), - ) - .env( - dioxus_runtime_config::MOBILE_DEVSERVER_ADDR_ENV, - serve.address.address().to_string(), - ) - .env("CARGO_MANIFEST_DIR", config.crate_dir()) - .stderr(Stdio::piped()) - .stdout(Stdio::piped()) - .kill_on_drop(true) - .current_dir(workspace); - - Ok(Some(_cmd.spawn()?)) + // + // open the exe with some arguments/envvars/etc + // we're going to try and configure this binary from the environment, if we can + // + // web can't be configured like this, so instead, we'll need to plumb a meta tag into the + // index.html during dev + // + let res = Command::new( + self.executable + .as_deref() + .expect("executable should be built if we're trying to open it") + .canonicalize()?, + ) + .env( + dioxus_runtime_config::FULLSTACK_ADDRESS_ENV, + fullstack_address + .as_ref() + .map(|addr| addr.to_string()) + .unwrap_or_else(|| "127.0.0.1:8080".to_string()), + ) + .env( + dioxus_runtime_config::DEVSERVER_ADDR_ENV, + serve.address.address().to_string(), + ) + .env( + dioxus_runtime_config::IOS_DEVSERVER_ADDR_ENV, + serve.address.address().to_string(), + ) + .env("CARGO_MANIFEST_DIR", config.crate_dir()) + .stderr(Stdio::piped()) + .stdout(Stdio::piped()) + .kill_on_drop(true) + .current_dir(workspace) + .spawn()?; + + Ok(Some(res)) } } diff --git a/packages/cli/src/builder/platform.rs b/packages/cli/src/builder/platform.rs new file mode 100644 index 0000000000..8b4f6f8832 --- /dev/null +++ b/packages/cli/src/builder/platform.rs @@ -0,0 +1,37 @@ +use std::str::FromStr; + +/// The target platform for the build +/// This is very similar to the Platform enum, but we need to be able to differentiate between the +/// server and web targets for the fullstack platform +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum TargetPlatform { + Web, + Desktop, + Server, + Liveview, +} + +impl FromStr for TargetPlatform { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + "web" => Ok(Self::Web), + "desktop" => Ok(Self::Desktop), + "axum" | "server" => Ok(Self::Server), + "liveview" => Ok(Self::Liveview), + _ => Err(()), + } + } +} + +impl std::fmt::Display for TargetPlatform { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + TargetPlatform::Web => write!(f, "web"), + TargetPlatform::Desktop => write!(f, "desktop"), + TargetPlatform::Server => write!(f, "server"), + TargetPlatform::Liveview => write!(f, "liveview"), + } + } +} diff --git a/packages/cli/src/builder/prepare_html.rs b/packages/cli/src/builder/prepare_html.rs index c338a62995..593a05cdf3 100644 --- a/packages/cli/src/builder/prepare_html.rs +++ b/packages/cli/src/builder/prepare_html.rs @@ -1,6 +1,6 @@ //! Build the HTML file to load a web application. The index.html file may be created from scratch or modified from the `index.html` file in the crate root. -use super::{BuildRequest, UpdateBuildProgress}; +use super::{BuildReason, BuildRequest, UpdateBuildProgress}; use crate::builder::Stage; use crate::Result; use crate::{assets::AssetManifest, builder::progress::MessageSource}; @@ -37,6 +37,10 @@ impl BuildRequest { Ok(html) } + fn is_dev_build(&self) -> bool { + self.reason == BuildReason::Serve && !self.build_arguments.release + } + // Inject any resources from the config into the html fn inject_resources( &self, @@ -49,12 +53,13 @@ impl BuildRequest { let mut style_list = resources.style.clone().unwrap_or_default(); let mut script_list = resources.script.clone().unwrap_or_default(); - if self.serve { + if self.is_dev_build() { style_list.extend(resources.dev.style.iter().cloned()); script_list.extend(resources.dev.script.iter().cloned()); } let mut head_resources = String::new(); + // Add all styles to the head for style in &style_list { writeln!( @@ -64,10 +69,6 @@ impl BuildRequest { )?; } - if !style_list.is_empty() { - self.send_resource_deprecation_warning(progress, style_list, ResourceType::Style); - } - // Add all scripts to the head for script in &script_list { writeln!( @@ -77,14 +78,17 @@ impl BuildRequest { )?; } + if !style_list.is_empty() { + self.send_resource_deprecation_warning(progress, style_list, ResourceType::Style); + } if !script_list.is_empty() { self.send_resource_deprecation_warning(progress, script_list, ResourceType::Script); } // Inject any resources from manganis into the head - if let Some(assets) = assets { - head_resources.push_str(&assets.head()); - } + // if let Some(assets) = assets { + // head_resources.push_str(&assets.head()); + // } replace_or_insert_before("{style_include}", " html.replace("{DX_TOAST_UTILITIES}", TOAST_HTML), false => html.replace("{DX_TOAST_UTILITIES}", ""), }; @@ -127,7 +132,8 @@ impl BuildRequest { " - Result<()> { - let cli_bindgen_version = wasm_bindgen_shared::version(); - tracing::info!("Attempting to recover from bindgen failure by setting the wasm-bindgen version to {cli_bindgen_version}..."); - - let output = Command::new("cargo") - .args([ - "update", - "-p", - "wasm-bindgen", - "--precise", - &cli_bindgen_version, - ]) - .output() - .await; - let mut error_message = None; - if let Ok(output) = output { - if output.status.success() { - tracing::info!("Successfully updated wasm-bindgen to {cli_bindgen_version}"); - return Ok(()); - } else { - error_message = Some(output); - } - } - - if let Some(output) = error_message { - tracing::error!("Failed to update wasm-bindgen: {:#?}", output); - } - - Err(Error::BuildFailed(format!("WASM bindgen build failed!\nThis is probably due to the Bindgen version, dioxus-cli is using `{cli_bindgen_version}` which is not compatible with your crate.\nPlease reinstall the dioxus cli to fix this issue.\nYou can reinstall the dioxus cli by running `cargo install dioxus-cli --force` and then rebuild your project"))) -} - -/// Check if the wasm32-unknown-unknown target is installed and try to install it if not -pub(crate) async fn install_web_build_tooling( - progress: &mut UnboundedSender, -) -> Result<()> { - // If the user has rustup, we can check if the wasm32-unknown-unknown target is installed - // Otherwise we can just assume it is installed - which is not great... - // Eventually we can poke at the errors and let the user know they need to install the target - if let Ok(wasm_check_command) = Command::new("rustup").args(["show"]).output().await { - let wasm_check_output = String::from_utf8(wasm_check_command.stdout).unwrap(); - if !wasm_check_output.contains("wasm32-unknown-unknown") { - _ = progress.start_send(UpdateBuildProgress { - stage: Stage::InstallingWasmTooling, - update: UpdateStage::Start, - }); - tracing::info!("wasm32-unknown-unknown target not detected, installing.."); - let _ = Command::new("rustup") - .args(["target", "add", "wasm32-unknown-unknown"]) - .output() - .await?; - } - } - - Ok(()) -} - impl BuildRequest { async fn run_wasm_bindgen(&self, input_path: &Path, bindgen_outdir: &Path) -> Result<()> { tracing::info!("Running wasm-bindgen"); @@ -112,7 +54,6 @@ impl BuildRequest { /// Post process the WASM build artifacts pub(crate) async fn post_process_web_build( &self, - build_result: &BuildResult, assets: Option<&AssetManifest>, progress: &mut UnboundedSender, ) -> Result<()> { @@ -122,7 +63,7 @@ impl BuildRequest { }); // Find the wasm file - let output_location = build_result.executable.clone(); + let output_location = self.executable.clone().unwrap(); let input_path = output_location.with_extension("wasm"); // Create the directory where the bindgen output will be placed @@ -176,6 +117,7 @@ impl BuildRequest { let pre_compress = self .dioxus_crate .should_pre_compress_web_assets(self.build_arguments.release); + tokio::task::spawn_blocking(move || pre_compress_folder(&bindgen_outdir, pre_compress)) .await .unwrap()?; @@ -191,3 +133,61 @@ impl BuildRequest { Ok(()) } } + +// Attempt to automatically recover from a bindgen failure by updating the wasm-bindgen version +async fn update_wasm_bindgen_version() -> Result<()> { + let cli_bindgen_version = wasm_bindgen_shared::version(); + tracing::info!("Attempting to recover from bindgen failure by setting the wasm-bindgen version to {cli_bindgen_version}..."); + + let output = Command::new("cargo") + .args([ + "update", + "-p", + "wasm-bindgen", + "--precise", + &cli_bindgen_version, + ]) + .output() + .await; + + let mut error_message = None; + if let Ok(output) = output { + if output.status.success() { + tracing::info!("Successfully updated wasm-bindgen to {cli_bindgen_version}"); + return Ok(()); + } else { + error_message = Some(output); + } + } + + if let Some(output) = error_message { + tracing::error!("Failed to update wasm-bindgen: {:#?}", output); + } + + Err(Error::BuildFailed(format!("WASM bindgen build failed!\nThis is probably due to the Bindgen version, dioxus-cli is using `{cli_bindgen_version}` which is not compatible with your crate.\nPlease reinstall the dioxus cli to fix this issue.\nYou can reinstall the dioxus cli by running `cargo install dioxus-cli --force` and then rebuild your project"))) +} + +type ProgressChannel = UnboundedSender; + +/// Check if the wasm32-unknown-unknown target is installed and try to install it if not +pub(crate) async fn install_web_build_tooling(progress: &mut ProgressChannel) -> Result<()> { + // If the user has rustup, we can check if the wasm32-unknown-unknown target is installed + // Otherwise we can just assume it is installed - which is not great... + // Eventually we can poke at the errors and let the user know they need to install the target + if let Ok(wasm_check_command) = Command::new("rustup").args(["show"]).output().await { + let wasm_check_output = String::from_utf8(wasm_check_command.stdout).unwrap(); + if !wasm_check_output.contains("wasm32-unknown-unknown") { + _ = progress.start_send(UpdateBuildProgress { + stage: Stage::InstallingWasmTooling, + update: UpdateStage::Start, + }); + tracing::info!("wasm32-unknown-unknown target not detected, installing.."); + let _ = Command::new("rustup") + .args(["target", "add", "wasm32-unknown-unknown"]) + .output() + .await?; + } + } + + Ok(()) +} diff --git a/packages/cli/src/cli/build.rs b/packages/cli/src/cli/build.rs index 4a4b6e5044..b201121fb7 100644 --- a/packages/cli/src/cli/build.rs +++ b/packages/cli/src/cli/build.rs @@ -1,47 +1,14 @@ use std::str::FromStr; -use crate::{builder::BuildRequest, dioxus_crate::DioxusCrate}; use crate::{builder::TargetPlatform, config::Platform}; +use crate::{ + builder::{BuildReason, BuildRequest}, + dioxus_crate::DioxusCrate, +}; use anyhow::Context; use super::*; -/// Information about the target to build -#[derive(Clone, Debug, Default, Deserialize, Parser)] -pub struct TargetArgs { - /// Build for nightly [default: false] - #[clap(long)] - pub nightly: bool, - - /// Build a example [default: ""] - #[clap(long)] - pub example: Option, - - /// Build a binary [default: ""] - #[clap(long)] - pub bin: Option, - - /// The package to build - #[clap(short, long)] - pub package: Option, - - /// Space separated list of features to activate - #[clap(long)] - pub features: Vec, - - /// The feature to use for the client in a fullstack app [default: "web"] - #[clap(long)] - pub client_feature: Option, - - /// The feature to use for the server in a fullstack app [default: "server"] - #[clap(long)] - pub server_feature: Option, - - /// Rustc platform triple - #[clap(long)] - pub target: Option, -} - /// Build the Rust Dioxus app and all of its assets. #[derive(Clone, Debug, Default, Deserialize, Parser)] #[clap(name = "build")] @@ -92,6 +59,42 @@ pub struct Build { pub target_args: TargetArgs, } +/// Information about the target to build +#[derive(Clone, Debug, Default, Deserialize, Parser)] +pub struct TargetArgs { + /// Build for nightly [default: false] + #[clap(long)] + pub nightly: bool, + + /// Build a example [default: ""] + #[clap(long)] + pub example: Option, + + /// Build a binary [default: ""] + #[clap(long)] + pub bin: Option, + + /// The package to build + #[clap(short, long)] + pub package: Option, + + /// Space separated list of features to activate + #[clap(long)] + pub features: Vec, + + /// The feature to use for the client in a fullstack app [default: "web"] + #[clap(long)] + pub client_feature: Option, + + /// The feature to use for the server in a fullstack app [default: "server"] + #[clap(long)] + pub server_feature: Option, + + /// Rustc platform triple + #[clap(long)] + pub target: Option, +} + impl Build { pub fn resolve(&mut self, dioxus_crate: &mut DioxusCrate) -> Result<()> { // Inherit the platform from the defaults @@ -110,7 +113,7 @@ impl Build { pub async fn build(&mut self, dioxus_crate: &mut DioxusCrate) -> Result<()> { self.resolve(dioxus_crate)?; - let build_requests = BuildRequest::create(false, dioxus_crate, self.clone())?; + let build_requests = BuildRequest::create(BuildReason::Build, dioxus_crate, self.clone())?; BuildRequest::build_all_parallel(build_requests).await?; Ok(()) } diff --git a/packages/cli/src/serve/builder.rs b/packages/cli/src/serve/builder.rs index 79028b67d6..0081231a3c 100644 --- a/packages/cli/src/serve/builder.rs +++ b/packages/cli/src/serve/builder.rs @@ -1,7 +1,6 @@ -use crate::builder::BuildRequest; -use crate::builder::BuildResult; use crate::builder::TargetPlatform; use crate::builder::UpdateBuildProgress; +use crate::builder::{BuildReason, BuildRequest}; use crate::dioxus_crate::DioxusCrate; use crate::serve::next_or_pending; use crate::serve::Serve; @@ -10,7 +9,7 @@ use futures_channel::mpsc::UnboundedReceiver; use futures_util::future::OptionFuture; use futures_util::stream::select_all; use futures_util::StreamExt; -use std::process::Stdio; +use std::process::{ExitStatus, Stdio}; use tokio::{ process::{Child, Command}, task::JoinHandle, @@ -19,7 +18,7 @@ use tokio::{ /// A handle to ongoing builds and then the spawned tasks themselves pub struct Builder { /// The results of the build - build_results: Option>>>, + build_results: Option>>>, /// The progress of the builds build_progress: Vec<(TargetPlatform, UnboundedReceiver)>, @@ -51,12 +50,15 @@ impl Builder { /// Start a new build - killing the current one if it exists pub fn build(&mut self) -> Result<()> { self.shutdown(); - let build_requests = - BuildRequest::create(true, &self.config, self.serve.build_arguments.clone())?; + let build_requests = BuildRequest::create( + BuildReason::Serve, + &self.config, + self.serve.build_arguments.clone(), + )?; let mut set = tokio::task::JoinSet::new(); - for build_request in build_requests { + for mut build_request in build_requests { let (mut tx, rx) = futures_channel::mpsc::unbounded(); self.build_progress .push((build_request.target_platform, rx)); @@ -180,10 +182,10 @@ pub enum BuilderUpdate { update: UpdateBuildProgress, }, Ready { - results: Vec, + results: Vec, }, ProcessExited { target_platform: TargetPlatform, - status: Result, + status: Result, }, } diff --git a/packages/cli/src/serve/mod.rs b/packages/cli/src/serve/mod.rs index dd4e71ba33..6bb7366070 100644 --- a/packages/cli/src/serve/mod.rs +++ b/packages/cli/src/serve/mod.rs @@ -164,6 +164,8 @@ pub async fn serve_all( // And then finally tell the server to reload server.send_reload_command().await; + + // We also want to watch any of its assets for changes? }, // If the process exited *cleanly*, we can exit diff --git a/packages/cli/src/serve/output.rs b/packages/cli/src/serve/output.rs index bb2b908409..0d894991ec 100644 --- a/packages/cli/src/serve/output.rs +++ b/packages/cli/src/serve/output.rs @@ -1,4 +1,8 @@ -use crate::config::{AddressArguments, Platform}; +use crate::{ + builder::BuildRequest, + config::{AddressArguments, Platform}, +}; +use crate::{builder::UpdateStage, serve::Serve}; use crate::{ builder::{ BuildMessage, MessageSource, MessageType, Stage, TargetPlatform, UpdateBuildProgress, @@ -7,10 +11,6 @@ use crate::{ serve::next_or_pending, tracer::CLILogControl, }; -use crate::{ - builder::{BuildResult, UpdateStage}, - serve::Serve, -}; use core::panic; use crossterm::{ event::{Event, EventStream, KeyCode, KeyModifiers, MouseEventKind}, @@ -525,7 +525,7 @@ impl Output { } } - pub fn new_ready_app(&mut self, build_engine: &mut Builder, results: Vec) { + pub fn new_ready_app(&mut self, build_engine: &mut Builder, results: Vec) { for result in results { let out = build_engine .children @@ -980,7 +980,7 @@ async fn rustc_version() -> String { } pub struct RunningApp { - result: BuildResult, + result: BuildRequest, output: Option, } diff --git a/packages/cli/src/serve/watcher.rs b/packages/cli/src/serve/watcher.rs index cdd57fd3ef..0d5dc1ad5d 100644 --- a/packages/cli/src/serve/watcher.rs +++ b/packages/cli/src/serve/watcher.rs @@ -22,7 +22,7 @@ pub struct Watcher { _tx: UnboundedSender, rx: UnboundedReceiver, _last_update_time: i64, - _watcher: Box, + watcher: Box, queued_events: Vec, file_map: FileMap, ignore: Gitignore, @@ -129,7 +129,7 @@ impl Watcher { Self { _tx: tx, rx, - _watcher: watcher, + watcher, file_map, ignore, queued_events: Vec::new(), diff --git a/packages/manganis-core/Cargo.toml b/packages/manganis-core/Cargo.toml new file mode 100644 index 0000000000..a8ced4f569 --- /dev/null +++ b/packages/manganis-core/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "manganis-core" +edition = "2021" +version.workspace = true + +[dependencies] \ No newline at end of file diff --git a/packages/manganis-core/src/lib.rs b/packages/manganis-core/src/lib.rs new file mode 100644 index 0000000000..e86444cf42 --- /dev/null +++ b/packages/manganis-core/src/lib.rs @@ -0,0 +1,69 @@ +/// Information about the manganis link section for a given platform +#[derive(Debug, Clone, Copy)] +pub struct LinkSection { + /// The link section we pass to the static + pub link_section: &'static str, + /// The name of the section we find in the binary + pub name: &'static str, +} + +impl LinkSection { + /// The list of link sections for all supported platforms + pub const ALL: &'static [&'static LinkSection] = + &[Self::WASM, Self::MACOS, Self::WINDOWS, Self::ILLUMOS]; + + /// Returns the link section used in linux, android, fuchsia, psp, freebsd, and wasm32 + pub const WASM: &'static LinkSection = &LinkSection { + link_section: "manganis", + name: "manganis", + }; + + /// Returns the link section used in macOS, iOS, tvOS + pub const MACOS: &'static LinkSection = &LinkSection { + link_section: "__DATA,manganis,regular,no_dead_strip", + name: "manganis", + }; + + /// Returns the link section used in windows + pub const WINDOWS: &'static LinkSection = &LinkSection { + link_section: "mg", + name: "mg", + }; + + /// Returns the link section used in illumos + pub const ILLUMOS: &'static LinkSection = &LinkSection { + link_section: "set_manganis", + name: "set_manganis", + }; + + /// The link section used on the current platform + pub const CURRENT: &'static LinkSection = { + #[cfg(any( + target_os = "none", + target_os = "linux", + target_os = "android", + target_os = "fuchsia", + target_os = "psp", + target_os = "freebsd", + target_arch = "wasm32" + ))] + { + Self::WASM + } + + #[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos"))] + { + Self::MACOS + } + + #[cfg(target_os = "windows")] + { + Self::WINDOWS + } + + #[cfg(target_os = "illumos")] + { + Self::ILLUMOS + } + }; +} diff --git a/packages/manganis/src/common/linker.rs b/packages/manganis/src/common/linker.rs index b795c8976f..82100b8314 100644 --- a/packages/manganis/src/common/linker.rs +++ b/packages/manganis/src/common/linker.rs @@ -3,73 +3,3 @@ //! // code taken from https://github.com/dtolnay/linkme/, // MIT license - -/// Information about the manganis link section for a given platform -#[derive(Debug, Clone, Copy)] -pub struct LinkSection { - /// The link section we pass to the static - pub link_section: &'static str, - /// The name of the section we find in the binary - pub name: &'static str, -} - -impl LinkSection { - /// The list of link sections for all supported platforms - pub const ALL: &'static [&'static LinkSection] = - &[Self::WASM, Self::MACOS, Self::WINDOWS, Self::ILLUMOS]; - - /// Returns the link section used in linux, android, fuchsia, psp, freebsd, and wasm32 - pub const WASM: &'static LinkSection = &LinkSection { - link_section: "manganis", - name: "manganis", - }; - - /// Returns the link section used in macOS, iOS, tvOS - pub const MACOS: &'static LinkSection = &LinkSection { - link_section: "__DATA,manganis,regular,no_dead_strip", - name: "manganis", - }; - - /// Returns the link section used in windows - pub const WINDOWS: &'static LinkSection = &LinkSection { - link_section: "mg", - name: "mg", - }; - - /// Returns the link section used in illumos - pub const ILLUMOS: &'static LinkSection = &LinkSection { - link_section: "set_manganis", - name: "set_manganis", - }; - - /// The link section used on the current platform - pub const CURRENT: &'static LinkSection = { - #[cfg(any( - target_os = "none", - target_os = "linux", - target_os = "android", - target_os = "fuchsia", - target_os = "psp", - target_os = "freebsd", - target_arch = "wasm32" - ))] - { - Self::WASM - } - - #[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos"))] - { - Self::MACOS - } - - #[cfg(target_os = "windows")] - { - Self::WINDOWS - } - - #[cfg(target_os = "illumos")] - { - Self::ILLUMOS - } - }; -} diff --git a/packages/runtime-config/src/lib.rs b/packages/runtime-config/src/lib.rs index 0b585406ec..8adccad160 100644 --- a/packages/runtime-config/src/lib.rs +++ b/packages/runtime-config/src/lib.rs @@ -1,7 +1,10 @@ use std::net::SocketAddr; pub const DEVSERVER_ADDR_ENV: &str = "DIOXUS_DEVSERVER_ADDR"; -pub const MOBILE_DEVSERVER_ADDR_ENV: &str = "DIOXUS_MOBILE_DEVSERVER_ADDR"; +pub const FULLSTACK_ADDRESS_ENV: &str = "DIOXUS_FULLSTACK_ADDRESS"; + +/// when targetting ios, we need to set a prefix to the argument such that it gets picked up by simctl +pub const IOS_DEVSERVER_ADDR_ENV: &str = "SIMCTL_CHILD_DEVSERVER_ADDR"; /// Get the address of the devserver pub fn devserver_addr() -> Option { @@ -10,8 +13,6 @@ pub fn devserver_addr() -> Option { .and_then(|s| s.parse().ok()) } -pub const FULLSTACK_ADDRESS_ENV: &str = "DIOXUS_FULLSTACK_ADDRESS"; - pub fn fullstack_address() -> Option { std::env::var(FULLSTACK_ADDRESS_ENV) .ok() From dc687040fd941ffd66993d6f3d73cfa58c29f654 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 22 Aug 2024 19:11:27 -0700 Subject: [PATCH 052/139] clean up cli, inline structs, cut down number of unique types where possible --- Cargo.lock | 3 +- packages/cli/Cargo.toml | 1 + packages/cli/src/assets/manifest.rs | 55 +++--- packages/cli/src/builder/assets.rs | 32 ++-- packages/cli/src/builder/cargo.rs | 47 ++--- packages/cli/src/builder/fullstack.rs | 30 +++- packages/cli/src/builder/mod.rs | 130 +++++++++----- packages/cli/src/builder/platform.rs | 11 ++ packages/cli/src/builder/prepare_html.rs | 53 ++---- packages/cli/src/builder/progress.rs | 211 ++++++++++++----------- packages/cli/src/builder/web.rs | 200 ++++++++++----------- packages/cli/src/cli/build.rs | 7 +- packages/cli/src/cli/bundle.rs | 3 +- packages/cli/src/config.rs | 1 + packages/cli/src/config/app.rs | 194 --------------------- packages/cli/src/config/desktop.rs | 17 ++ packages/cli/src/config/dioxus_config.rs | 1 - packages/cli/src/config/platform.rs | 6 +- packages/cli/src/config/serve.rs | 53 +----- packages/cli/src/config/web.rs | 180 +++++++++++++++++++ packages/cli/src/dioxus_crate.rs | 1 + packages/cli/src/serve/builder.rs | 79 ++++----- packages/cli/src/serve/mod.rs | 200 +++++++++++---------- packages/cli/src/serve/output.rs | 8 +- packages/cli/src/serve/server.rs | 17 +- packages/cli/src/serve/update.rs | 34 ++++ packages/cli/src/serve/watcher.rs | 20 ++- packages/desktop/src/app.rs | 2 +- packages/devtools/src/lib.rs | 2 - packages/fullstack/Cargo.toml | 3 +- packages/fullstack/src/launch.rs | 15 +- packages/liveview/src/pool.rs | 2 +- packages/runtime-config/src/lib.rs | 10 +- 33 files changed, 844 insertions(+), 784 deletions(-) create mode 100644 packages/cli/src/serve/update.rs diff --git a/Cargo.lock b/Cargo.lock index 17687c4fb9..6aa8c22153 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2428,6 +2428,7 @@ dependencies = [ "tower-http", "tracing", "tracing-subscriber", + "uuid", "walkdir", "wasm-bindgen-cli-support", "wasm-bindgen-shared", @@ -2617,7 +2618,6 @@ dependencies = [ "base64 0.22.1", "bytes", "ciborium", - "clap", "dioxus", "dioxus-core-types", "dioxus-desktop", @@ -2627,6 +2627,7 @@ dependencies = [ "dioxus-isrg", "dioxus-lib", "dioxus-mobile", + "dioxus-runtime-config", "dioxus-ssr", "dioxus-web", "dioxus_server_macro", diff --git a/packages/cli/Cargo.toml b/packages/cli/Cargo.toml index 13bbe04b14..d8e4c277ba 100644 --- a/packages/cli/Cargo.toml +++ b/packages/cli/Cargo.toml @@ -17,6 +17,7 @@ wasm-bindgen-cli-support = "0.2" wasm-bindgen-shared = "0.2" # features +uuid = { version = "1.3.0", features = ["v4"] } log = "0.4.14" serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } diff --git a/packages/cli/src/assets/manifest.rs b/packages/cli/src/assets/manifest.rs index 4be9ddb278..c40582d32b 100644 --- a/packages/cli/src/assets/manifest.rs +++ b/packages/cli/src/assets/manifest.rs @@ -1,7 +1,7 @@ use manganis_core::LinkSection; -use object::{File, Object, ObjectSection}; -use std::fs; +use object::{Object, ObjectSection}; use std::path::PathBuf; +use std::{collections::HashMap, fs}; // pub use railwind::warning::Warning as TailwindWarning; // use crate::{file::process_file, process_folder}; @@ -9,26 +9,33 @@ use std::path::PathBuf; // get the text containing all the asset descriptions // in the "link section" of the binary -fn get_string_manganis(file: &File) -> Option { +fn get_string_manganis(file: &object::File) -> Option { for section in file.sections() { - if let Ok(section_name) = section.name() { - // Check if the link section matches the asset section for one of the platforms we support. This may not be the current platform if the user is cross compiling - if LinkSection::ALL - .iter() - .any(|x| x.link_section == section_name) - { - let bytes = section.uncompressed_data().ok()?; - // Some platforms (e.g. macOS) start the manganis section with a null byte, we need to filter that out before we deserialize the JSON - return Some( - std::str::from_utf8(&bytes) - .ok()? - .chars() - .filter(|c| !c.is_control()) - .collect::(), - ); - } + let Ok(section_name) = section.name() else { + continue; + }; + + // Check if the link section matches the asset section for one of the platforms we support. This may not be the current platform if the user is cross compiling + let matches = LinkSection::ALL + .iter() + .any(|x| x.link_section == section_name); + + if !matches { + continue; } + + let bytes = section.uncompressed_data().ok()?; + + // Some platforms (e.g. macOS) start the manganis section with a null byte, we need to filter that out before we deserialize the JSON + return Some( + std::str::from_utf8(&bytes) + .ok()? + .chars() + .filter(|c| !c.is_control()) + .collect::(), + ); } + None } @@ -36,6 +43,7 @@ fn get_string_manganis(file: &File) -> Option { #[derive(Debug, PartialEq, Default, Clone)] pub struct AssetManifest { pub(crate) assets: Vec, + pub(crate) asset_map: HashMap, } #[derive(Debug, PartialEq, Clone)] @@ -47,7 +55,14 @@ pub enum AssetType { impl AssetManifest { /// Creates a new asset manifest pub fn new(assets: Vec) -> Self { - Self { assets } + let mut asset_map = HashMap::new(); + for asset in assets.iter() { + match asset { + AssetType::File(path) => asset_map.insert(path.clone(), asset.clone()), + AssetType::Folder(path) => asset_map.insert(path.clone(), asset.clone()), + }; + } + Self { assets, asset_map } } /// Returns all assets collected from dependencies diff --git a/packages/cli/src/builder/assets.rs b/packages/cli/src/builder/assets.rs index 53403c4af6..ff02a3eef9 100644 --- a/packages/cli/src/builder/assets.rs +++ b/packages/cli/src/builder/assets.rs @@ -1,11 +1,6 @@ -use super::web::install_web_build_tooling; use super::BuildRequest; use super::TargetPlatform; -// use crate::assets::create_assets_head; -// use crate::assets::{asset_manifest, process_assets}; use crate::assets::{copy_dir_to, AssetManifest}; -// use crate::assets::{copy_dir_to, AssetManifest}; -use crate::builder::progress::build_cargo; use crate::builder::progress::CargoBuildResult; use crate::builder::progress::Stage; use crate::builder::progress::UpdateBuildProgress; @@ -23,19 +18,20 @@ type ProgressChannel = UnboundedSender; impl BuildRequest { pub async fn collect_assets( - &self, + &mut self, cargo_args: Vec, - progress: &mut UnboundedSender, ) -> anyhow::Result> { - todo!("collect assets is disabled currently") - // // If this is the server build, the client build already copied any assets we need - // if self.target_platform == TargetPlatform::Server { - // return Ok(None); - // } - // // If assets are skipped, we don't need to collect them - // if self.build_arguments.skip_assets { - // return Ok(None); - // } + // If this is the server build, the client build already copied any assets we need + if self.target_platform == TargetPlatform::Server { + return Ok(None); + } + + // If assets are skipped, we don't need to collect them + if self.build_arguments.skip_assets { + return Ok(None); + } + + Ok(None) // // Start Manganis linker intercept. // let linker_args = vec![format!("{}", self.target_out_dir().display())]; @@ -65,13 +61,13 @@ impl BuildRequest { pub fn copy_assets_dir(&self) -> anyhow::Result<()> { tracing::info!("Copying public assets to the output directory..."); let out_dir = self.target_out_dir(); - let asset_dir = self.dioxus_crate.asset_dir(); + let asset_dir = self.krate.asset_dir(); if asset_dir.is_dir() { // Only pre-compress the assets from the web build. Desktop assets are not served, so they don't need to be pre_compressed let pre_compress = self.targeting_web() && self - .dioxus_crate + .krate .should_pre_compress_web_assets(self.build_arguments.release); copy_dir_to(asset_dir, out_dir, pre_compress)?; diff --git a/packages/cli/src/builder/cargo.rs b/packages/cli/src/builder/cargo.rs index 3b0b9aaf55..05b196ef24 100644 --- a/packages/cli/src/builder/cargo.rs +++ b/packages/cli/src/builder/cargo.rs @@ -1,26 +1,16 @@ -use super::web::install_web_build_tooling; use super::BuildRequest; use super::TargetPlatform; -// use crate::assets::create_assets_head; -// use crate::assets::{asset_manifest, process_assets}; -use crate::assets::AssetManifest; -// use crate::assets::{copy_dir_to, AssetManifest}; -use crate::builder::progress::build_cargo; use crate::builder::progress::CargoBuildResult; use crate::builder::progress::Stage; use crate::builder::progress::UpdateBuildProgress; use crate::builder::progress::UpdateStage; use crate::config::Platform; -use crate::link::LinkCommand; use crate::Result; use anyhow::Context; -use futures_channel::mpsc::UnboundedSender; use std::fs::create_dir_all; use std::path::PathBuf; use tokio::process::Command; -type ProgressChannel = UnboundedSender; - impl BuildRequest { /// Create a list of arguments for cargo builds pub(crate) fn build_arguments(&self) -> Vec { @@ -62,7 +52,7 @@ impl BuildRequest { cargo_args.append(&mut self.build_arguments.cargo_args.clone()); - match self.dioxus_crate.executable_type() { + match self.krate.executable_type() { krates::cm::TargetKind::Bin => { cargo_args.push("--bin".to_string()); } @@ -74,7 +64,7 @@ impl BuildRequest { } _ => {} }; - cargo_args.push(self.dioxus_crate.executable_name().to_string()); + cargo_args.push(self.krate.executable_name().to_string()); cargo_args } @@ -86,7 +76,7 @@ impl BuildRequest { if let Some(target_dir) = &self.target_dir { cmd.env("CARGO_TARGET_DIR", target_dir); } - cmd.current_dir(self.dioxus_crate.crate_dir()) + cmd.current_dir(self.krate.crate_dir()) .arg("--message-format") .arg("json-diagnostic-rendered-ansi"); @@ -98,7 +88,7 @@ impl BuildRequest { Ok((cmd, cargo_args)) } - pub(crate) async fn build(self, mut progress: ProgressChannel) -> Result { + pub(crate) async fn build(mut self) -> Result { tracing::info!("🚅 Running build [Desktop] command..."); // Set up runtime guards @@ -110,7 +100,7 @@ impl BuildRequest { // If this is a web, build make sure we have the web build tooling set up if self.targeting_web() { - install_web_build_tooling(&mut progress).await?; + self.install_web_build_tooling().await?; } // Create the build command @@ -118,19 +108,17 @@ impl BuildRequest { // Run the build command with a pretty loader let crate_count = self.get_unit_count_estimate().await; - let cargo_result = build_cargo(crate_count, cmd, &mut progress).await?; + let cargo_result = self.build_cargo(crate_count, cmd).await?; // Post process the build result - self.post_process_build(cargo_args, &cargo_result, &mut progress) + self.post_process_build(cargo_args, &cargo_result) .await .context("Failed to post process build")?; - tracing::info!( - "🚩 Build completed: [{}]", - self.dioxus_crate.out_dir().display() - ); + tracing::info!("🚩 Build completed: [{}]", self.krate.out_dir().display()); - _ = progress.start_send(UpdateBuildProgress { + _ = self.progress.start_send(UpdateBuildProgress { + platform: self.target_platform, stage: Stage::Finished, update: UpdateStage::Start, }); @@ -139,19 +127,19 @@ impl BuildRequest { } async fn post_process_build( - &self, + &mut self, cargo_args: Vec, cargo_build_result: &CargoBuildResult, - progress: &mut UnboundedSender, ) -> Result<()> { - _ = progress.start_send(UpdateBuildProgress { + _ = self.progress.start_send(UpdateBuildProgress { stage: Stage::OptimizingAssets, update: UpdateStage::Start, + platform: self.target_platform, }); - let assets = self.collect_assets(cargo_args, progress).await?; + self.collect_assets(cargo_args).await?; - let file_name = self.dioxus_crate.executable_name(); + let file_name = self.krate.executable_name(); // Move the final output executable into the dist folder let out_dir = self.target_out_dir(); @@ -177,8 +165,7 @@ impl BuildRequest { // If this is a web build, run web post processing steps if self.targeting_web() { - self.post_process_web_build(assets.as_ref(), progress) - .await?; + self.post_process_web_build().await?; } Ok(()) @@ -186,7 +173,7 @@ impl BuildRequest { /// Get the output directory for a specific built target pub fn target_out_dir(&self) -> PathBuf { - let out_dir = self.dioxus_crate.out_dir(); + let out_dir = self.krate.out_dir(); match self.build_arguments.platform { Some(Platform::Fullstack | Platform::StaticGeneration) => match self.target_platform { TargetPlatform::Web => out_dir.join("public"), diff --git a/packages/cli/src/builder/fullstack.rs b/packages/cli/src/builder/fullstack.rs index 443e928d9a..0c05c3e5e6 100644 --- a/packages/cli/src/builder/fullstack.rs +++ b/packages/cli/src/builder/fullstack.rs @@ -1,3 +1,4 @@ +use futures_channel::mpsc::UnboundedSender; use toml_edit::Item; use crate::builder::Build; @@ -6,19 +7,20 @@ use crate::dioxus_crate::DioxusCrate; use crate::builder::BuildRequest; use std::io::Write; -use super::{BuildReason, TargetPlatform}; +use super::{BuildReason, TargetPlatform, UpdateBuildProgress}; impl BuildRequest { pub(crate) fn new_fullstack( config: DioxusCrate, build_arguments: Build, serve: BuildReason, + progress: UnboundedSender, ) -> Result, crate::Error> { initialize_profiles(&config)?; Ok(vec![ - Self::new_client(serve, &config, &build_arguments), - Self::new_server(serve, &config, &build_arguments), + Self::new_client(serve, &config, &build_arguments, progress.clone()), + Self::new_server(serve, &config, &build_arguments, progress), ]) } @@ -28,9 +30,11 @@ impl BuildRequest { build: &Build, feature: Option, target_platform: TargetPlatform, + progress: UnboundedSender, ) -> Self { let config = config.clone(); let mut build = build.clone(); + // Add the server feature to the features we pass to the build if let Some(feature) = feature { build.target_args.features.push(feature); @@ -40,15 +44,22 @@ impl BuildRequest { Self { reason: serve, build_arguments: build.clone(), - dioxus_crate: config, + krate: config, rust_flags: Default::default(), target_dir: None, target_platform, executable: None, + assets: Default::default(), + progress, } } - fn new_server(serve: BuildReason, config: &DioxusCrate, build: &Build) -> Self { + fn new_server( + serve: BuildReason, + config: &DioxusCrate, + build: &Build, + progress: UnboundedSender, + ) -> Self { let mut build = build.clone(); if build.profile.is_none() { build.profile = Some(CLIENT_PROFILE.to_string()); @@ -60,10 +71,16 @@ impl BuildRequest { &build, build.target_args.server_feature.clone().or(client_feature), TargetPlatform::Server, + progress, ) } - fn new_client(serve: BuildReason, config: &DioxusCrate, build: &Build) -> Self { + fn new_client( + serve: BuildReason, + config: &DioxusCrate, + build: &Build, + progress: UnboundedSender, + ) -> Self { let mut build = build.clone(); if build.profile.is_none() { build.profile = Some(SERVER_PROFILE.to_string()); @@ -75,6 +92,7 @@ impl BuildRequest { &build, build.target_args.client_feature.clone().or(client_feature), client_platform, + progress, ) } } diff --git a/packages/cli/src/builder/mod.rs b/packages/cli/src/builder/mod.rs index 3e71c765a4..08f1315b57 100644 --- a/packages/cli/src/builder/mod.rs +++ b/packages/cli/src/builder/mod.rs @@ -1,9 +1,11 @@ -use crate::dioxus_crate::DioxusCrate; use crate::Result; +use crate::{assets::AssetManifest, dioxus_crate::DioxusCrate}; use crate::{build::Build, config}; use crate::{cli::serve::ServeArguments, config::Platform}; +use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender}; use futures_util::stream::select_all; use futures_util::StreamExt; +pub use platform::TargetArch; pub use platform::TargetPlatform; use std::{net::SocketAddr, path::Path}; use std::{path::PathBuf, process::Stdio}; @@ -32,7 +34,7 @@ pub struct BuildRequest { pub reason: BuildReason, /// The configuration for the crate we are building - pub dioxus_crate: DioxusCrate, + pub krate: DioxusCrate, /// The target platform for the build pub target_platform: TargetPlatform, @@ -48,6 +50,12 @@ pub struct BuildRequest { /// The output executable location pub executable: Option, + + /// The assets manifest - starts empty and will be populated as we go + pub assets: AssetManifest, + + /// Status channel to send our progress updates to + pub progress: UnboundedSender, } /// The reason for the build - this will determine how we prep the output @@ -63,20 +71,23 @@ impl BuildRequest { serve: BuildReason, dioxus_crate: &DioxusCrate, build_arguments: impl Into, + progress: UnboundedSender, ) -> crate::Result> { - let build_arguments = build_arguments.into(); + let build_arguments: Build = build_arguments.into(); let platform = build_arguments.platform(); let single_platform = |platform| { let dioxus_crate = dioxus_crate.clone(); let request = Self { reason: serve, - dioxus_crate, + krate: dioxus_crate, build_arguments: build_arguments.clone(), target_platform: platform, rust_flags: Default::default(), target_dir: Default::default(), executable: Default::default(), + assets: Default::default(), + progress: progress.clone(), }; vec![request] @@ -87,7 +98,7 @@ impl BuildRequest { Platform::Web => single_platform(TargetPlatform::Web), Platform::Desktop => single_platform(TargetPlatform::Desktop), Platform::StaticGeneration | Platform::Fullstack => { - Self::new_fullstack(dioxus_crate.clone(), build_arguments, serve)? + Self::new_fullstack(dioxus_crate.clone(), build_arguments, serve, progress)? } _ => unimplemented!("Unknown platform: {platform:?}"), }) @@ -95,44 +106,33 @@ impl BuildRequest { pub(crate) async fn build_all_parallel( build_requests: Vec, + mut rx: UnboundedReceiver, ) -> Result> { let multi_platform_build = build_requests.len() > 1; - let mut build_progress = Vec::new(); let mut set = tokio::task::JoinSet::new(); + for build_request in build_requests { - let (tx, rx) = futures_channel::mpsc::unbounded(); - build_progress.push((build_request.build_arguments.platform(), rx)); - set.spawn(async move { build_request.build(tx).await }); + set.spawn(async move { build_request.build().await }); } // Watch the build progress as it comes in - loop { - let mut next = select_all( - build_progress - .iter_mut() - .map(|(platform, rx)| rx.map(move |update| (*platform, update))), - ); - match next.next().await { - Some((platform, update)) => { - if multi_platform_build { - print!("{platform} build: "); - update.to_std_out(); - } else { - update.to_std_out(); - } - } - None => { - break; - } + while let Some(update) = rx.next().await { + if multi_platform_build { + let platform = update.platform; + print!("{platform} build: "); + update.to_std_out(); + } else { + update.to_std_out(); } } let mut all_results = Vec::new(); while let Some(result) = set.join_next().await { - let result = result - .map_err(|_| crate::Error::Unique("Failed to build project".to_owned()))??; - all_results.push(result); + all_results.push( + result + .map_err(|_| crate::Error::Unique("Failed to build project".to_owned()))??, + ); } Ok(all_results) @@ -146,19 +146,27 @@ impl BuildRequest { /// Open the executable if this is a native build pub fn open( &self, - config: &DioxusCrate, serve: &ServeArguments, fullstack_address: Option, - workspace: &Path, ) -> std::io::Result> { - if self.target_platform == TargetPlatform::Web { - return Ok(None); - } - if self.target_platform == TargetPlatform::Server { tracing::trace!("Proxying fullstack server from port {fullstack_address:?}"); } + match self.target_platform { + TargetPlatform::Web => Ok(None), + TargetPlatform::Mobile => self.open_bundled_ios_app(serve), + TargetPlatform::Desktop | TargetPlatform::Server | TargetPlatform::Liveview => { + self.open_unbundled_native_app(serve, fullstack_address) + } + } + } + + fn open_unbundled_native_app( + &self, + serve: &ServeArguments, + fullstack_address: Option, + ) -> std::io::Result> { // // open the exe with some arguments/envvars/etc // we're going to try and configure this binary from the environment, if we can @@ -180,20 +188,64 @@ impl BuildRequest { .unwrap_or_else(|| "127.0.0.1:8080".to_string()), ) .env( - dioxus_runtime_config::DEVSERVER_ADDR_ENV, + dioxus_runtime_config::RAW_DEVSERVER_ADDR_ENV, serve.address.address().to_string(), ) .env( dioxus_runtime_config::IOS_DEVSERVER_ADDR_ENV, serve.address.address().to_string(), ) - .env("CARGO_MANIFEST_DIR", config.crate_dir()) + .env("CARGO_MANIFEST_DIR", self.krate.crate_dir()) .stderr(Stdio::piped()) .stdout(Stdio::piped()) .kill_on_drop(true) - .current_dir(workspace) + .current_dir(self.krate.workspace_dir()) .spawn()?; Ok(Some(res)) } + + fn open_bundled_ios_app(&self, serve: &ServeArguments) -> std::io::Result> { + // command = "xcrun" + // args = [ + // "simctl", + // "install", + // "booted", + // "target/aarch64-apple-ios-sim/debug/bundle/ios/DioxusApp.app", + // ] + + // [tasks.run_ios_sim] + // args = ["simctl", "launch", "--console", "booted", "com.dioxuslabs"] + // command = "xcrun" + // dependencies = ["build_ios_sim", "install_ios_sim"] + + // [tasks.serve-sim] + // dependencies = ["build_ios_sim", "install_ios_sim", "run_ios_sim"] + + // APP_PATH="target/aarch64-apple-ios/debug/bundle/ios/DioxusApp.app" + + // # get the device id by jq-ing the json of the device list + // xcrun devicectl list devices --json-output target/deviceid.json + // DEVICE_UUID=$(jq -r '.result.devices[0].identifier' target/deviceid.json) + + // xcrun devicectl device install app --device "${DEVICE_UUID}" "${APP_PATH}" --json-output target/xcrun.json + + // # get the installation url by jq-ing the json of the device install + // INSTALLATION_URL=$(jq -r '.result.installedApplications[0].installationURL' target/xcrun.json) + + // # launch the app + // # todo: we can just background it immediately and then pick it up for loading its logs + // xcrun devicectl device process launch --device "${DEVICE_UUID}" "${INSTALLATION_URL}" + + // # # launch the app and put it in background + // # xcrun devicectl device process launch --no-activate --verbose --device "${DEVICE_UUID}" "${INSTALLATION_URL}" --json-output "${XCRUN_DEVICE_PROCESS_LAUNCH_LOG_DIR}" + + // # # Extract background PID of status app + // # STATUS_PID=$(jq -r '.result.process.processIdentifier' "${XCRUN_DEVICE_PROCESS_LAUNCH_LOG_DIR}") + // # "${GIT_ROOT}/scripts/wait-for-metro-port.sh" 2>&1 + + // # # now that metro is ready, resume the app from background + // # xcrun devicectl device process resume --device "${DEVICE_UUID}" --pid "${STATUS_PID}" > "${XCRUN_DEVICE_PROCESS_RESUME_LOG_DIR}" 2>&1 + todo!("Open mobile apps") + } } diff --git a/packages/cli/src/builder/platform.rs b/packages/cli/src/builder/platform.rs index 8b4f6f8832..3f820f02ba 100644 --- a/packages/cli/src/builder/platform.rs +++ b/packages/cli/src/builder/platform.rs @@ -8,9 +8,19 @@ pub enum TargetPlatform { Web, Desktop, Server, + Mobile, Liveview, } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum TargetArch { + Linux, + Mac, + Windows, + Ios, + Android, +} + impl FromStr for TargetPlatform { type Err = (); @@ -32,6 +42,7 @@ impl std::fmt::Display for TargetPlatform { TargetPlatform::Desktop => write!(f, "desktop"), TargetPlatform::Server => write!(f, "server"), TargetPlatform::Liveview => write!(f, "liveview"), + TargetPlatform::Mobile => write!(f, "ios"), } } } diff --git a/packages/cli/src/builder/prepare_html.rs b/packages/cli/src/builder/prepare_html.rs index 593a05cdf3..95d9119c7d 100644 --- a/packages/cli/src/builder/prepare_html.rs +++ b/packages/cli/src/builder/prepare_html.rs @@ -1,10 +1,9 @@ //! Build the HTML file to load a web application. The index.html file may be created from scratch or modified from the `index.html` file in the crate root. use super::{BuildReason, BuildRequest, UpdateBuildProgress}; +use crate::builder::progress::MessageSource; use crate::builder::Stage; use crate::Result; -use crate::{assets::AssetManifest, builder::progress::MessageSource}; -use futures_channel::mpsc::UnboundedSender; use std::fmt::Write; use std::path::{Path, PathBuf}; @@ -14,15 +13,11 @@ const DEFAULT_HTML: &str = include_str!("../../assets/index.html"); const TOAST_HTML: &str = include_str!("../../assets/toast.html"); impl BuildRequest { - pub(crate) fn prepare_html( - &self, - assets: Option<&AssetManifest>, - progress: &mut UnboundedSender, - ) -> Result { - let mut html = html_or_default(&self.dioxus_crate.crate_dir()); + pub(crate) fn prepare_html(&mut self) -> Result { + let mut html = html_or_default(&self.krate.crate_dir()); // Inject any resources from the config into the html - self.inject_resources(&mut html, assets, progress)?; + self.inject_resources(&mut html)?; // Inject loading scripts if they are not already present self.inject_loading_scripts(&mut html); @@ -30,7 +25,7 @@ impl BuildRequest { // Replace any special placeholders in the HTML with resolved values self.replace_template_placeholders(&mut html); - let title = self.dioxus_crate.dioxus_config.web.app.title.clone(); + let title = self.krate.dioxus_config.web.app.title.clone(); replace_or_insert_before("{app_title}", ", - progress: &mut UnboundedSender, - ) -> Result<()> { + fn inject_resources(&mut self, html: &mut String) -> Result<()> { // Collect all resources into a list of styles and scripts - let resources = &self.dioxus_crate.dioxus_config.web.resource; + let resources = &self.krate.dioxus_config.web.resource; let mut style_list = resources.style.clone().unwrap_or_default(); let mut script_list = resources.script.clone().unwrap_or_default(); @@ -79,10 +69,10 @@ impl BuildRequest { } if !style_list.is_empty() { - self.send_resource_deprecation_warning(progress, style_list, ResourceType::Style); + self.send_resource_deprecation_warning(style_list, ResourceType::Style); } if !script_list.is_empty() { - self.send_resource_deprecation_warning(progress, script_list, ResourceType::Script); + self.send_resource_deprecation_warning(script_list, ResourceType::Script); } // Inject any resources from manganis into the head @@ -96,7 +86,7 @@ impl BuildRequest { } /// Inject loading scripts if they are not already present - fn inject_loading_scripts(&self, html: &mut String) { + fn inject_loading_scripts(&mut self, html: &mut String) { // If it looks like we are already loading wasm or the current build opted out of injecting loading scripts, don't inject anything if !self.build_arguments.inject_loading_scripts || html.contains("__wbindgen_start") { return; @@ -138,19 +128,14 @@ impl BuildRequest { /// Replace any special placeholders in the HTML with resolved values fn replace_template_placeholders(&self, html: &mut String) { - let base_path = self.dioxus_crate.dioxus_config.web.app.base_path(); + let base_path = self.krate.dioxus_config.web.app.base_path(); *html = html.replace("{base_path}", base_path); - let app_name = &self.dioxus_crate.dioxus_config.application.name; + let app_name = &self.krate.dioxus_config.application.name; *html = html.replace("{app_name}", app_name); } - fn send_resource_deprecation_warning( - &self, - progress: &mut UnboundedSender, - paths: Vec, - variant: ResourceType, - ) { + fn send_resource_deprecation_warning(&mut self, paths: Vec, variant: ResourceType) { const RESOURCE_DEPRECATION_MESSAGE: &str = r#"The `web.resource` config has been deprecated in favor of head components and will be removed in a future release. Instead of including assets in the config, you can include assets with the `asset!` macro and add them to the head with `document::Link` and `Script` components."#; let replacement_components = paths @@ -162,10 +147,9 @@ impl BuildRequest { // If the path is absolute, make it relative to the current directory before we join it // The path is actually a web path which is relative to the root of the website let path = path.strip_prefix("/").unwrap_or(path); - let asset_dir_path = self.dioxus_crate.asset_dir().join(path); + let asset_dir_path = self.krate.asset_dir().join(path); if let Ok(absolute_path) = asset_dir_path.canonicalize() { - let absolute_crate_root = - self.dioxus_crate.crate_dir().canonicalize().unwrap(); + let absolute_crate_root = self.krate.crate_dir().canonicalize().unwrap(); PathBuf::from("./") .join(absolute_path.strip_prefix(absolute_crate_root).unwrap()) } else { @@ -189,10 +173,11 @@ impl BuildRequest { }; let message = format!( - "{RESOURCE_DEPRECATION_MESSAGE}\nTo migrate to head components, remove `{section_name}` and include the following rsx in your root component:\n```rust\n{replacement_components}\n```" - ); + "{RESOURCE_DEPRECATION_MESSAGE}\nTo migrate to head components, remove `{section_name}` and include the following rsx in your root component:\n```rust\n{replacement_components}\n```" + ); - _ = progress.unbounded_send(UpdateBuildProgress { + _ = self.progress.unbounded_send(UpdateBuildProgress { + platform: self.target_platform, stage: Stage::OptimizingWasm, update: super::UpdateStage::AddMessage(super::BuildMessage { level: Level::WARN, diff --git a/packages/cli/src/builder/progress.rs b/packages/cli/src/builder/progress.rs index e5f3ae73b9..9c9acb4f97 100644 --- a/packages/cli/src/builder/progress.rs +++ b/packages/cli/src/builder/progress.rs @@ -1,7 +1,6 @@ //! Report progress about the build to the user. We use channels to report progress back to the CLI. use anyhow::Context; use cargo_metadata::{diagnostic::Diagnostic, Message}; -use futures_channel::mpsc::UnboundedSender; use serde::Deserialize; use std::fmt::Display; use std::ops::Deref; @@ -10,7 +9,7 @@ use std::process::Stdio; use tokio::io::AsyncBufReadExt; use tracing::Level; -use super::BuildRequest; +use super::{BuildRequest, TargetPlatform}; #[derive(Default, Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Copy)] pub enum Stage { @@ -48,6 +47,7 @@ impl std::fmt::Display for Stage { pub struct UpdateBuildProgress { pub stage: Stage, pub update: UpdateStage, + pub platform: TargetPlatform, } impl UpdateBuildProgress { @@ -139,120 +139,125 @@ impl From for BuildMessage { } } -pub(crate) async fn build_cargo( - crate_count: usize, - mut cmd: tokio::process::Command, - progress: &mut UnboundedSender, -) -> anyhow::Result { - _ = progress.start_send(UpdateBuildProgress { - stage: Stage::Compiling, - update: UpdateStage::Start, - }); +pub(crate) struct CargoBuildResult { + pub(crate) output_location: Option, +} - let mut child = cmd - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .spawn() - .context("Failed to spawn cargo build")?; - let stdout = child.stdout.take().unwrap(); - let stderr = child.stderr.take().unwrap(); - let stdout = tokio::io::BufReader::new(stdout); - let stderr = tokio::io::BufReader::new(stderr); - let mut output_location = None; +impl BuildRequest { + pub(crate) async fn build_cargo( + &mut self, + crate_count: usize, + mut cmd: tokio::process::Command, + ) -> anyhow::Result { + _ = self.progress.start_send(UpdateBuildProgress { + stage: Stage::Compiling, + update: UpdateStage::Start, + platform: self.target_platform, + }); - let mut stdout = stdout.lines(); - let mut stderr = stderr.lines(); - let mut units_compiled = 0; - let mut errors = Vec::new(); - loop { - let line = tokio::select! { - line = stdout.next_line() => { - line - } - line = stderr.next_line() => { - line - } - }; - let Some(line) = line? else { - break; - }; - let mut deserializer = serde_json::Deserializer::from_str(line.trim()); - deserializer.disable_recursion_limit(); + let mut child = cmd + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .context("Failed to spawn cargo build")?; + let stdout = child.stdout.take().unwrap(); + let stderr = child.stderr.take().unwrap(); + let stdout = tokio::io::BufReader::new(stdout); + let stderr = tokio::io::BufReader::new(stderr); + let mut output_location = None; - let message = Message::deserialize(&mut deserializer).unwrap_or(Message::TextLine(line)); - match message { - Message::CompilerMessage(msg) => { - let message = msg.message; - _ = progress.start_send(UpdateBuildProgress { - stage: Stage::Compiling, - update: UpdateStage::AddMessage(message.clone().into()), - }); - const WARNING_LEVELS: &[cargo_metadata::diagnostic::DiagnosticLevel] = &[ - cargo_metadata::diagnostic::DiagnosticLevel::Help, - cargo_metadata::diagnostic::DiagnosticLevel::Note, - cargo_metadata::diagnostic::DiagnosticLevel::Warning, - cargo_metadata::diagnostic::DiagnosticLevel::Error, - cargo_metadata::diagnostic::DiagnosticLevel::FailureNote, - cargo_metadata::diagnostic::DiagnosticLevel::Ice, - ]; - const FATAL_LEVELS: &[cargo_metadata::diagnostic::DiagnosticLevel] = &[ - cargo_metadata::diagnostic::DiagnosticLevel::Error, - cargo_metadata::diagnostic::DiagnosticLevel::FailureNote, - cargo_metadata::diagnostic::DiagnosticLevel::Ice, - ]; - if WARNING_LEVELS.contains(&message.level) { - if let Some(rendered) = message.rendered { - errors.push(rendered); + let mut stdout = stdout.lines(); + let mut stderr = stderr.lines(); + let mut units_compiled = 0; + let mut errors = Vec::new(); + loop { + let line = tokio::select! { + line = stdout.next_line() => { + line + } + line = stderr.next_line() => { + line + } + }; + let Some(line) = line? else { + break; + }; + let mut deserializer = serde_json::Deserializer::from_str(line.trim()); + deserializer.disable_recursion_limit(); + + let message = + Message::deserialize(&mut deserializer).unwrap_or(Message::TextLine(line)); + match message { + Message::CompilerMessage(msg) => { + let message = msg.message; + _ = self.progress.start_send(UpdateBuildProgress { + stage: Stage::Compiling, + update: UpdateStage::AddMessage(message.clone().into()), + platform: self.target_platform, + }); + const WARNING_LEVELS: &[cargo_metadata::diagnostic::DiagnosticLevel] = &[ + cargo_metadata::diagnostic::DiagnosticLevel::Help, + cargo_metadata::diagnostic::DiagnosticLevel::Note, + cargo_metadata::diagnostic::DiagnosticLevel::Warning, + cargo_metadata::diagnostic::DiagnosticLevel::Error, + cargo_metadata::diagnostic::DiagnosticLevel::FailureNote, + cargo_metadata::diagnostic::DiagnosticLevel::Ice, + ]; + const FATAL_LEVELS: &[cargo_metadata::diagnostic::DiagnosticLevel] = &[ + cargo_metadata::diagnostic::DiagnosticLevel::Error, + cargo_metadata::diagnostic::DiagnosticLevel::FailureNote, + cargo_metadata::diagnostic::DiagnosticLevel::Ice, + ]; + if WARNING_LEVELS.contains(&message.level) { + if let Some(rendered) = message.rendered { + errors.push(rendered); + } + } + if FATAL_LEVELS.contains(&message.level) { + return Err(anyhow::anyhow!(errors.join("\n"))); } } - if FATAL_LEVELS.contains(&message.level) { - return Err(anyhow::anyhow!(errors.join("\n"))); + Message::CompilerArtifact(artifact) => { + units_compiled += 1; + if let Some(executable) = artifact.executable { + output_location = Some(executable.into()); + } else { + let build_progress = units_compiled as f64 / crate_count as f64; + _ = self.progress.start_send(UpdateBuildProgress { + platform: self.target_platform, + stage: Stage::Compiling, + update: UpdateStage::SetProgress((build_progress).clamp(0.0, 1.00)), + }); + } } - } - Message::CompilerArtifact(artifact) => { - units_compiled += 1; - if let Some(executable) = artifact.executable { - output_location = Some(executable.into()); - } else { - let build_progress = units_compiled as f64 / crate_count as f64; - _ = progress.start_send(UpdateBuildProgress { + Message::BuildScriptExecuted(_) => { + units_compiled += 1; + } + Message::BuildFinished(finished) => { + if !finished.success { + return Err(anyhow::anyhow!("Build failed")); + } + } + Message::TextLine(line) => { + _ = self.progress.start_send(UpdateBuildProgress { + platform: self.target_platform, stage: Stage::Compiling, - update: UpdateStage::SetProgress((build_progress).clamp(0.0, 1.00)), + update: UpdateStage::AddMessage(BuildMessage { + level: Level::DEBUG, + message: MessageType::Text(line), + source: MessageSource::Build, + }), }); } - } - Message::BuildScriptExecuted(_) => { - units_compiled += 1; - } - Message::BuildFinished(finished) => { - if !finished.success { - return Err(anyhow::anyhow!("Build failed")); + _ => { + // Unknown message } } - Message::TextLine(line) => { - _ = progress.start_send(UpdateBuildProgress { - stage: Stage::Compiling, - update: UpdateStage::AddMessage(BuildMessage { - level: Level::DEBUG, - message: MessageType::Text(line), - source: MessageSource::Build, - }), - }); - } - _ => { - // Unknown message - } } - } - Ok(CargoBuildResult { output_location }) -} - -pub(crate) struct CargoBuildResult { - pub(crate) output_location: Option, -} + Ok(CargoBuildResult { output_location }) + } -impl BuildRequest { /// Try to get the unit graph for the crate. This is a nightly only feature which may not be available with the current version of rustc the user has installed. async fn get_unit_count(&self) -> Option { #[derive(Debug, Deserialize)] @@ -291,7 +296,7 @@ impl BuildRequest { self.get_unit_count().await.unwrap_or_else(|| { // Otherwise, use cargo metadata (self - .dioxus_crate + .krate .krates .krates_filtered(krates::DepKind::Dev) .iter() diff --git a/packages/cli/src/builder/web.rs b/packages/cli/src/builder/web.rs index 76d3c86180..66f55660d5 100644 --- a/packages/cli/src/builder/web.rs +++ b/packages/cli/src/builder/web.rs @@ -1,10 +1,9 @@ use super::BuildRequest; -use crate::assets::{pre_compress_folder, AssetManifest}; +use crate::assets::pre_compress_folder; use crate::builder::progress::Stage; use crate::builder::progress::UpdateBuildProgress; use crate::builder::progress::UpdateStage; use crate::error::{Error, Result}; -use futures_channel::mpsc::UnboundedSender; use std::path::Path; use tokio::process::Command; use wasm_bindgen_cli_support::Bindgen; @@ -15,8 +14,9 @@ impl BuildRequest { let input_path = input_path.to_path_buf(); let bindgen_outdir = bindgen_outdir.to_path_buf(); let keep_debug = - self.dioxus_crate.dioxus_config.web.wasm_opt.debug || (!self.build_arguments.release); - let name = self.dioxus_crate.dioxus_config.application.name.clone(); + self.krate.dioxus_config.web.wasm_opt.debug || (!self.build_arguments.release); + let name = self.krate.dioxus_config.application.name.clone(); + let run_wasm_bindgen = move || { // [3] Bindgen the final binary for use easy linking let mut bindgen_builder = Bindgen::new(); @@ -35,6 +35,7 @@ impl BuildRequest { .generate(&bindgen_outdir) .unwrap(); }; + let start = std::time::Instant::now(); let bindgen_result = tokio::task::spawn_blocking(run_wasm_bindgen.clone()).await; @@ -44,7 +45,7 @@ impl BuildRequest { // If we get an error, we can try to recover by pinning the user's wasm-bindgen version to the version we used if let Err(err) = bindgen_result { tracing::error!("Bindgen build failed: {:?}", err); - update_wasm_bindgen_version().await?; + Self::update_wasm_bindgen_version().await?; run_wasm_bindgen(); } @@ -52,14 +53,11 @@ impl BuildRequest { } /// Post process the WASM build artifacts - pub(crate) async fn post_process_web_build( - &self, - assets: Option<&AssetManifest>, - progress: &mut UnboundedSender, - ) -> Result<()> { - _ = progress.start_send(UpdateBuildProgress { + pub(crate) async fn post_process_web_build(&mut self) -> Result<()> { + _ = self.progress.start_send(UpdateBuildProgress { stage: Stage::OptimizingWasm, update: UpdateStage::Start, + platform: self.target_platform, }); // Find the wasm file @@ -74,48 +72,13 @@ impl BuildRequest { // Only run wasm-opt if the feature is enabled // Wasm-opt has an expensive build script that makes it annoying to keep enabled for iterative dev + // We put it behind the "wasm-opt" feature flag so that it can be disabled when iterating on the cli #[cfg(feature = "wasm-opt")] - { - // Run wasm-opt if this is a release build - if self.build_arguments.release { - use dioxus_cli_config::WasmOptLevel; - - tracing::info!("Running optimization with wasm-opt..."); - let mut options = match self.dioxus_crate.dioxus_config.web.wasm_opt.level { - WasmOptLevel::Z => { - wasm_opt::OptimizationOptions::new_optimize_for_size_aggressively() - } - WasmOptLevel::S => wasm_opt::OptimizationOptions::new_optimize_for_size(), - WasmOptLevel::Zero => wasm_opt::OptimizationOptions::new_opt_level_0(), - WasmOptLevel::One => wasm_opt::OptimizationOptions::new_opt_level_1(), - WasmOptLevel::Two => wasm_opt::OptimizationOptions::new_opt_level_2(), - WasmOptLevel::Three => wasm_opt::OptimizationOptions::new_opt_level_3(), - WasmOptLevel::Four => wasm_opt::OptimizationOptions::new_opt_level_4(), - }; - let wasm_file = bindgen_outdir.join(format!( - "{}_bg.wasm", - self.dioxus_crate.dioxus_config.application.name - )); - let old_size = wasm_file.metadata()?.len(); - options - // WASM bindgen relies on reference types - .enable_feature(wasm_opt::Feature::ReferenceTypes) - .debug_info(self.dioxus_crate.dioxus_config.web.wasm_opt.debug) - .run(&wasm_file, &wasm_file) - .map_err(|err| Error::Other(anyhow::anyhow!(err)))?; - let new_size = wasm_file.metadata()?.len(); - tracing::info!( - "wasm-opt reduced WASM size from {} to {} ({:2}%)", - old_size, - new_size, - (new_size as f64 - old_size as f64) / old_size as f64 * 100.0 - ); - } - } + self.run_wasm_opt(&bindgen_outdir)?; // If pre-compressing is enabled, we can pre_compress the wasm-bindgen output let pre_compress = self - .dioxus_crate + .krate .should_pre_compress_web_assets(self.build_arguments.release); tokio::task::spawn_blocking(move || pre_compress_folder(&bindgen_outdir, pre_compress)) @@ -126,68 +89,107 @@ impl BuildRequest { // Note that we do this last since the webserver will attempt to serve the index.html file // If we do this too early, the wasm won't be ready but the index.html will be served, leading // to test failures and broken pages. - let html = self.prepare_html(assets, progress)?; + let html = self.prepare_html()?; let html_path = self.target_out_dir().join("index.html"); std::fs::write(html_path, html)?; Ok(()) } -} -// Attempt to automatically recover from a bindgen failure by updating the wasm-bindgen version -async fn update_wasm_bindgen_version() -> Result<()> { - let cli_bindgen_version = wasm_bindgen_shared::version(); - tracing::info!("Attempting to recover from bindgen failure by setting the wasm-bindgen version to {cli_bindgen_version}..."); - - let output = Command::new("cargo") - .args([ - "update", - "-p", - "wasm-bindgen", - "--precise", - &cli_bindgen_version, - ]) - .output() - .await; - - let mut error_message = None; - if let Ok(output) = output { - if output.status.success() { - tracing::info!("Successfully updated wasm-bindgen to {cli_bindgen_version}"); - return Ok(()); - } else { - error_message = Some(output); + /// Check if the wasm32-unknown-unknown target is installed and try to install it if not + pub(crate) async fn install_web_build_tooling(&mut self) -> Result<()> { + // If the user has rustup, we can check if the wasm32-unknown-unknown target is installed + // Otherwise we can just assume it is installed - which is not great... + // Eventually we can poke at the errors and let the user know they need to install the target + if let Ok(wasm_check_command) = Command::new("rustup").args(["show"]).output().await { + let wasm_check_output = String::from_utf8(wasm_check_command.stdout).unwrap(); + if !wasm_check_output.contains("wasm32-unknown-unknown") { + _ = self.progress.start_send(UpdateBuildProgress { + stage: Stage::InstallingWasmTooling, + update: UpdateStage::Start, + platform: self.target_platform, + }); + tracing::info!("wasm32-unknown-unknown target not detected, installing.."); + let _ = Command::new("rustup") + .args(["target", "add", "wasm32-unknown-unknown"]) + .output() + .await?; + } } - } - if let Some(output) = error_message { - tracing::error!("Failed to update wasm-bindgen: {:#?}", output); + Ok(()) } - Err(Error::BuildFailed(format!("WASM bindgen build failed!\nThis is probably due to the Bindgen version, dioxus-cli is using `{cli_bindgen_version}` which is not compatible with your crate.\nPlease reinstall the dioxus cli to fix this issue.\nYou can reinstall the dioxus cli by running `cargo install dioxus-cli --force` and then rebuild your project"))) -} + // Attempt to automatically recover from a bindgen failure by updating the wasm-bindgen version + pub(crate) async fn update_wasm_bindgen_version() -> Result<()> { + let cli_bindgen_version = wasm_bindgen_shared::version(); + tracing::info!("Attempting to recover from bindgen failure by setting the wasm-bindgen version to {cli_bindgen_version}..."); + + let output = Command::new("cargo") + .args([ + "update", + "-p", + "wasm-bindgen", + "--precise", + &cli_bindgen_version, + ]) + .output() + .await; + + let mut error_message = None; + if let Ok(output) = output { + if output.status.success() { + tracing::info!("Successfully updated wasm-bindgen to {cli_bindgen_version}"); + return Ok(()); + } else { + error_message = Some(output); + } + } -type ProgressChannel = UnboundedSender; - -/// Check if the wasm32-unknown-unknown target is installed and try to install it if not -pub(crate) async fn install_web_build_tooling(progress: &mut ProgressChannel) -> Result<()> { - // If the user has rustup, we can check if the wasm32-unknown-unknown target is installed - // Otherwise we can just assume it is installed - which is not great... - // Eventually we can poke at the errors and let the user know they need to install the target - if let Ok(wasm_check_command) = Command::new("rustup").args(["show"]).output().await { - let wasm_check_output = String::from_utf8(wasm_check_command.stdout).unwrap(); - if !wasm_check_output.contains("wasm32-unknown-unknown") { - _ = progress.start_send(UpdateBuildProgress { - stage: Stage::InstallingWasmTooling, - update: UpdateStage::Start, - }); - tracing::info!("wasm32-unknown-unknown target not detected, installing.."); - let _ = Command::new("rustup") - .args(["target", "add", "wasm32-unknown-unknown"]) - .output() - .await?; + if let Some(output) = error_message { + tracing::error!("Failed to update wasm-bindgen: {:#?}", output); } + + Err(Error::BuildFailed(format!("WASM bindgen build failed!\nThis is probably due to the Bindgen version, dioxus-cli is using `{cli_bindgen_version}` which is not compatible with your crate.\nPlease reinstall the dioxus cli to fix this issue.\nYou can reinstall the dioxus cli by running `cargo install dioxus-cli --force` and then rebuild your project"))) } - Ok(()) + #[cfg(feature = "wasm-opt")] + fn run_wasm_opt(&mut self, bindgen_outdir: &std::path::PathBuf) -> Result<(), Error> { + if !self.build_arguments.release { + return Ok(()); + }; + + use crate::config::WasmOptLevel; + + tracing::info!("Running optimization with wasm-opt..."); + let mut options = match self.dioxus_crate.dioxus_config.web.wasm_opt.level { + WasmOptLevel::Z => wasm_opt::OptimizationOptions::new_optimize_for_size_aggressively(), + WasmOptLevel::S => wasm_opt::OptimizationOptions::new_optimize_for_size(), + WasmOptLevel::Zero => wasm_opt::OptimizationOptions::new_opt_level_0(), + WasmOptLevel::One => wasm_opt::OptimizationOptions::new_opt_level_1(), + WasmOptLevel::Two => wasm_opt::OptimizationOptions::new_opt_level_2(), + WasmOptLevel::Three => wasm_opt::OptimizationOptions::new_opt_level_3(), + WasmOptLevel::Four => wasm_opt::OptimizationOptions::new_opt_level_4(), + }; + let wasm_file = bindgen_outdir.join(format!( + "{}_bg.wasm", + self.dioxus_crate.dioxus_config.application.name + )); + let old_size = wasm_file.metadata()?.len(); + options + // WASM bindgen relies on reference types + .enable_feature(wasm_opt::Feature::ReferenceTypes) + .debug_info(self.dioxus_crate.dioxus_config.web.wasm_opt.debug) + .run(&wasm_file, &wasm_file) + .map_err(|err| Error::Other(anyhow::anyhow!(err)))?; + let new_size = wasm_file.metadata()?.len(); + tracing::info!( + "wasm-opt reduced WASM size from {} to {} ({:2}%)", + old_size, + new_size, + (new_size as f64 - old_size as f64) / old_size as f64 * 100.0 + ); + + Ok(()) + } } diff --git a/packages/cli/src/cli/build.rs b/packages/cli/src/cli/build.rs index b201121fb7..1f81308e98 100644 --- a/packages/cli/src/cli/build.rs +++ b/packages/cli/src/cli/build.rs @@ -113,8 +113,11 @@ impl Build { pub async fn build(&mut self, dioxus_crate: &mut DioxusCrate) -> Result<()> { self.resolve(dioxus_crate)?; - let build_requests = BuildRequest::create(BuildReason::Build, dioxus_crate, self.clone())?; - BuildRequest::build_all_parallel(build_requests).await?; + let (tx, rx) = futures_channel::mpsc::unbounded(); + + let build_requests = + BuildRequest::create(BuildReason::Build, dioxus_crate, self.clone(), tx)?; + BuildRequest::build_all_parallel(build_requests, rx).await?; Ok(()) } diff --git a/packages/cli/src/cli/bundle.rs b/packages/cli/src/cli/bundle.rs index abdbbaf651..87e3864436 100644 --- a/packages/cli/src/cli/bundle.rs +++ b/packages/cli/src/cli/bundle.rs @@ -5,7 +5,7 @@ use std::env::current_dir; use std::fs::create_dir_all; use std::ops::Deref; use std::str::FromStr; -use tauri_bundler::{BundleSettings, PackageSettings, SettingsBuilder}; +use tauri_bundler::{PackageSettings, SettingsBuilder}; use super::*; @@ -15,6 +15,7 @@ use super::*; pub struct Bundle { #[clap(long)] pub package: Option>, + /// The arguments for the dioxus build #[clap(flatten)] pub build_arguments: Build, diff --git a/packages/cli/src/config.rs b/packages/cli/src/config.rs index c569fd5412..549ce285f2 100644 --- a/packages/cli/src/config.rs +++ b/packages/cli/src/config.rs @@ -11,6 +11,7 @@ mod web; pub use app::*; pub use bundle::*; +pub use desktop::*; pub use dioxus_config::*; pub use platform::*; pub use serve::*; diff --git a/packages/cli/src/config/app.rs b/packages/cli/src/config/app.rs index be2d9b9b28..bcb04dcf49 100644 --- a/packages/cli/src/config/app.rs +++ b/packages/cli/src/config/app.rs @@ -35,197 +35,3 @@ pub fn asset_dir_default() -> PathBuf { pub fn out_dir_default() -> PathBuf { PathBuf::from("dist") } - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct WebConfig { - #[serde(default)] - pub app: WebAppConfig, - - #[serde(default)] - pub proxy: Vec, - - #[serde(default)] - pub watcher: WebWatcherConfig, - - #[serde(default)] - pub resource: WebResourceConfig, - - #[serde(default)] - pub https: WebHttpsConfig, - - /// Whether to enable pre-compression of assets and wasm during a web build in release mode - #[serde(default = "true_bool")] - pub pre_compress: bool, - - /// The wasm-opt configuration - #[serde(default)] - pub wasm_opt: WasmOptConfig, -} - -impl Default for WebConfig { - fn default() -> Self { - Self { - pre_compress: true_bool(), - app: Default::default(), - https: Default::default(), - wasm_opt: Default::default(), - proxy: Default::default(), - watcher: Default::default(), - resource: Default::default(), - } - } -} - -/// Represents configuration items for the desktop platform. -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct DesktopConfig { - /// Describes whether a debug-mode desktop app should be always-on-top. - #[serde(default)] - pub always_on_top: bool, -} - -impl Default for DesktopConfig { - fn default() -> Self { - Self { - always_on_top: true, - } - } -} - -/// The wasm-opt configuration -#[derive(Debug, Clone, Serialize, Deserialize, Default)] -pub struct WasmOptConfig { - /// The wasm-opt level to use for release builds [default: s] - /// Options: - /// - z: optimize aggressively for size - /// - s: optimize for size - /// - 1: optimize for speed - /// - 2: optimize for more for speed - /// - 3: optimize for even more for speed - /// - 4: optimize aggressively for speed - #[serde(default)] - pub level: WasmOptLevel, - - /// Keep debug symbols in the wasm file - #[serde(default = "false_bool")] - pub debug: bool, -} - -/// The wasm-opt level to use for release web builds [default: 4] -#[derive(Default, Debug, Copy, Clone, Serialize, Deserialize)] -pub enum WasmOptLevel { - /// Optimize aggressively for size - #[serde(rename = "z")] - Z, - /// Optimize for size - #[serde(rename = "s")] - S, - /// Don't optimize - #[serde(rename = "0")] - Zero, - /// Optimize for speed - #[serde(rename = "1")] - One, - /// Optimize for more for speed - #[serde(rename = "2")] - Two, - /// Optimize for even more for speed - #[serde(rename = "3")] - Three, - /// Optimize aggressively for speed - #[serde(rename = "4")] - #[default] - Four, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct WebAppConfig { - #[serde(default = "default_title")] - pub title: String, - pub base_path: Option, -} - -impl WebAppConfig { - /// Get the normalized base path for the application with `/` trimmed from both ends. If the base path is not set, this will return `.`. - pub fn base_path(&self) -> &str { - match &self.base_path { - Some(path) => path.trim_matches('/'), - None => ".", - } - } -} - -impl Default for WebAppConfig { - fn default() -> Self { - Self { - title: default_title(), - base_path: None, - } - } -} - -pub fn default_title() -> String { - "dioxus | ⛺".into() -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct WebProxyConfig { - pub backend: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct WebWatcherConfig { - #[serde(default = "watch_path_default")] - pub watch_path: Vec, - - #[serde(default)] - pub reload_html: bool, - - #[serde(default = "true_bool")] - pub index_on_404: bool, -} - -impl Default for WebWatcherConfig { - fn default() -> Self { - Self { - watch_path: watch_path_default(), - reload_html: false, - index_on_404: true, - } - } -} - -fn watch_path_default() -> Vec { - vec![PathBuf::from("src"), PathBuf::from("examples")] -} - -#[derive(Default, Debug, Clone, Serialize, Deserialize)] -pub struct WebResourceConfig { - pub dev: WebDevResourceConfig, - pub style: Option>, - pub script: Option>, -} - -#[derive(Default, Debug, Clone, Serialize, Deserialize)] -pub struct WebDevResourceConfig { - #[serde(default)] - pub style: Vec, - #[serde(default)] - pub script: Vec, -} - -#[derive(Debug, Default, Clone, Serialize, Deserialize)] -pub struct WebHttpsConfig { - pub enabled: Option, - pub mkcert: Option, - pub key_path: Option, - pub cert_path: Option, -} - -fn true_bool() -> bool { - true -} - -fn false_bool() -> bool { - false -} diff --git a/packages/cli/src/config/desktop.rs b/packages/cli/src/config/desktop.rs index e69de29bb2..ec6cd49365 100644 --- a/packages/cli/src/config/desktop.rs +++ b/packages/cli/src/config/desktop.rs @@ -0,0 +1,17 @@ +use serde::{Deserialize, Serialize}; + +/// Represents configuration items for the desktop platform. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct DesktopConfig { + /// Describes whether a debug-mode desktop app should be always-on-top. + #[serde(default)] + pub always_on_top: bool, +} + +impl Default for DesktopConfig { + fn default() -> Self { + Self { + always_on_top: true, + } + } +} diff --git a/packages/cli/src/config/dioxus_config.rs b/packages/cli/src/config/dioxus_config.rs index ecc9d52caa..3f8fa23d99 100644 --- a/packages/cli/src/config/dioxus_config.rs +++ b/packages/cli/src/config/dioxus_config.rs @@ -1,6 +1,5 @@ use super::*; use serde::{Deserialize, Serialize}; -use std::path::PathBuf; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct DioxusConfig { diff --git a/packages/cli/src/config/platform.rs b/packages/cli/src/config/platform.rs index 2b04be2b82..8624bcc75d 100644 --- a/packages/cli/src/config/platform.rs +++ b/packages/cli/src/config/platform.rs @@ -1,6 +1,5 @@ use serde::{Deserialize, Serialize}; use std::fmt::Display; -use std::path::PathBuf; use std::str::FromStr; #[derive( @@ -30,6 +29,10 @@ pub enum Platform { #[serde(rename = "desktop")] Desktop, + #[clap(name = "mobile")] + #[serde(rename = "mobile")] + Mobile, + /// Targeting the server platform using Axum and Dioxus-Fullstack #[clap(name = "fullstack")] #[serde(rename = "fullstack")] @@ -94,6 +97,7 @@ impl Platform { Platform::Fullstack => "fullstack", Platform::StaticGeneration => "static-generation", Platform::Liveview => "liveview", + Platform::Mobile => "mobile", } } } diff --git a/packages/cli/src/config/serve.rs b/packages/cli/src/config/serve.rs index 57671b15f8..39d24cb747 100644 --- a/packages/cli/src/config/serve.rs +++ b/packages/cli/src/config/serve.rs @@ -3,58 +3,7 @@ use clap::Parser; use std::net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4}; -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] -pub struct RuntimeCLIArguments { - /// The address hot reloading is running on - cli_address: SocketAddr, - - /// The address the server should run on - server_socket: Option, -} - -impl RuntimeCLIArguments { - /// Create a new RuntimeCLIArguments - pub fn new(cli_address: SocketAddr, server_socket: Option) -> Self { - Self { - cli_address, - server_socket, - } - } - - /// Attempt to read the current serve settings from the CLI. This will only be set for the fullstack platform on recent versions of the CLI. - pub fn from_cli() -> Option { - None - // std::env::var(crate::__private::SERVE_ENV) - // .ok() - // .and_then(|json| serde_json::from_str(&json).ok()) - } - - /// Get the address the server should run on - pub fn server_socket(&self) -> Option { - self.server_socket - } - - /// Get the address the CLI is running on - pub fn cli_address(&self) -> SocketAddr { - self.cli_address - } -} - -// /// Get the address the proxied fullstack server should run on -// #[cfg(feature = "read-from-args")] -// pub fn fullstack_address(&self) -> AddressArguments { -// let socket = self.server_socket.unwrap_or_else(|| { -// SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, default_port())) -// }); - -// AddressArguments { -// port: socket.port(), -// addr: socket.ip(), -// } -// } - -// /// The arguments for the address the server will run on - +/// The arguments for the address the server will run on #[derive(Clone, Debug, Parser)] pub struct AddressArguments { /// The port the server will run on diff --git a/packages/cli/src/config/web.rs b/packages/cli/src/config/web.rs index e69de29bb2..037e72f49f 100644 --- a/packages/cli/src/config/web.rs +++ b/packages/cli/src/config/web.rs @@ -0,0 +1,180 @@ +use serde::{Deserialize, Serialize}; +use std::path::PathBuf; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WebConfig { + #[serde(default)] + pub app: WebAppConfig, + + #[serde(default)] + pub proxy: Vec, + + #[serde(default)] + pub watcher: WebWatcherConfig, + + #[serde(default)] + pub resource: WebResourceConfig, + + #[serde(default)] + pub https: WebHttpsConfig, + + /// Whether to enable pre-compression of assets and wasm during a web build in release mode + #[serde(default = "true_bool")] + pub pre_compress: bool, + + /// The wasm-opt configuration + #[serde(default)] + pub wasm_opt: WasmOptConfig, +} + +impl Default for WebConfig { + fn default() -> Self { + Self { + pre_compress: true_bool(), + app: Default::default(), + https: Default::default(), + wasm_opt: Default::default(), + proxy: Default::default(), + watcher: Default::default(), + resource: Default::default(), + } + } +} + +/// The wasm-opt configuration +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct WasmOptConfig { + /// The wasm-opt level to use for release builds [default: s] + /// Options: + /// - z: optimize aggressively for size + /// - s: optimize for size + /// - 1: optimize for speed + /// - 2: optimize for more for speed + /// - 3: optimize for even more for speed + /// - 4: optimize aggressively for speed + #[serde(default)] + pub level: WasmOptLevel, + + /// Keep debug symbols in the wasm file + #[serde(default = "false_bool")] + pub debug: bool, +} + +/// The wasm-opt level to use for release web builds [default: 4] +#[derive(Default, Debug, Copy, Clone, Serialize, Deserialize)] +pub enum WasmOptLevel { + /// Optimize aggressively for size + #[serde(rename = "z")] + Z, + /// Optimize for size + #[serde(rename = "s")] + S, + /// Don't optimize + #[serde(rename = "0")] + Zero, + /// Optimize for speed + #[serde(rename = "1")] + One, + /// Optimize for more for speed + #[serde(rename = "2")] + Two, + /// Optimize for even more for speed + #[serde(rename = "3")] + Three, + /// Optimize aggressively for speed + #[serde(rename = "4")] + #[default] + Four, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WebAppConfig { + #[serde(default = "default_title")] + pub title: String, + pub base_path: Option, +} + +impl WebAppConfig { + /// Get the normalized base path for the application with `/` trimmed from both ends. If the base path is not set, this will return `.`. + pub fn base_path(&self) -> &str { + match &self.base_path { + Some(path) => path.trim_matches('/'), + None => ".", + } + } +} + +impl Default for WebAppConfig { + fn default() -> Self { + Self { + title: default_title(), + base_path: None, + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WebProxyConfig { + pub backend: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WebWatcherConfig { + #[serde(default = "watch_path_default")] + pub watch_path: Vec, + + #[serde(default)] + pub reload_html: bool, + + #[serde(default = "true_bool")] + pub index_on_404: bool, +} + +impl Default for WebWatcherConfig { + fn default() -> Self { + Self { + watch_path: watch_path_default(), + reload_html: false, + index_on_404: true, + } + } +} + +fn watch_path_default() -> Vec { + vec![PathBuf::from("src"), PathBuf::from("examples")] +} + +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +pub struct WebResourceConfig { + pub dev: WebDevResourceConfig, + pub style: Option>, + pub script: Option>, +} + +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +pub struct WebDevResourceConfig { + #[serde(default)] + pub style: Vec, + #[serde(default)] + pub script: Vec, +} + +#[derive(Debug, Default, Clone, Serialize, Deserialize)] +pub struct WebHttpsConfig { + pub enabled: Option, + pub mkcert: Option, + pub key_path: Option, + pub cert_path: Option, +} + +fn true_bool() -> bool { + true +} + +fn false_bool() -> bool { + false +} + +pub fn default_title() -> String { + "dioxus | ⛺".into() +} diff --git a/packages/cli/src/dioxus_crate.rs b/packages/cli/src/dioxus_crate.rs index 643b25a18b..57eda6d42d 100644 --- a/packages/cli/src/dioxus_crate.rs +++ b/packages/cli/src/dioxus_crate.rs @@ -33,6 +33,7 @@ fn load_dioxus_config( .as_std_path() .to_path_buf() .canonicalize()?; + let workspace_path = krates .workspace_root() .as_std_path() diff --git a/packages/cli/src/serve/builder.rs b/packages/cli/src/serve/builder.rs index 0081231a3c..f640b19c49 100644 --- a/packages/cli/src/serve/builder.rs +++ b/packages/cli/src/serve/builder.rs @@ -5,23 +5,23 @@ use crate::dioxus_crate::DioxusCrate; use crate::serve::next_or_pending; use crate::serve::Serve; use crate::Result; -use futures_channel::mpsc::UnboundedReceiver; +use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender}; use futures_util::future::OptionFuture; -use futures_util::stream::select_all; use futures_util::StreamExt; -use std::process::{ExitStatus, Stdio}; +use std::process::Stdio; use tokio::{ process::{Child, Command}, task::JoinHandle, }; +use super::update::ServeUpdate; + /// A handle to ongoing builds and then the spawned tasks themselves pub struct Builder { /// The results of the build build_results: Option>>>, - - /// The progress of the builds - build_progress: Vec<(TargetPlatform, UnboundedReceiver)>, + tx: UnboundedSender, + rx: UnboundedReceiver, /// The application we are building config: DioxusCrate, @@ -36,13 +36,13 @@ pub struct Builder { impl Builder { /// Create a new builder pub fn new(config: &DioxusCrate, serve: &Serve) -> Self { - let serve = serve.clone(); - let config = config.clone(); + let (tx, rx) = futures_channel::mpsc::unbounded(); Self { + tx, + rx, build_results: None, - build_progress: Vec::new(), config: config.clone(), - serve, + serve: serve.clone(), children: Vec::new(), } } @@ -50,25 +50,27 @@ impl Builder { /// Start a new build - killing the current one if it exists pub fn build(&mut self) -> Result<()> { self.shutdown(); + let build_requests = BuildRequest::create( BuildReason::Serve, &self.config, self.serve.build_arguments.clone(), + self.tx.clone(), )?; let mut set = tokio::task::JoinSet::new(); - for mut build_request in build_requests { - let (mut tx, rx) = futures_channel::mpsc::unbounded(); - self.build_progress - .push((build_request.target_platform, rx)); - set.spawn(async move { - let res = build_request.build(tx.clone()).await; + for build_request in build_requests { + let mut tx = self.tx.clone(); + set.spawn(async move { + let platform = build_request.target_platform.clone(); + let res = build_request.build().await; if let Err(err) = &res { let _ = tx.start_send(UpdateBuildProgress { stage: crate::builder::Stage::Finished, update: crate::builder::UpdateStage::Failed(format!("{err}")), + platform, }); } @@ -92,14 +94,9 @@ impl Builder { } /// Wait for any new updates to the builder - either it completed or gave us a message etc - pub async fn wait(&mut self) -> Result { + pub async fn wait(&mut self) -> ServeUpdate { // Wait for build progress - let mut next = select_all( - self.build_progress - .iter_mut() - .map(|(platform, rx)| rx.map(move |update| (*platform, update))), - ); - let next = next_or_pending(next.next()); + let next = next_or_pending(self.rx.next()); // The ongoing builds directly let results: OptionFuture<_> = self.build_results.as_mut().into(); @@ -111,11 +108,12 @@ impl Builder { .children .iter_mut() .map(|(target, child)| Box::pin(async move { (*target, child.wait().await) })); + let process_exited = async move { - if children_empty { - return futures_util::future::pending().await; + match children_empty { + true => return futures_util::future::pending().await, + false => futures_util::future::select_all(process_exited).await, } - futures_util::future::select_all(process_exited).await }; // Wait for the next build result @@ -124,16 +122,18 @@ impl Builder { self.build_results = None; // If we have a build result, bubble it up to the main loop - let build_results = build_results.map_err(|_| crate::Error::Unique("Build join failed".to_string()))??; - - Ok(BuilderUpdate::Ready { results: build_results }) + match build_results { + Ok(Ok(build_results)) => ServeUpdate::BuildReady { results: build_results }, + Err(_ee) => ServeUpdate::BuildFailed { err: crate::Error::BuildFailed("Build join failed".to_string()) }, + Ok(Err(ee)) => ServeUpdate::BuildFailed { err: ee.into() }, + } } - (platform, update) = next => { + update = next => { // If we have a build progress, send it to the screen - Ok(BuilderUpdate::Progress { platform, update }) + ServeUpdate::Progress { update } } ((target, exit_status), _, _) = process_exited => { - Ok(BuilderUpdate::ProcessExited { status: exit_status, target_platform: target }) + ServeUpdate::ProcessExited { status: exit_status, target_platform: target } } } } @@ -172,20 +172,5 @@ impl Builder { if let Some(tasks) = self.build_results.take() { tasks.abort(); } - self.build_progress.clear(); } } - -pub enum BuilderUpdate { - Progress { - platform: TargetPlatform, - update: UpdateBuildProgress, - }, - Ready { - results: Vec, - }, - ProcessExited { - target_platform: TargetPlatform, - status: Result, - }, -} diff --git a/packages/cli/src/serve/mod.rs b/packages/cli/src/serve/mod.rs index 6bb7366070..55023f72f4 100644 --- a/packages/cli/src/serve/mod.rs +++ b/packages/cli/src/serve/mod.rs @@ -15,11 +15,13 @@ mod logs_tab; mod output; mod proxy; mod server; +mod update; mod watcher; use builder::*; use output::*; use server::*; +use update::ServeUpdate; use watcher::*; /// For *all* builds the CLI spins up a dedicated webserver, file watcher, and build infrastructure to serve the project. @@ -62,8 +64,7 @@ pub async fn serve_all( let mut server = Server::start(&serve, &dioxus_crate); let mut watcher = Watcher::start(&serve, &dioxus_crate); let mut screen = Output::start(&serve, log_control).expect("Failed to open terminal logger"); - - let is_hot_reload = serve.server_arguments.hot_reload.unwrap_or(true); + let hotreload = serve.server_arguments.hot_reload.unwrap_or(true); loop { // Make sure we don't hog the CPU: these loop { select! {} } blocks can starve the executor @@ -73,11 +74,20 @@ pub async fn serve_all( screen.render(&serve, &dioxus_crate, &builder, &server, &watcher); // And then wait for any updates before redrawing - tokio::select! { - // rebuild the project or hotreload it - _ = watcher.wait(), if is_hot_reload => { + let msg = tokio::select! { + msg = watcher.wait(), if hotreload => msg, + msg = server.wait() => msg, + msg = builder.wait() => msg, + msg = screen.wait() => match msg { + Ok(res) => res, + Err(_err) => break + } + }; + + match msg { + ServeUpdate::FilesChanged {} => { if !watcher.pending_changes() { - continue + continue; } let changed_files = watcher.dequeue_changed_files(&dioxus_crate); @@ -87,7 +97,7 @@ pub async fn serve_all( if let Some(hr) = watcher.attempt_hot_reload(&dioxus_crate, changed_files) { // Only send a hotreload message for templates and assets - otherwise we'll just get a full rebuild if hr.templates.is_empty() && hr.assets.is_empty() { - continue + continue; } server.send_hotreload(hr).await; @@ -104,105 +114,109 @@ pub async fn serve_all( } } - // reload the page - msg = server.wait() => { - // Run the server in the background - // Waiting for updates here lets us tap into when clients are added/removed - match msg { - Some(ServerUpdate::NewConnection) => { - if let Some(msg) = watcher.applied_hot_reload_changes() { - server.send_hotreload(msg).await; - } - } - Some(ServerUpdate::Message(msg)) => { - screen.new_ws_message(TargetPlatform::Web, msg); - } - None => {} + // Run the server in the background + // Waiting for updates here lets us tap into when clients are added/removed + ServeUpdate::NewConnection => { + if let Some(msg) = watcher.applied_hot_reload_changes() { + server.send_hotreload(msg).await; + } + } + + ServeUpdate::Message(msg) => { + screen.new_ws_message(TargetPlatform::Web, msg); + } + + // Wait for logs from the build engine + // These will cause us to update the screen + // We also can check the status of the builds here in case we have multiple ongoing builds + ServeUpdate::Progress { update } => { + let update_clone = update.clone(); + screen.new_build_logs(update.platform, update_clone); + server + .update_build_status(screen.build_progress.progress(), update.stage.to_string()) + .await; + + match update { + // Send rebuild start message. + UpdateBuildProgress { + stage: Stage::Compiling, + update: UpdateStage::Start, + platform: _, + } => server.send_reload_start().await, + + // Send rebuild failed message. + UpdateBuildProgress { + stage: Stage::Finished, + update: UpdateStage::Failed(_), + platform: _, + } => server.send_reload_failed().await, + + _ => {} } } - // Handle updates from the build engine - application = builder.wait() => { - // Wait for logs from the build engine - // These will cause us to update the screen - // We also can check the status of the builds here in case we have multiple ongoing builds - match application { - Ok(BuilderUpdate::Progress { platform, update }) => { - let update_clone = update.clone(); - screen.new_build_logs(platform, update_clone); - server.update_build_status(screen.build_progress.progress(), update.stage.to_string()).await; - - match update { - // Send rebuild start message. - UpdateBuildProgress { stage: Stage::Compiling, update: UpdateStage::Start } => server.send_reload_start().await, - // Send rebuild failed message. - UpdateBuildProgress { stage: Stage::Finished, update: UpdateStage::Failed(_) } => server.send_reload_failed().await, - _ => {}, + ServeUpdate::BuildFailed { err } => { + tracing::error!("Build failed with err: {err}"); + } + + ServeUpdate::BuildReady { results } => { + if !results.is_empty() { + builder.children.clear(); + } + + // If we have a build result, open it + for build_result in results.iter() { + let child = + build_result.open(&serve.server_arguments, server.fullstack_address()); + + match child { + Ok(Some(child_proc)) => builder + .children + .push((build_result.target_platform, child_proc)), + Err(e) => { + tracing::error!("Failed to open build result: {e}"); + break; } + _ => {} } - Ok(BuilderUpdate::Ready { results }) => { - if !results.is_empty() { - builder.children.clear(); - } + } - // If we have a build result, open it - for build_result in results.iter() { - let child = build_result.open(&dioxus_crate, &serve.server_arguments, server.fullstack_address(), &dioxus_crate.workspace_dir()); - match child { - Ok(Some(child_proc)) => builder.children.push((build_result.target_platform, child_proc)), - Err(e) => { - tracing::error!("Failed to open build result: {e}"); - break; - }, - _ => {} - } - } + // Make sure we immediately capture the stdout/stderr of the executable - + // otherwise it'll clobber our terminal output + screen.new_ready_app(&mut builder, results); - // Make sure we immediately capture the stdout/stderr of the executable - - // otherwise it'll clobber our terminal output - screen.new_ready_app(&mut builder, results); - - // And then finally tell the server to reload - server.send_reload_command().await; - - // We also want to watch any of its assets for changes? - }, - - // If the process exited *cleanly*, we can exit - Ok(BuilderUpdate::ProcessExited { status, target_platform }) => { - // Then remove the child process - builder.children.retain(|(platform, _)| *platform != target_platform); - match status { - Ok(status) => { - if status.success() { - break; - } - else { - tracing::error!("Application exited with status: {status}"); - } - }, - Err(e) => { - tracing::error!("Application exited with error: {e}"); - } + // And then finally tell the server to reload + server.send_reload_command().await; + } + + // If the process exited *cleanly*, we can exit + ServeUpdate::ProcessExited { + status, + target_platform, + } => { + // Then remove the child process + builder + .children + .retain(|(platform, _)| *platform != target_platform); + match status { + Ok(status) => { + if status.success() { + break; + } else { + tracing::error!("Application exited with status: {status}"); } } - Err(err) => { - server.send_build_error(err).await; + Err(e) => { + tracing::error!("Application exited with error: {e}"); } } } - // Handle input from the user using our settings - res = screen.wait() => { - match res { - Ok(false) => {} - // Request a rebuild. - Ok(true) => { - builder.build()?; - server.start_build().await - }, - // Shutdown the server. - Err(_) => break, + ServeUpdate::TuiInput { rebuild } => { + // Request a rebuild. + if rebuild { + builder.build()?; + server.start_build().await } } } diff --git a/packages/cli/src/serve/output.rs b/packages/cli/src/serve/output.rs index 0d894991ec..2c9f5487d6 100644 --- a/packages/cli/src/serve/output.rs +++ b/packages/cli/src/serve/output.rs @@ -36,7 +36,7 @@ use tokio::{ }; use tracing::Level; -use super::{Builder, Server, Watcher}; +use super::{update::ServeUpdate, Builder, Server, Watcher}; #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum LogSource { @@ -238,7 +238,7 @@ impl Output { /// Why is the ctrl_c handler here? /// /// Also tick animations every few ms - pub async fn wait(&mut self) -> io::Result { + pub async fn wait(&mut self) -> io::Result { fn ok_and_some(f: F) -> impl Future where F: Future, E>>, @@ -302,12 +302,12 @@ impl Output { event = user_input => { if self.handle_events(event).await? { - return Ok(true) + return Ok(ServeUpdate::TuiInput { rebuild: true }); } } } - Ok(false) + Ok(ServeUpdate::TuiInput { rebuild: false }) } pub fn shutdown(&mut self) -> io::Result<()> { diff --git a/packages/cli/src/serve/server.rs b/packages/cli/src/serve/server.rs index 7750e16185..07b1864960 100644 --- a/packages/cli/src/serve/server.rs +++ b/packages/cli/src/serve/server.rs @@ -1,6 +1,9 @@ -use crate::config::{Platform, WebHttpsConfig}; use crate::dioxus_crate::DioxusCrate; use crate::serve::{next_or_pending, Serve}; +use crate::{ + config::{Platform, WebHttpsConfig}, + serve::update::ServeUpdate, +}; use crate::{Error, Result}; use axum::extract::{Request, State}; use axum::middleware::{self, Next}; @@ -21,7 +24,7 @@ use axum::{ use axum_server::tls_rustls::RustlsConfig; use dioxus_devtools_types::{DevserverMsg, HotReloadMsg}; use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender}; -use futures_util::stream; +use futures_util::{future, stream}; use futures_util::{stream::FuturesUnordered, StreamExt}; use hyper::header::ACCEPT; use hyper::HeaderMap; @@ -240,7 +243,7 @@ impl Server { } /// Wait for new clients to be connected and then save them - pub async fn wait(&mut self) -> Option { + pub async fn wait(&mut self) -> ServeUpdate { let mut new_hot_reload_socket = self.new_hot_reload_sockets.next(); let mut new_build_status_socket = self.new_build_status_sockets.next(); let mut new_message = self @@ -256,7 +259,7 @@ impl Server { if let Some(new_socket) = new_hot_reload_socket { drop(new_message); self.hot_reload_sockets.push(new_socket); - return Some(ServerUpdate::NewConnection); + return ServeUpdate::NewConnection; } else { panic!("Could not receive a socket - the devtools could not boot - the port is likely already in use"); } @@ -271,14 +274,14 @@ impl Server { _ = send_build_status_to(&self.build_status, &mut new_socket).await; self.build_status_sockets.push(new_socket); } - return None; + return future::pending::().await; } else { panic!("Could not receive a socket - the devtools could not boot - the port is likely already in use"); } } (idx, message) = next_new_message => { match message { - Some(Ok(message)) => return Some(ServerUpdate::Message(message)), + Some(Ok(message)) => return ServeUpdate::Message(message), _ => { drop(new_message); _ = self.hot_reload_sockets.remove(idx); @@ -287,7 +290,7 @@ impl Server { } } - None + future::pending().await } /// Converts a `cargo` error to HTML and sends it to clients. diff --git a/packages/cli/src/serve/update.rs b/packages/cli/src/serve/update.rs new file mode 100644 index 0000000000..7ad4d64c4b --- /dev/null +++ b/packages/cli/src/serve/update.rs @@ -0,0 +1,34 @@ +use crate::builder::{BuildRequest, TargetPlatform, UpdateBuildProgress}; +use axum::extract::ws::Message as WsMessage; +use std::process::ExitStatus; + +/// One fat enum to rule them all.... +/// +/// Thanks to libraries like winit for the inspiration +pub enum ServeUpdate { + NewConnection, + Message(WsMessage), + + Progress { + update: UpdateBuildProgress, + }, + + BuildReady { + results: Vec, + }, + + BuildFailed { + err: crate::Error, + }, + + ProcessExited { + target_platform: TargetPlatform, + status: Result, + }, + + FilesChanged {}, + + TuiInput { + rebuild: bool, + }, +} diff --git a/packages/cli/src/serve/watcher.rs b/packages/cli/src/serve/watcher.rs index 0d5dc1ad5d..0c73417335 100644 --- a/packages/cli/src/serve/watcher.rs +++ b/packages/cli/src/serve/watcher.rs @@ -1,7 +1,7 @@ use std::collections::{HashMap, HashSet}; use std::{fs, path::PathBuf, time::Duration}; -use super::hot_reloading_file_map::HotreloadError; +use super::{hot_reloading_file_map::HotreloadError, update::ServeUpdate}; use crate::serve::hot_reloading_file_map::FileMap; use crate::{cli::serve::Serve, dioxus_crate::DioxusCrate}; use dioxus_devtools_types::HotReloadMsg; @@ -141,20 +141,22 @@ impl Watcher { /// A cancel safe handle to the file watcher /// /// todo: this should be simpler logic? - pub async fn wait(&mut self) { + pub async fn wait(&mut self) -> ServeUpdate { // Pull off any queued events in succession while let Ok(Some(event)) = self.rx.try_next() { self.queued_events.push(event); } - if !self.queued_events.is_empty() { - return; - } + // if !self.queued_events.is_empty() { + // return; + // } - // If there are no queued events, wait for the next event - if let Some(event) = self.rx.next().await { - self.queued_events.push(event); - } + // // If there are no queued events, wait for the next event + // if let Some(event) = self.rx.next().await { + // self.queued_events.push(event); + // } + + todo!() } /// Deques changed files from the event queue, doing the proper intelligent filtering diff --git a/packages/desktop/src/app.rs b/packages/desktop/src/app.rs index 021b90a18f..c844b14721 100644 --- a/packages/desktop/src/app.rs +++ b/packages/desktop/src/app.rs @@ -136,7 +136,7 @@ impl App { #[cfg(all(feature = "devtools", debug_assertions,))] pub fn connect_devtools(&self) { - if let Some(addr) = dioxus_runtime_config::devserver_addr() { + if let Some(addr) = dioxus_runtime_config::devserver_raw_addr() { let proxy = self.shared.proxy.clone(); dioxus_devtools::connect(addr, move |msg| { _ = proxy.send_event(UserWindowEvent::HotReloadEvent(msg)); diff --git a/packages/devtools/src/lib.rs b/packages/devtools/src/lib.rs index 866743723c..2be9e7fa46 100644 --- a/packages/devtools/src/lib.rs +++ b/packages/devtools/src/lib.rs @@ -63,8 +63,6 @@ pub fn connect(addr: SocketAddr, mut callback: impl FnMut(DevserverMsg) + Send + } else { eprintln!("Failed to parse message from devserver: {:?}", buf); } - - std::thread::sleep(std::time::Duration::from_millis(100)); } }); } diff --git a/packages/fullstack/Cargo.toml b/packages/fullstack/Cargo.toml index 8c67f6d9d9..c0a5496b32 100644 --- a/packages/fullstack/Cargo.toml +++ b/packages/fullstack/Cargo.toml @@ -21,6 +21,7 @@ tower-http = { workspace = true, optional = true, features = ["fs"] } dioxus-lib = { workspace = true } generational-box = { workspace = true } +dioxus-runtime-config = { workspace = true } # Dioxus + SSR dioxus-ssr = { workspace = true, optional = true } @@ -62,7 +63,6 @@ tower = { workspace = true, features = ["util"], optional = true } tower-layer = { version = "0.3.2", optional = true } parking_lot = { version = "0.12.1", features = ["send_guard"], optional = true } web-sys = { version = "0.3.61", optional = true, features = ["Window", "Document", "Element", "HtmlDocument", "Storage", "console"] } -clap = { workspace = true, optional = true, features = ["derive"] } aws-lc-rs = { version = "1.8.1", optional = true } dioxus-devtools = { workspace = true, optional = true } @@ -105,7 +105,6 @@ server = [ "dep:async-trait", "dep:parking_lot", "dioxus-interpreter-js", - "dep:clap", ] aws-lc-rs = ["dep:aws-lc-rs"] diff --git a/packages/fullstack/src/launch.rs b/packages/fullstack/src/launch.rs index a3b1f2d269..ec518f6f8a 100644 --- a/packages/fullstack/src/launch.rs +++ b/packages/fullstack/src/launch.rs @@ -127,21 +127,10 @@ async fn launch_server( build_virtual_dom: impl Fn() -> VirtualDom + Send + Sync + 'static, context_providers: ContextProviders, ) { - use clap::Parser; - // Get the address the server should run on. If the CLI is running, the CLI proxies fullstack into the main address // and we use the generated address the CLI gives us - let cli_args = dioxus_cli_config::RuntimeCLIArguments::from_cli(); - let address = cli_args - .as_ref() - .map(|args| args.fullstack_address()) - .unwrap_or_else(dioxus_cli_config::AddressArguments::parse) - .address(); - - // Point the user to the CLI address if the CLI is running or the fullstack address if not - let serve_address = cli_args - .map(|args| args.cli_address()) - .unwrap_or_else(|| address); + let serve_address = dioxus_runtime_config::fullstack_address() + .unwrap_or_else(|| todo!("parse cli args for fullstack address")); #[cfg(feature = "axum")] { diff --git a/packages/liveview/src/pool.rs b/packages/liveview/src/pool.rs index 132547c538..f7b5f3097c 100644 --- a/packages/liveview/src/pool.rs +++ b/packages/liveview/src/pool.rs @@ -120,7 +120,7 @@ pub async fn run(mut vdom: VirtualDom, ws: impl LiveViewSocket) -> Result<(), Li #[cfg(all(feature = "devtools", debug_assertions))] let mut hot_reload_rx = { let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); - if let Some(addr) = dioxus_runtime_config::devserver_addr() { + if let Some(addr) = dioxus_runtime_config::devserver_raw_addr() { dioxus_devtools::connect(addr, move |template| _ = tx.send(template)); } rx diff --git a/packages/runtime-config/src/lib.rs b/packages/runtime-config/src/lib.rs index 8adccad160..47bedb76b9 100644 --- a/packages/runtime-config/src/lib.rs +++ b/packages/runtime-config/src/lib.rs @@ -1,14 +1,16 @@ use std::net::SocketAddr; -pub const DEVSERVER_ADDR_ENV: &str = "DIOXUS_DEVSERVER_ADDR"; +pub const RAW_DEVSERVER_ADDR_ENV: &str = "DIOXUS_DEVSERVER_ADDR"; pub const FULLSTACK_ADDRESS_ENV: &str = "DIOXUS_FULLSTACK_ADDRESS"; /// when targetting ios, we need to set a prefix to the argument such that it gets picked up by simctl pub const IOS_DEVSERVER_ADDR_ENV: &str = "SIMCTL_CHILD_DEVSERVER_ADDR"; -/// Get the address of the devserver -pub fn devserver_addr() -> Option { - std::env::var(DEVSERVER_ADDR_ENV) +/// Get the address of the devserver for use over a raw socket +/// +/// This is not a websocket! There's no protocol! +pub fn devserver_raw_addr() -> Option { + std::env::var(RAW_DEVSERVER_ADDR_ENV) .ok() .and_then(|s| s.parse().ok()) } From 76b04edfd085fd2236866db04141e86b28a45448 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 22 Aug 2024 20:34:30 -0700 Subject: [PATCH 053/139] hotreload works again for desktop and this time, mobile too --- Cargo.lock | 21 ++++- Cargo.toml | 2 +- packages/cli/src/builder/cargo.rs | 3 + packages/cli/src/builder/mod.rs | 106 --------------------- packages/cli/src/config/serve.rs | 2 +- packages/cli/src/serve/mod.rs | 5 +- packages/cli/src/serve/output.rs | 4 +- packages/cli/src/serve/server.rs | 143 ++++++++++++++++++++++++++--- packages/cli/src/serve/watcher.rs | 18 ++-- packages/devtools/Cargo.toml | 1 + packages/devtools/src/lib.rs | 77 +++++++++++----- packages/mobile/Cargo.toml | 4 +- packages/runtime-config/src/lib.rs | 14 +-- 13 files changed, 233 insertions(+), 167 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6aa8c22153..4f71f3a2a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2553,6 +2553,7 @@ dependencies = [ "serde_json", "tokio", "tracing", + "tungstenite 0.23.0", "warnings", ] @@ -9672,7 +9673,7 @@ dependencies = [ "futures-util", "log", "tokio", - "tungstenite", + "tungstenite 0.21.0", ] [[package]] @@ -9982,6 +9983,24 @@ dependencies = [ "utf-8", ] +[[package]] +name = "tungstenite" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e2ce1e47ed2994fd43b04c8f618008d4cabdd5ee34027cf14f9d918edd9c8" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http 1.1.0", + "httparse", + "log", + "rand 0.8.5", + "sha1", + "thiserror", + "utf-8", +] + [[package]] name = "twofish" version = "0.7.1" diff --git a/Cargo.toml b/Cargo.toml index 2278c775e5..377bd537e6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -91,7 +91,7 @@ dioxus-html-internal-macro = { path = "packages/html-internal-macro", version = dioxus-hooks = { path = "packages/hooks", version = "0.6.0-alpha.2" } dioxus-web = { path = "packages/web", version = "0.6.0-alpha.2", default-features = false } dioxus-ssr = { path = "packages/ssr", version = "0.6.0-alpha.2", default-features = false } -dioxus-desktop = { path = "packages/desktop", version = "0.6.0-alpha.2", default-features = false } +dioxus-desktop = { path = "packages/desktop", version = "0.6.0-alpha.2" } dioxus-mobile = { path = "packages/mobile", version = "0.6.0-alpha.2" } dioxus-interpreter-js = { path = "packages/interpreter", version = "0.6.0-alpha.2" } dioxus-liveview = { path = "packages/liveview", version = "0.6.0-alpha.2" } diff --git a/packages/cli/src/builder/cargo.rs b/packages/cli/src/builder/cargo.rs index 05b196ef24..e9eff429e1 100644 --- a/packages/cli/src/builder/cargo.rs +++ b/packages/cli/src/builder/cargo.rs @@ -161,6 +161,9 @@ impl BuildRequest { std::fs::copy(res_path, &output_path)?; } + // Make sure we set the exeutable + self.executable = Some(output_path.canonicalize()?); + self.copy_assets_dir()?; // If this is a web build, run web post processing steps diff --git a/packages/cli/src/builder/mod.rs b/packages/cli/src/builder/mod.rs index 08f1315b57..eabf79d4d1 100644 --- a/packages/cli/src/builder/mod.rs +++ b/packages/cli/src/builder/mod.rs @@ -142,110 +142,4 @@ impl BuildRequest { pub fn targeting_web(&self) -> bool { self.target_platform == TargetPlatform::Web } - - /// Open the executable if this is a native build - pub fn open( - &self, - serve: &ServeArguments, - fullstack_address: Option, - ) -> std::io::Result> { - if self.target_platform == TargetPlatform::Server { - tracing::trace!("Proxying fullstack server from port {fullstack_address:?}"); - } - - match self.target_platform { - TargetPlatform::Web => Ok(None), - TargetPlatform::Mobile => self.open_bundled_ios_app(serve), - TargetPlatform::Desktop | TargetPlatform::Server | TargetPlatform::Liveview => { - self.open_unbundled_native_app(serve, fullstack_address) - } - } - } - - fn open_unbundled_native_app( - &self, - serve: &ServeArguments, - fullstack_address: Option, - ) -> std::io::Result> { - // - // open the exe with some arguments/envvars/etc - // we're going to try and configure this binary from the environment, if we can - // - // web can't be configured like this, so instead, we'll need to plumb a meta tag into the - // index.html during dev - // - let res = Command::new( - self.executable - .as_deref() - .expect("executable should be built if we're trying to open it") - .canonicalize()?, - ) - .env( - dioxus_runtime_config::FULLSTACK_ADDRESS_ENV, - fullstack_address - .as_ref() - .map(|addr| addr.to_string()) - .unwrap_or_else(|| "127.0.0.1:8080".to_string()), - ) - .env( - dioxus_runtime_config::RAW_DEVSERVER_ADDR_ENV, - serve.address.address().to_string(), - ) - .env( - dioxus_runtime_config::IOS_DEVSERVER_ADDR_ENV, - serve.address.address().to_string(), - ) - .env("CARGO_MANIFEST_DIR", self.krate.crate_dir()) - .stderr(Stdio::piped()) - .stdout(Stdio::piped()) - .kill_on_drop(true) - .current_dir(self.krate.workspace_dir()) - .spawn()?; - - Ok(Some(res)) - } - - fn open_bundled_ios_app(&self, serve: &ServeArguments) -> std::io::Result> { - // command = "xcrun" - // args = [ - // "simctl", - // "install", - // "booted", - // "target/aarch64-apple-ios-sim/debug/bundle/ios/DioxusApp.app", - // ] - - // [tasks.run_ios_sim] - // args = ["simctl", "launch", "--console", "booted", "com.dioxuslabs"] - // command = "xcrun" - // dependencies = ["build_ios_sim", "install_ios_sim"] - - // [tasks.serve-sim] - // dependencies = ["build_ios_sim", "install_ios_sim", "run_ios_sim"] - - // APP_PATH="target/aarch64-apple-ios/debug/bundle/ios/DioxusApp.app" - - // # get the device id by jq-ing the json of the device list - // xcrun devicectl list devices --json-output target/deviceid.json - // DEVICE_UUID=$(jq -r '.result.devices[0].identifier' target/deviceid.json) - - // xcrun devicectl device install app --device "${DEVICE_UUID}" "${APP_PATH}" --json-output target/xcrun.json - - // # get the installation url by jq-ing the json of the device install - // INSTALLATION_URL=$(jq -r '.result.installedApplications[0].installationURL' target/xcrun.json) - - // # launch the app - // # todo: we can just background it immediately and then pick it up for loading its logs - // xcrun devicectl device process launch --device "${DEVICE_UUID}" "${INSTALLATION_URL}" - - // # # launch the app and put it in background - // # xcrun devicectl device process launch --no-activate --verbose --device "${DEVICE_UUID}" "${INSTALLATION_URL}" --json-output "${XCRUN_DEVICE_PROCESS_LAUNCH_LOG_DIR}" - - // # # Extract background PID of status app - // # STATUS_PID=$(jq -r '.result.process.processIdentifier' "${XCRUN_DEVICE_PROCESS_LAUNCH_LOG_DIR}") - // # "${GIT_ROOT}/scripts/wait-for-metro-port.sh" 2>&1 - - // # # now that metro is ready, resume the app from background - // # xcrun devicectl device process resume --device "${DEVICE_UUID}" --pid "${STATUS_PID}" > "${XCRUN_DEVICE_PROCESS_RESUME_LOG_DIR}" 2>&1 - todo!("Open mobile apps") - } } diff --git a/packages/cli/src/config/serve.rs b/packages/cli/src/config/serve.rs index 39d24cb747..e860539e74 100644 --- a/packages/cli/src/config/serve.rs +++ b/packages/cli/src/config/serve.rs @@ -37,5 +37,5 @@ fn default_port() -> u16 { } fn default_address() -> IpAddr { - IpAddr::V4(std::net::Ipv4Addr::new(127, 0, 0, 1)) + IpAddr::V4(std::net::Ipv4Addr::new(0, 0, 0, 0)) } diff --git a/packages/cli/src/serve/mod.rs b/packages/cli/src/serve/mod.rs index 55023f72f4..0a625078e7 100644 --- a/packages/cli/src/serve/mod.rs +++ b/packages/cli/src/serve/mod.rs @@ -61,7 +61,7 @@ pub async fn serve_all( // Start the first build builder.build()?; - let mut server = Server::start(&serve, &dioxus_crate); + let mut server = DevServer::start(&serve, &dioxus_crate); let mut watcher = Watcher::start(&serve, &dioxus_crate); let mut screen = Output::start(&serve, log_control).expect("Failed to open terminal logger"); let hotreload = serve.server_arguments.hot_reload.unwrap_or(true); @@ -166,8 +166,7 @@ pub async fn serve_all( // If we have a build result, open it for build_result in results.iter() { - let child = - build_result.open(&serve.server_arguments, server.fullstack_address()); + let child = server.open(build_result); match child { Ok(Some(child_proc)) => builder diff --git a/packages/cli/src/serve/output.rs b/packages/cli/src/serve/output.rs index 2c9f5487d6..2a743dda05 100644 --- a/packages/cli/src/serve/output.rs +++ b/packages/cli/src/serve/output.rs @@ -36,7 +36,7 @@ use tokio::{ }; use tracing::Level; -use super::{update::ServeUpdate, Builder, Server, Watcher}; +use super::{update::ServeUpdate, Builder, DevServer, Watcher}; #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum LogSource { @@ -568,7 +568,7 @@ impl Output { _opts: &Serve, _config: &DioxusCrate, _build_engine: &Builder, - server: &Server, + server: &DevServer, _watcher: &Watcher, ) { // just drain the build logs diff --git a/packages/cli/src/serve/server.rs b/packages/cli/src/serve/server.rs index 07b1864960..167e085088 100644 --- a/packages/cli/src/serve/server.rs +++ b/packages/cli/src/serve/server.rs @@ -1,5 +1,8 @@ -use crate::dioxus_crate::DioxusCrate; -use crate::serve::{next_or_pending, Serve}; +use crate::{builder::BuildRequest, dioxus_crate::DioxusCrate}; +use crate::{ + builder::TargetPlatform, + serve::{next_or_pending, Serve}, +}; use crate::{ config::{Platform, WebHttpsConfig}, serve::update::ServeUpdate, @@ -30,15 +33,16 @@ use hyper::header::ACCEPT; use hyper::HeaderMap; use serde::{Deserialize, Serialize}; use std::net::TcpListener; -use std::path::Path; use std::sync::Arc; use std::sync::RwLock; use std::{ convert::Infallible, fs, io, net::{IpAddr, SocketAddr}, - process::Command, }; +use std::{path::Path, process::Stdio}; +use syn::LitCStr; +use tokio::process::{Child, Command}; use tokio::task::JoinHandle; use tower::ServiceBuilder; use tower_http::{ @@ -86,13 +90,15 @@ impl SharedStatus { } } -pub(crate) struct Server { +pub struct DevServer { + pub serve: Serve, pub hot_reload_sockets: Vec, pub build_status_sockets: Vec, pub ip: SocketAddr, pub new_hot_reload_sockets: UnboundedReceiver, pub new_build_status_sockets: UnboundedReceiver, _server_task: JoinHandle>, + /// We proxy (not hot reloading) fullstack requests to this port pub fullstack_port: Option, @@ -101,7 +107,7 @@ pub(crate) struct Server { platform: String, } -impl Server { +impl DevServer { pub fn start(serve: &Serve, cfg: &DioxusCrate) -> Self { let (hot_reload_sockets_tx, hot_reload_sockets_rx) = futures_channel::mpsc::unbounded(); let (build_status_sockets_tx, build_status_sockets_rx) = futures_channel::mpsc::unbounded(); @@ -140,6 +146,11 @@ impl Server { let web_config = cfg.dioxus_config.web.https.clone(); let base_path = cfg.dioxus_config.web.app.base_path.clone(); let platform = serve.platform(); + + let mut listener = std::net::TcpListener::bind(addr).expect("Failed to bind port"); + listener.set_nonblocking(true); + let ip = listener.local_addr().unwrap(); + let _server_task = tokio::spawn(async move { let web_config = web_config.clone(); // HTTPS @@ -154,13 +165,13 @@ impl Server { // Start the server with or without rustls if let Some(rustls) = rustls { - axum_server::bind_rustls(addr, rustls) + axum_server::from_tcp_rustls(listener, rustls) .serve(router.into_make_service()) .await? } else { // Create a TCP listener bound to the address axum::serve( - tokio::net::TcpListener::bind(&addr).await?, + tokio::net::TcpListener::from_std(listener).unwrap(), router.into_make_service(), ) .await? @@ -170,12 +181,13 @@ impl Server { }); Self { + serve: serve.clone(), hot_reload_sockets: Default::default(), build_status_sockets: Default::default(), new_hot_reload_sockets: hot_reload_sockets_rx, new_build_status_sockets: build_status_sockets_rx, _server_task, - ip: addr, + ip, fullstack_port, build_status, @@ -348,6 +360,113 @@ impl Server { self.fullstack_port .map(|port| SocketAddr::new(self.ip.ip(), port)) } + + /// Open the executable if this is a native build + pub fn open(&self, build: &BuildRequest) -> std::io::Result> { + match build.target_platform { + TargetPlatform::Web => Ok(None), + TargetPlatform::Mobile => self.open_bundled_ios_app(build), + TargetPlatform::Desktop | TargetPlatform::Server | TargetPlatform::Liveview => { + self.open_unbundled_native_app(build) + } + } + } + + fn open_unbundled_native_app(&self, build: &BuildRequest) -> std::io::Result> { + if build.target_platform == TargetPlatform::Server { + tracing::trace!( + "Proxying fullstack server from port {:?}", + self.fullstack_address() + ); + } + + tracing::info!( + "Opening exectuable with dev server ip {}", + self.ip.to_string() + ); + + // + // open the exe with some arguments/envvars/etc + // we're going to try and configure this binary from the environment, if we can + // + // web can't be configured like this, so instead, we'll need to plumb a meta tag into the + // index.html during dev + // + let res = Command::new( + build + .executable + .as_deref() + .expect("executable should be built if we're trying to open it") + .canonicalize()?, + ) + .env( + dioxus_runtime_config::FULLSTACK_ADDRESS_ENV, + self.fullstack_address() + .as_ref() + .map(|addr| addr.to_string()) + .unwrap_or_else(|| "127.0.0.1:8080".to_string()), + ) + .env( + dioxus_runtime_config::IOS_DEVSERVER_ADDR_ENV, + format!("ws://{}/_dioxus", self.ip.to_string()), + ) + .env( + dioxus_runtime_config::DEVSERVER_RAW_ADDR_ENV, + format!("ws://{}/_dioxus", self.ip.to_string()), + ) + .env("CARGO_MANIFEST_DIR", build.krate.crate_dir()) + .stderr(Stdio::piped()) + .stdout(Stdio::piped()) + .kill_on_drop(true) + .current_dir(build.krate.workspace_dir()) + .spawn()?; + + Ok(Some(res)) + } + + fn open_bundled_ios_app(&self, build: &BuildRequest) -> std::io::Result> { + // command = "xcrun" + // args = [ + // "simctl", + // "install", + // "booted", + // "target/aarch64-apple-ios-sim/debug/bundle/ios/DioxusApp.app", + // ] + + // [tasks.run_ios_sim] + // args = ["simctl", "launch", "--console", "booted", "com.dioxuslabs"] + // command = "xcrun" + // dependencies = ["build_ios_sim", "install_ios_sim"] + + // [tasks.serve-sim] + // dependencies = ["build_ios_sim", "install_ios_sim", "run_ios_sim"] + + // APP_PATH="target/aarch64-apple-ios/debug/bundle/ios/DioxusApp.app" + + // # get the device id by jq-ing the json of the device list + // xcrun devicectl list devices --json-output target/deviceid.json + // DEVICE_UUID=$(jq -r '.result.devices[0].identifier' target/deviceid.json) + + // xcrun devicectl device install app --device "${DEVICE_UUID}" "${APP_PATH}" --json-output target/xcrun.json + + // # get the installation url by jq-ing the json of the device install + // INSTALLATION_URL=$(jq -r '.result.installedApplications[0].installationURL' target/xcrun.json) + + // # launch the app + // # todo: we can just background it immediately and then pick it up for loading its logs + // xcrun devicectl device process launch --device "${DEVICE_UUID}" "${INSTALLATION_URL}" + + // # # launch the app and put it in background + // # xcrun devicectl device process launch --no-activate --verbose --device "${DEVICE_UUID}" "${INSTALLATION_URL}" --json-output "${XCRUN_DEVICE_PROCESS_LAUNCH_LOG_DIR}" + + // # # Extract background PID of status app + // # STATUS_PID=$(jq -r '.result.process.processIdentifier' "${XCRUN_DEVICE_PROCESS_LAUNCH_LOG_DIR}") + // # "${GIT_ROOT}/scripts/wait-for-metro-port.sh" 2>&1 + + // # # now that metro is ready, resume the app from background + // # xcrun devicectl device process resume --device "${DEVICE_UUID}" --pid "${STATUS_PID}" > "${XCRUN_DEVICE_PROCESS_RESUME_LOG_DIR}" 2>&1 + todo!("Open mobile apps") + } } /// Sets up and returns a router @@ -531,7 +650,7 @@ pub async fn get_rustls(web_config: &WebHttpsConfig) -> Result get_rustls_with_mkcert(web_config)?, + Some(true) => get_rustls_with_mkcert(web_config).await?, _ => get_rustls_without_mkcert(web_config)?, }; @@ -540,7 +659,7 @@ pub async fn get_rustls(web_config: &WebHttpsConfig) -> Result Result<(String, String)> { +pub async fn get_rustls_with_mkcert(web_config: &WebHttpsConfig) -> Result<(String, String)> { const DEFAULT_KEY_PATH: &str = "ssl/key.pem"; const DEFAULT_CERT_PATH: &str = "ssl/cert.pem"; @@ -582,7 +701,7 @@ pub fn get_rustls_with_mkcert(web_config: &WebHttpsConfig) -> Result<(String, St return Err("failed to generate mkcert certificates".into()); } Ok(mut cmd) => { - cmd.wait()?; + cmd.wait().await?; } } diff --git a/packages/cli/src/serve/watcher.rs b/packages/cli/src/serve/watcher.rs index 0c73417335..ead44e29cf 100644 --- a/packages/cli/src/serve/watcher.rs +++ b/packages/cli/src/serve/watcher.rs @@ -147,16 +147,18 @@ impl Watcher { self.queued_events.push(event); } - // if !self.queued_events.is_empty() { - // return; - // } + if !self.queued_events.is_empty() { + tokio::time::sleep(std::time::Duration::from_millis(100)).await; + tracing::info!("Waiting for file changes!"); + return ServeUpdate::FilesChanged {}; + } - // // If there are no queued events, wait for the next event - // if let Some(event) = self.rx.next().await { - // self.queued_events.push(event); - // } + // If there are no queued events, wait for the next event + if let Some(event) = self.rx.next().await { + self.queued_events.push(event); + } - todo!() + ServeUpdate::FilesChanged {} } /// Deques changed files from the event queue, doing the proper intelligent filtering diff --git a/packages/devtools/Cargo.toml b/packages/devtools/Cargo.toml index e42a3a2c52..1ab8d6ae2a 100644 --- a/packages/devtools/Cargo.toml +++ b/packages/devtools/Cargo.toml @@ -17,6 +17,7 @@ dioxus-core = { workspace = true, features = ["serialize"] } dioxus-devtools-types = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } +tungstenite = { version = "0.23.0" } # hot reloading serve tracing = { workspace = true } diff --git a/packages/devtools/src/lib.rs b/packages/devtools/src/lib.rs index 2be9e7fa46..2c5ebf0760 100644 --- a/packages/devtools/src/lib.rs +++ b/packages/devtools/src/lib.rs @@ -1,6 +1,6 @@ use std::{ io::{self, BufRead}, - net::SocketAddr, + net::{SocketAddr, TcpListener, ToSocketAddrs}, }; use dioxus_core::{ScopeId, VirtualDom}; @@ -32,37 +32,64 @@ pub fn apply_changes(dom: &VirtualDom, msg: &HotReloadMsg) { /// Connect to the devserver and handle its messages with a callback. /// /// This doesn't use any form of security or protocol, so it's not safe to expose to the internet. -pub fn connect(addr: SocketAddr, mut callback: impl FnMut(DevserverMsg) + Send + 'static) { +pub fn connect(addr: String, mut callback: impl FnMut(DevserverMsg) + Send + 'static) { std::thread::spawn(move || { - let connect = std::net::TcpStream::connect(addr); - let Ok(mut stream) = connect else { - return; + let (mut websocket, req) = match tungstenite::connect(addr.clone()) { + Ok((websocket, req)) => (websocket, req), + Err(err) => { + eprintln!("Failed to connect to devserver at {} because {}", addr, err); + return; + } }; - // Wrap the stream in a BufReader, so we can use the BufRead methods - let mut reader = io::BufReader::new(&mut stream); + println!("Connected to devserver at {}", addr); - // Loop and read lines from the stream - loop { - let mut buf = String::new(); - let msg = reader.read_line(&mut buf); + while let Ok(msg) = websocket.read() { + match msg { + tungstenite::Message::Text(text) => { + if let Ok(msg) = serde_json::from_str(&text) { + callback(msg); + } else { + eprintln!("Failed to parse message from devserver: {:?}", text); + } + } + msg => { + println!("Received a non-text message: {:?}", msg); + } + } + } - let Ok(amt) = msg else { - break; - }; + eprintln!("Disconnected from devserver"); - // eof received - connection closed - if amt == 0 { - break; - } + // let connect = std::net::TcpStream::connect(addr); + // let Ok(mut stream) = connect else { + // return; + // }; - reader.consume(amt); + // // Wrap the stream in a BufReader, so we can use the BufRead methods + // let mut reader = io::BufReader::new(&mut stream); - if let Ok(msg) = serde_json::from_str(&buf) { - callback(msg); - } else { - eprintln!("Failed to parse message from devserver: {:?}", buf); - } - } + // // Loop and read lines from the stream + // loop { + // let mut buf = String::new(); + // let msg = reader.read_line(&mut buf); + + // let Ok(amt) = msg else { + // break; + // }; + + // // eof received - connection closed + // if amt == 0 { + // break; + // } + + // reader.consume(amt); + + // if let Ok(msg) = serde_json::from_str(&buf) { + // callback(msg); + // } else { + // eprintln!("Failed to parse message from devserver: {:?}", buf); + // } + // } }); } diff --git a/packages/mobile/Cargo.toml b/packages/mobile/Cargo.toml index 6e8f6cb121..bce9a513bd 100644 --- a/packages/mobile/Cargo.toml +++ b/packages/mobile/Cargo.toml @@ -10,12 +10,12 @@ keywords = ["dom", "ui", "gui", "react"] license = "MIT OR Apache-2.0" [dependencies] -dioxus-desktop = { workspace = true, features = ["tokio_runtime"] } +dioxus-desktop = { workspace = true } [lib] doctest = false -# tests suspended until package ready test = false +# tests suspended until package ready [package.metadata.docs.rs] cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"] diff --git a/packages/runtime-config/src/lib.rs b/packages/runtime-config/src/lib.rs index 47bedb76b9..dfee2e6b74 100644 --- a/packages/runtime-config/src/lib.rs +++ b/packages/runtime-config/src/lib.rs @@ -1,18 +1,20 @@ use std::net::SocketAddr; -pub const RAW_DEVSERVER_ADDR_ENV: &str = "DIOXUS_DEVSERVER_ADDR"; +pub const DEVSERVER_RAW_ADDR_ENV: &str = "DIOXUS_DEVSERVER_ADDR"; pub const FULLSTACK_ADDRESS_ENV: &str = "DIOXUS_FULLSTACK_ADDRESS"; /// when targetting ios, we need to set a prefix to the argument such that it gets picked up by simctl -pub const IOS_DEVSERVER_ADDR_ENV: &str = "SIMCTL_CHILD_DEVSERVER_ADDR"; +pub const IOS_DEVSERVER_ADDR_ENV: &str = "SIMCTL_CHILDDIOXUS_DEVSERVER_ADDR"; /// Get the address of the devserver for use over a raw socket /// /// This is not a websocket! There's no protocol! -pub fn devserver_raw_addr() -> Option { - std::env::var(RAW_DEVSERVER_ADDR_ENV) - .ok() - .and_then(|s| s.parse().ok()) +pub fn devserver_raw_addr() -> Option { + // #[cfg(target_os = "ios")] + // return std::env::var(IOS_DEVSERVER_ADDR_ENV).ok(); + + // #[cfg(not(target_os = "ios"))] + return std::env::var(DEVSERVER_RAW_ADDR_ENV).ok(); } pub fn fullstack_address() -> Option { From f3fc792d5c0c3c05fd3e647b1f7c38cd8e0f82f8 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 22 Aug 2024 21:26:10 -0700 Subject: [PATCH 054/139] cfg out tungstenite --- packages/cli/src/config/serve.rs | 2 +- packages/cli/src/serve/server.rs | 8 ++++---- packages/devtools/Cargo.toml | 4 +++- packages/devtools/src/lib.rs | 11 ++++++----- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/packages/cli/src/config/serve.rs b/packages/cli/src/config/serve.rs index e860539e74..39d24cb747 100644 --- a/packages/cli/src/config/serve.rs +++ b/packages/cli/src/config/serve.rs @@ -37,5 +37,5 @@ fn default_port() -> u16 { } fn default_address() -> IpAddr { - IpAddr::V4(std::net::Ipv4Addr::new(0, 0, 0, 0)) + IpAddr::V4(std::net::Ipv4Addr::new(127, 0, 0, 1)) } diff --git a/packages/cli/src/serve/server.rs b/packages/cli/src/serve/server.rs index 167e085088..0224dc2154 100644 --- a/packages/cli/src/serve/server.rs +++ b/packages/cli/src/serve/server.rs @@ -147,9 +147,9 @@ impl DevServer { let base_path = cfg.dioxus_config.web.app.base_path.clone(); let platform = serve.platform(); - let mut listener = std::net::TcpListener::bind(addr).expect("Failed to bind port"); - listener.set_nonblocking(true); - let ip = listener.local_addr().unwrap(); + let listener = std::net::TcpListener::bind(addr).expect("Failed to bind port"); + _ = listener.set_nonblocking(true); + let addr = listener.local_addr().unwrap(); let _server_task = tokio::spawn(async move { let web_config = web_config.clone(); @@ -187,7 +187,7 @@ impl DevServer { new_hot_reload_sockets: hot_reload_sockets_rx, new_build_status_sockets: build_status_sockets_rx, _server_task, - ip, + ip: addr, fullstack_port, build_status, diff --git a/packages/devtools/Cargo.toml b/packages/devtools/Cargo.toml index 1ab8d6ae2a..9d818a64bf 100644 --- a/packages/devtools/Cargo.toml +++ b/packages/devtools/Cargo.toml @@ -17,12 +17,14 @@ dioxus-core = { workspace = true, features = ["serialize"] } dioxus-devtools-types = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } -tungstenite = { version = "0.23.0" } # hot reloading serve tracing = { workspace = true } warnings.workspace = true +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +tungstenite = { version = "0.23.0" } + [dev-dependencies] tokio = { workspace = true, features = ["full"] } serde_json = "1.0.91" diff --git a/packages/devtools/src/lib.rs b/packages/devtools/src/lib.rs index 2c5ebf0760..3e372feee7 100644 --- a/packages/devtools/src/lib.rs +++ b/packages/devtools/src/lib.rs @@ -1,8 +1,3 @@ -use std::{ - io::{self, BufRead}, - net::{SocketAddr, TcpListener, ToSocketAddrs}, -}; - use dioxus_core::{ScopeId, VirtualDom}; pub use dioxus_devtools_types::*; use dioxus_signals::Writable; @@ -32,7 +27,13 @@ pub fn apply_changes(dom: &VirtualDom, msg: &HotReloadMsg) { /// Connect to the devserver and handle its messages with a callback. /// /// This doesn't use any form of security or protocol, so it's not safe to expose to the internet. +#[cfg(not(target_arch = "wasm32"))] pub fn connect(addr: String, mut callback: impl FnMut(DevserverMsg) + Send + 'static) { + use std::{ + io::{self, BufRead}, + net::{SocketAddr, TcpListener, ToSocketAddrs}, + }; + std::thread::spawn(move || { let (mut websocket, req) = match tungstenite::connect(addr.clone()) { Ok((websocket, req)) => (websocket, req), From daf235b2984df8a033eec1302858cd85ac5fffaa Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 23 Aug 2024 07:11:57 -0700 Subject: [PATCH 055/139] devserver err --- packages/cli/src/serve/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/serve/mod.rs b/packages/cli/src/serve/mod.rs index 0a625078e7..e9a01bdd66 100644 --- a/packages/cli/src/serve/mod.rs +++ b/packages/cli/src/serve/mod.rs @@ -156,7 +156,7 @@ pub async fn serve_all( } ServeUpdate::BuildFailed { err } => { - tracing::error!("Build failed with err: {err}"); + server.send_build_error(err).await; } ServeUpdate::BuildReady { results } => { From 12f3ae6b2c09e34f591bcd6fe6a3d3194d196a20 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Sun, 1 Sep 2024 13:39:17 -0700 Subject: [PATCH 056/139] more refactor to cli builder --- Cargo.lock | 23 +- packages/cli/Cargo.toml | 7 +- packages/cli/src/assets.rs | 79 --- packages/cli/src/assets/file.rs | 541 ++++++++++++++++++ packages/cli/src/assets/folder.rs | 59 -- packages/cli/src/assets/linker_intercept.rs | 187 ------ packages/cli/src/assets/manifest.rs | 406 +++++-------- packages/cli/src/builder/assets.rs | 166 ++++-- packages/cli/src/builder/bundle.rs | 13 + packages/cli/src/builder/cargo.rs | 8 +- packages/cli/src/builder/fullstack.rs | 38 +- packages/cli/src/builder/handle.rs | 10 + packages/cli/src/builder/mod.rs | 63 +- packages/cli/src/builder/platform.rs | 2 +- packages/cli/src/builder/web.rs | 7 +- packages/cli/src/cli/link.rs | 69 +-- packages/cli/src/cli/mod.rs | 5 - packages/cli/src/cli/serve.rs | 4 + packages/cli/src/config.rs | 6 +- packages/cli/src/config/platform.rs | 14 +- packages/cli/src/main.rs | 31 +- packages/cli/src/serve/builder.rs | 108 ++-- packages/cli/src/serve/detect.rs | 32 ++ packages/cli/src/serve/mod.rs | 160 +++--- packages/cli/src/serve/output.rs | 277 +++++---- packages/cli/src/serve/server.rs | 138 +++-- packages/cli/src/serve/update.rs | 3 +- packages/cli/src/serve/util.rs | 25 + packages/cli/src/serve/watcher.rs | 43 +- packages/devtools/Cargo.toml | 2 - .../interpreter/src/write_native_mutations.rs | 3 +- packages/manganis-core/Cargo.toml | 4 +- packages/manganis-core/src/asset.rs | 117 ++++ packages/manganis-core/src/folder.rs | 0 packages/manganis-core/src/lib.rs | 77 +-- packages/manganis-core/src/linker.rs | 69 +++ packages/manganis-core/src/options.rs | 0 packages/manganis-macro/Cargo.toml | 3 +- packages/manganis-macro/src/asset.rs | 186 ++---- packages/manganis-macro/src/asset_options.rs | 77 +++ packages/manganis-macro/src/lib.rs | 123 +--- packages/manganis-macro/src/linker.rs | 100 ++++ packages/manganis/src/builder.rs | 376 ++---------- packages/manganis/src/folder.rs | 14 + packages/manganis/src/images.rs | 15 + packages/manganis/src/lib.rs | 50 +- packages/manganis/src/new.rs | 141 +++++ packages/web/Cargo.toml | 6 +- packages/web/src/devtools.rs | 8 +- 49 files changed, 2037 insertions(+), 1858 deletions(-) delete mode 100644 packages/cli/src/assets/folder.rs delete mode 100644 packages/cli/src/assets/linker_intercept.rs create mode 100644 packages/cli/src/builder/bundle.rs create mode 100644 packages/cli/src/builder/handle.rs create mode 100644 packages/cli/src/serve/detect.rs create mode 100644 packages/cli/src/serve/util.rs create mode 100644 packages/manganis-core/src/asset.rs create mode 100644 packages/manganis-core/src/folder.rs create mode 100644 packages/manganis-core/src/linker.rs create mode 100644 packages/manganis-core/src/options.rs create mode 100644 packages/manganis-macro/src/asset_options.rs create mode 100644 packages/manganis-macro/src/linker.rs create mode 100644 packages/manganis/src/new.rs diff --git a/Cargo.lock b/Cargo.lock index 8753e3e2f3..71cb83c06b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2407,7 +2407,6 @@ dependencies = [ "hyper-util", "ignore", "krates", - "log", "manganis-core", "notify", "object", @@ -2554,8 +2553,6 @@ version = "0.6.0-alpha.2" dependencies = [ "dioxus-core", "dioxus-devtools-types", - "dioxus-html", - "dioxus-rsx", "dioxus-signals", "serde", "serde_json", @@ -2997,7 +2994,7 @@ dependencies = [ "lazy-js-bundle", "rustc-hash 1.1.0", "serde", - "serde-wasm-bindgen", + "serde-wasm-bindgen 0.6.5", "serde_json", "tracing", "tracing-wasm", @@ -4509,7 +4506,7 @@ dependencies = [ "gloo-events", "gloo-utils 0.1.7", "serde", - "serde-wasm-bindgen", + "serde-wasm-bindgen 0.5.0", "serde_urlencoded", "thiserror", "wasm-bindgen", @@ -5942,11 +5939,16 @@ dependencies = [ [[package]] name = "manganis-core" version = "0.6.0-alpha.2" +dependencies = [ + "serde", + "serde_json", +] [[package]] name = "manganis-macro" version = "0.6.0-alpha.2" dependencies = [ + "manganis-core", "proc-macro2", "quote", "serde", @@ -8352,6 +8354,17 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "serde-wasm-bindgen" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + [[package]] name = "serde_derive" version = "1.0.208" diff --git a/packages/cli/Cargo.toml b/packages/cli/Cargo.toml index 3726274a43..fe93d09eb0 100644 --- a/packages/cli/Cargo.toml +++ b/packages/cli/Cargo.toml @@ -18,7 +18,6 @@ wasm-bindgen-shared = "0.2" # features uuid = { version = "1.3.0", features = ["v4"] } -log = "0.4.14" serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } toml = { workspace = true } @@ -106,7 +105,8 @@ ansi-to-tui = "=5.0.0-rc.1" ansi-to-html = "0.2.1" manganis-core = { workspace = true } - +# link intercept +tempfile = "3.3" # # just use the manganis crate directly since it has all the types we need @@ -174,8 +174,7 @@ wasm-opt = ["dep:wasm-opt"] path = "src/main.rs" name = "dx" -[dev-dependencies] -tempfile = "3.3" + [package.metadata.binstall] pkg-url = "{ repo }/releases/download/v{ version }/dx-{ target }-v{ version }{ archive-suffix }" diff --git a/packages/cli/src/assets.rs b/packages/cli/src/assets.rs index 876196f50d..490dcc8912 100644 --- a/packages/cli/src/assets.rs +++ b/packages/cli/src/assets.rs @@ -5,7 +5,6 @@ use crate::Result; use anyhow::Context; use brotli::enc::BrotliEncoderParams; use futures_channel::mpsc::UnboundedSender; -// use manganis_cli_support::{process_file, AssetManifest, AssetManifestExt, AssetType}; use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use std::fs; use std::path::Path; @@ -17,90 +16,12 @@ use tracing::Level; use walkdir::WalkDir; mod file; -mod folder; -mod linker_intercept; mod manifest; // pub use file::process_file; // pub use folder::process_folder; -pub use linker_intercept::*; pub use manifest::*; -/// The temp file name for passing manganis json from linker to current exec. -pub const MG_JSON_OUT: &str = "mg-out"; - -// pub fn asset_manifest(build: &BuildRequest) -> AssetManifest { -// let file_path = build.target_out_dir().join(MG_JSON_OUT); -// let read = fs::read_to_string(&file_path).unwrap_or_default(); -// _ = fs::remove_file(file_path); -// let json: Vec = serde_json::from_str(&read).unwrap_or_default(); - -// AssetManifest::load(json) -// } - -// /// Create a head file that contains all of the imports for assets that the user project uses -// pub fn create_assets_head(build: &BuildRequest, manifest: &AssetManifest) -> Result<()> { -// let out_dir = build.target_out_dir(); -// std::fs::create_dir_all(&out_dir)?; -// let mut file = File::create(out_dir.join("__assets_head.html"))?; -// file.write_all(manifest.head().as_bytes())?; -// Ok(()) -// } - -// /// Process any assets collected from the binary -// pub(crate) fn process_assets( -// build: &BuildRequest, -// manifest: &AssetManifest, -// progress: &mut UnboundedSender, -// ) -> anyhow::Result<()> { -// let static_asset_output_dir = build.target_out_dir(); - -// std::fs::create_dir_all(&static_asset_output_dir) -// .context("Failed to create static asset output directory")?; - -// let assets_finished = Arc::new(AtomicUsize::new(0)); -// let assets = manifest.assets(); -// let asset_count = assets.len(); -// assets.par_iter().try_for_each_init( -// || progress.clone(), -// move |progress, asset| { -// // if let AssetType::File(file_asset) = asset { -// // match process_file(file_asset, &static_asset_output_dir) { -// // Ok(_) => { -// // // Update the progress -// // _ = progress.start_send(UpdateBuildProgress { -// // stage: Stage::OptimizingAssets, -// // update: UpdateStage::AddMessage(BuildMessage { -// // level: Level::INFO, -// // message: MessageType::Text(format!( -// // "Optimized static asset {}", -// // file_asset -// // )), -// // source: MessageSource::Build, -// // }), -// // }); -// // let assets_finished = -// // assets_finished.fetch_add(1, std::sync::atomic::Ordering::SeqCst); -// // _ = progress.start_send(UpdateBuildProgress { -// // stage: Stage::OptimizingAssets, -// // update: UpdateStage::SetProgress( -// // assets_finished as f64 / asset_count as f64, -// // ), -// // }); -// // } -// // Err(err) => { -// // tracing::error!("Failed to copy static asset: {}", err); -// // return Err(err); -// // } -// // } -// // } -// Ok::<(), anyhow::Error>(()) -// }, -// )?; - -// Ok(()) -// } - pub(crate) fn copy_dir_to( src_dir: PathBuf, dest_dir: PathBuf, diff --git a/packages/cli/src/assets/file.rs b/packages/cli/src/assets/file.rs index a453632161..1922154b85 100644 --- a/packages/cli/src/assets/file.rs +++ b/packages/cli/src/assets/file.rs @@ -1,4 +1,6 @@ use anyhow::Context; +// use manganis_common::{FileOptions, FolderAsset}; +use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; // use image::{DynamicImage, EncodableLayout}; // use lightningcss::stylesheet::{MinifyOptions, ParserOptions, PrinterOptions, StyleSheet}; // use manganis_common::{ @@ -277,3 +279,542 @@ use std::{ // Ok(()) // } // } + +// /// Process a folder, optimizing and copying all assets into the output folder +// pub fn process_folder(folder: &FolderAsset, output_folder: &Path) -> anyhow::Result<()> { +// // Push the unique name of the folder to the output folder +// let output_folder = output_folder.join(folder.unique_name()); + +// if output_folder.exists() { +// return Ok(()); +// } + +// // .location() +// // // .source() +// // .as_path() +// let folder = folder.path(); + +// // Optimize and copy all assets in the folder in parallel +// process_folder_inner(folder, &output_folder) +// } + +// fn process_folder_inner(folder: &Path, output_folder: &Path) -> anyhow::Result<()> { +// // Create the folder +// std::fs::create_dir_all(output_folder)?; + +// // Then optimize children +// let files: Vec<_> = std::fs::read_dir(folder) +// .into_iter() +// .flatten() +// .flatten() +// .collect(); + +// files.par_iter().try_for_each(|file| { +// let file = file.path(); +// let metadata = file.metadata()?; +// let output_path = output_folder.join(file.strip_prefix(folder)?); +// if metadata.is_dir() { +// process_folder_inner(&file, &output_path) +// } else { +// process_file_minimal(&file, &output_path) +// } +// })?; + +// Ok(()) +// } + +// /// Optimize a file without changing any of its contents significantly (e.g. by changing the extension) +// fn process_file_minimal(input_path: &Path, output_path: &Path) -> anyhow::Result<()> { +// todo!() +// // let options = +// // FileOptions::default_for_extension(input_path.extension().and_then(|e| e.to_str())); +// // let source = input_path.to_path_buf(); +// // options.process(&source, output_path)?; +// // Ok(()) +// } + +// use image::{DynamicImage, EncodableLayout}; +// use lightningcss::stylesheet::{MinifyOptions, ParserOptions, PrinterOptions, StyleSheet}; +// use manganis_common::{ +// CssOptions, FileOptions, ImageOptions, ImageType, JsOptions, JsonOptions, ResourceAsset, +// }; + +// use swc::{config::JsMinifyOptions, try_with_handler, BoolOrDataConfig}; +// use swc_common::{sync::Lrc, FileName}; +// use swc_common::{SourceMap, GLOBALS}; + +// pub trait Process { +// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()>; +// } + +// /// Process a specific file asset +// pub fn process_file(file: &ResourceAsset, output_folder: &Path) -> anyhow::Result<()> { +// todo!() +// // let location = file.location(); +// // let source = location.source(); +// // let output_path = output_folder.join(location.unique_name()); +// // file.options().process(source, &output_path) +// } + +// impl Process for FileOptions { +// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { +// if output_path.exists() { +// return Ok(()); +// } +// match self { +// Self::Other { .. } => { +// let bytes = source.read_to_bytes()?; +// std::fs::write(output_path, bytes).with_context(|| { +// format!( +// "Failed to write file to output location: {}", +// output_path.display() +// ) +// })?; +// } +// Self::Css(options) => { +// options.process(source, output_path)?; +// } +// Self::Js(options) => { +// options.process(source, output_path)?; +// } +// Self::Json(options) => { +// options.process(source, output_path)?; +// } +// Self::Image(options) => { +// options.process(source, output_path)?; +// } +// _ => todo!(), +// } + +// Ok(()) +// } +// } + +// impl Process for ImageOptions { +// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { +// let mut image = image::ImageReader::new(std::io::Cursor::new(&*source.read_to_bytes()?)) +// .with_guessed_format()? +// .decode()?; + +// if let Some(size) = self.size() { +// image = image.resize_exact(size.0, size.1, image::imageops::FilterType::Lanczos3); +// } + +// match self.ty() { +// ImageType::Png => { +// compress_png(image, output_path); +// } +// ImageType::Jpg => { +// compress_jpg(image, output_path)?; +// } +// ImageType::Avif => { +// if let Err(error) = image.save(output_path) { +// tracing::error!("Failed to save avif image: {} with path {}. You must have the avif feature enabled to use avif assets", error, output_path.display()); +// } +// } +// ImageType::Webp => { +// if let Err(err) = image.save(output_path) { +// tracing::error!("Failed to save webp image: {}. You must have the avif feature enabled to use webp assets", err); +// } +// } +// } + +// Ok(()) +// } +// } + +// fn compress_jpg(image: DynamicImage, output_location: &Path) -> anyhow::Result<()> { +// let mut comp = mozjpeg::Compress::new(mozjpeg::ColorSpace::JCS_EXT_RGBX); +// let width = image.width() as usize; +// let height = image.height() as usize; + +// comp.set_size(width, height); +// let mut comp = comp.start_compress(Vec::new())?; // any io::Write will work + +// comp.write_scanlines(image.to_rgba8().as_bytes())?; + +// let jpeg_bytes = comp.finish()?; + +// let file = std::fs::File::create(output_location)?; +// let w = &mut BufWriter::new(file); +// w.write_all(&jpeg_bytes)?; +// Ok(()) +// } + +// fn compress_png(image: DynamicImage, output_location: &Path) { +// // Image loading/saving is outside scope of this library +// let width = image.width() as usize; +// let height = image.height() as usize; +// let bitmap: Vec<_> = image +// .into_rgba8() +// .pixels() +// .map(|px| imagequant::RGBA::new(px[0], px[1], px[2], px[3])) +// .collect(); + +// // Configure the library +// let mut liq = imagequant::new(); +// liq.set_speed(5).unwrap(); +// liq.set_quality(0, 99).unwrap(); + +// // Describe the bitmap +// let mut img = liq.new_image(&bitmap[..], width, height, 0.0).unwrap(); + +// // The magic happens in quantize() +// let mut res = match liq.quantize(&mut img) { +// Ok(res) => res, +// Err(err) => panic!("Quantization failed, because: {err:?}"), +// }; + +// let (palette, pixels) = res.remapped(&mut img).unwrap(); + +// let file = std::fs::File::create(output_location).unwrap(); +// let w = &mut BufWriter::new(file); + +// let mut encoder = png::Encoder::new(w, width as u32, height as u32); +// encoder.set_color(png::ColorType::Rgba); +// let mut flattened_palette = Vec::new(); +// let mut alpha_palette = Vec::new(); +// for px in palette { +// flattened_palette.push(px.r); +// flattened_palette.push(px.g); +// flattened_palette.push(px.b); +// alpha_palette.push(px.a); +// } +// encoder.set_palette(flattened_palette); +// encoder.set_trns(alpha_palette); +// encoder.set_depth(png::BitDepth::Eight); +// encoder.set_color(png::ColorType::Indexed); +// encoder.set_compression(png::Compression::Best); +// let mut writer = encoder.write_header().unwrap(); +// writer.write_image_data(&pixels).unwrap(); +// writer.finish().unwrap(); +// } + +// impl Process for CssOptions { +// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { +// let css = source.read_to_string()?; + +// let css = if self.minify() { minify_css(&css) } else { css }; + +// std::fs::write(output_path, css).with_context(|| { +// format!( +// "Failed to write css to output location: {}", +// output_path.display() +// ) +// })?; + +// Ok(()) +// } +// } + +// pub(crate) fn minify_css(css: &str) -> String { +// let mut stylesheet = StyleSheet::parse(css, ParserOptions::default()).unwrap(); +// stylesheet.minify(MinifyOptions::default()).unwrap(); +// let printer = PrinterOptions { +// minify: true, +// ..Default::default() +// }; +// let res = stylesheet.to_css(printer).unwrap(); +// res.code +// } + +// pub(crate) fn minify_js(source: &ResourceAsset) -> anyhow::Result { +// todo!("disabled swc due to semver issues") +// // let cm = Arc::::default(); + +// // let js = source.read_to_string()?; +// // let c = swc::Compiler::new(cm.clone()); +// // let output = GLOBALS +// // .set(&Default::default(), || { +// // try_with_handler(cm.clone(), Default::default(), |handler| { +// // // let filename = Lrc::new(match source { +// // // manganis_common::ResourceAsset::Local(path) => { +// // // FileName::Real(path.canonicalized.clone()) +// // // } +// // // manganis_common::ResourceAsset::Remote(url) => FileName::Url(url.clone()), +// // // }); +// // let filename = todo!(); +// // let fm = cm.new_source_file(filename, js.to_string()); + +// // c.minify( +// // fm, +// // handler, +// // &JsMinifyOptions { +// // compress: BoolOrDataConfig::from_bool(true), +// // mangle: BoolOrDataConfig::from_bool(true), +// // ..Default::default() +// // }, +// // ) +// // .context("failed to minify javascript") +// // }) +// // }) +// // .map(|output| output.code); + +// // match output { +// // Ok(output) => Ok(output), +// // Err(err) => { +// // tracing::error!("Failed to minify javascript: {}", err); +// // Ok(js) +// // } +// // } +// } + +// impl Process for JsOptions { +// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { +// let js = if self.minify() { +// minify_js(source)? +// } else { +// source.read_to_string()? +// }; + +// std::fs::write(output_path, js).with_context(|| { +// format!( +// "Failed to write js to output location: {}", +// output_path.display() +// ) +// })?; + +// Ok(()) +// } +// } + +// pub(crate) fn minify_json(source: &str) -> anyhow::Result { +// // First try to parse the json +// let json: serde_json::Value = serde_json::from_str(source)?; +// // Then print it in a minified format +// let json = serde_json::to_string(&json)?; +// Ok(json) +// } + +// impl Process for JsonOptions { +// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { +// let source = source.read_to_string()?; +// let json = match minify_json(&source) { +// Ok(json) => json, +// Err(err) => { +// tracing::error!("Failed to minify json: {}", err); +// source +// } +// }; + +// std::fs::write(output_path, json).with_context(|| { +// format!( +// "Failed to write json to output location: {}", +// output_path.display() +// ) +// })?; + +// Ok(()) +// } +// } + +// /// Returns the HTML that should be injected into the head of the page +// pub fn head(&self) -> String { +// let mut head = String::new(); +// for asset in &self.assets { +// if let crate::AssetType::Resource(file) = asset { +// match file.options() { +// crate::FileOptions::Css(css_options) => { +// if css_options.preload() { +// if let Ok(asset_path) = file.served_location() { +// head.push_str(&format!( +// "\n" +// )) +// } +// } +// } +// crate::FileOptions::Image(image_options) => { +// if image_options.preload() { +// if let Ok(asset_path) = file.served_location() { +// head.push_str(&format!( +// "\n" +// )) +// } +// } +// } +// crate::FileOptions::Js(js_options) => { +// if js_options.preload() { +// if let Ok(asset_path) = file.served_location() { +// head.push_str(&format!( +// "\n" +// )) +// } +// } +// } +// _ => {} +// } +// } +// } +// head +// } + +// /// An extension trait CLI support for the asset manifest +// pub trait AssetManifestExt { +// /// Load a manifest from a list of Manganis JSON strings. +// /// +// /// The asset descriptions are stored inside a manifest file that is produced when the linker is intercepted. +// fn load(json: Vec) -> Self; +// /// Load a manifest from the assets propogated through object files. +// /// +// /// The asset descriptions are stored inside a manifest file that is produced when the linker is intercepted. +// fn load_from_objects(object_paths: Vec) -> Self; +// /// Optimize and copy all assets in the manifest to a folder +// fn copy_static_assets_to(&self, location: impl Into) -> anyhow::Result<()>; +// /// Collect all tailwind classes and generate string with the output css +// fn collect_tailwind_css( +// &self, +// include_preflight: bool, +// warnings: &mut Vec, +// ) -> String; +// } + +// impl AssetManifestExt for AssetManifest { +// fn load(json: Vec) -> Self { +// let mut all_assets = Vec::new(); + +// // Collect all assets for each manganis string found. +// for item in json { +// let mut assets = deserialize_assets(item.as_str()); +// all_assets.append(&mut assets); +// } + +// // If we don't see any manganis assets used in the binary, just return an empty manifest +// if all_assets.is_empty() { +// return Self::default(); +// }; + +// Self::new(all_assets) +// } + +// fn load_from_objects(object_files: Vec) -> Self { +// let json = get_json_from_object_files(object_files); +// Self::load(json) +// } + +// fn copy_static_assets_to(&self, location: impl Into) -> anyhow::Result<()> { +// let location = location.into(); +// match std::fs::create_dir_all(&location) { +// Ok(_) => {} +// Err(err) => { +// tracing::error!("Failed to create directory for static assets: {}", err); +// return Err(err.into()); +// } +// } + +// self.assets().iter().try_for_each(|asset| { +// match asset { +// AssetType::Resource(file_asset) => { +// tracing::info!("Optimizing and bundling {:?}", file_asset); +// tracing::trace!("Copying asset from {:?} to {:?}", file_asset, location); +// match process_file(file_asset, &location) { +// Ok(_) => {} +// Err(err) => { +// tracing::error!("Failed to copy static asset: {}", err); +// return Err(err); +// } +// } + +// // tracing::info!("Copying folder asset {}", folder_asset); +// // match process_folder(folder_asset, &location) { +// // Ok(_) => {} +// // Err(err) => { +// // tracing::error!("Failed to copy static asset: {}", err); +// // return Err(err); +// // } +// // } +// } + +// _ => {} +// } +// Ok::<(), anyhow::Error>(()) +// }) +// } + +// // fn collect_tailwind_css( +// // self: &AssetManifest, +// // include_preflight: bool, +// // warnings: &mut Vec, +// // ) -> String { +// // let mut all_classes = String::new(); + +// // for asset in self.assets() { +// // if let AssetType::Tailwind(classes) = asset { +// // all_classes.push_str(classes.classes()); +// // all_classes.push(' '); +// // } +// // } + +// // let source = railwind::Source::String(all_classes, railwind::CollectionOptions::String); + +// // let css = railwind::parse_to_string(source, include_preflight, warnings); + +// // crate::file::minify_css(&css) +// // } +// } + +// The temp file name for passing manganis json from linker to current exec. +// pub const MG_JSON_OUT: &str = "mg-out"; + +// /// Create a head file that contains all of the imports for assets that the user project uses +// pub fn create_assets_head(build: &BuildRequest, manifest: &AssetManifest) -> Result<()> { +// let out_dir = build.target_out_dir(); +// std::fs::create_dir_all(&out_dir)?; +// let mut file = File::create(out_dir.join("__assets_head.html"))?; +// file.write_all(manifest.head().as_bytes())?; +// Ok(()) +// } + +// use crate::file::Process; + +// /// Process a folder, optimizing and copying all assets into the output folder +// pub fn process_folder(folder: &FolderAsset, output_folder: &Path) -> anyhow::Result<()> { +// // Push the unique name of the folder to the output folder +// let output_folder = output_folder.join(folder.unique_name()); + +// if output_folder.exists() { +// return Ok(()); +// } + +// // .location() +// // // .source() +// // .as_path() +// let folder = folder.path(); + +// // Optimize and copy all assets in the folder in parallel +// process_folder_inner(folder, &output_folder) +// } + +// fn process_folder_inner(folder: &Path, output_folder: &Path) -> anyhow::Result<()> { +// // Create the folder +// std::fs::create_dir_all(output_folder)?; + +// // Then optimize children +// let files: Vec<_> = std::fs::read_dir(folder) +// .into_iter() +// .flatten() +// .flatten() +// .collect(); + +// files.par_iter().try_for_each(|file| { +// let file = file.path(); +// let metadata = file.metadata()?; +// let output_path = output_folder.join(file.strip_prefix(folder)?); +// if metadata.is_dir() { +// process_folder_inner(&file, &output_path) +// } else { +// process_file_minimal(&file, &output_path) +// } +// })?; + +// Ok(()) +// } + +// /// Optimize a file without changing any of its contents significantly (e.g. by changing the extension) +// fn process_file_minimal(input_path: &Path, output_path: &Path) -> anyhow::Result<()> { +// todo!() +// // let options = +// // FileOptions::default_for_extension(input_path.extension().and_then(|e| e.to_str())); +// // let source = input_path.to_path_buf(); +// // options.process(&source, output_path)?; +// // Ok(()) +// } diff --git a/packages/cli/src/assets/folder.rs b/packages/cli/src/assets/folder.rs deleted file mode 100644 index 0b4d2232a4..0000000000 --- a/packages/cli/src/assets/folder.rs +++ /dev/null @@ -1,59 +0,0 @@ -use std::path::Path; - -// use manganis_common::{FileOptions, FolderAsset}; -use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; - -// use crate::file::Process; - -// /// Process a folder, optimizing and copying all assets into the output folder -// pub fn process_folder(folder: &FolderAsset, output_folder: &Path) -> anyhow::Result<()> { -// // Push the unique name of the folder to the output folder -// let output_folder = output_folder.join(folder.unique_name()); - -// if output_folder.exists() { -// return Ok(()); -// } - -// // .location() -// // // .source() -// // .as_path() -// let folder = folder.path(); - -// // Optimize and copy all assets in the folder in parallel -// process_folder_inner(folder, &output_folder) -// } - -// fn process_folder_inner(folder: &Path, output_folder: &Path) -> anyhow::Result<()> { -// // Create the folder -// std::fs::create_dir_all(output_folder)?; - -// // Then optimize children -// let files: Vec<_> = std::fs::read_dir(folder) -// .into_iter() -// .flatten() -// .flatten() -// .collect(); - -// files.par_iter().try_for_each(|file| { -// let file = file.path(); -// let metadata = file.metadata()?; -// let output_path = output_folder.join(file.strip_prefix(folder)?); -// if metadata.is_dir() { -// process_folder_inner(&file, &output_path) -// } else { -// process_file_minimal(&file, &output_path) -// } -// })?; - -// Ok(()) -// } - -// /// Optimize a file without changing any of its contents significantly (e.g. by changing the extension) -// fn process_file_minimal(input_path: &Path, output_path: &Path) -> anyhow::Result<()> { -// todo!() -// // let options = -// // FileOptions::default_for_extension(input_path.extension().and_then(|e| e.to_str())); -// // let source = input_path.to_path_buf(); -// // options.process(&source, output_path)?; -// // Ok(()) -// } diff --git a/packages/cli/src/assets/linker_intercept.rs b/packages/cli/src/assets/linker_intercept.rs deleted file mode 100644 index bd8e65000a..0000000000 --- a/packages/cli/src/assets/linker_intercept.rs +++ /dev/null @@ -1,187 +0,0 @@ -use std::{ffi::OsStr, fs, path::PathBuf, process::Stdio}; - -// The prefix to link args passed from parent process. -const MG_ARG_NAME: &str = "mg-arg="; - -/// Intercept the linker for object files. -/// -/// Takes the arguments used in a CLI and returns a list of paths to `.rlib` or `.o` files to be searched for asset sections. -pub fn linker_intercept(args: I) -> Option<(Vec, Vec)> -where - I: IntoIterator, - T: ToString, -{ - let args: Vec = args.into_iter().map(|x| x.to_string()).collect(); - - // Check if we were provided with a command file. - let mut is_command_file = None; - for arg in args.iter() { - // On windows the linker args are passed in a file that is referenced by `@` - if arg.starts_with('@') { - is_command_file = Some(arg.clone()); - break; - } - } - - let linker_args = match is_command_file { - // On unix/linux/mac the linker args are passed directly. - None => args, - // Handle windows here - uf16le and utf8 files are supported. - Some(arg) => { - let path = arg.trim().trim_start_matches('@'); - let file_binary = fs::read(path).unwrap(); - - // This may be a utf-16le file. Let's try utf-8 first. - let content = match String::from_utf8(file_binary.clone()) { - Ok(s) => s, - Err(_) => { - // Convert Vec to Vec to convert into a String - let binary_u16le: Vec = file_binary - .chunks_exact(2) - .map(|a| u16::from_le_bytes([a[0], a[1]])) - .collect(); - - String::from_utf16_lossy(&binary_u16le) - } - }; - - // Gather linker args - let mut linker_args = Vec::new(); - let lines = content.lines(); - - for line in lines { - // Remove quotes from the line - windows link args files are quoted - let line_parsed = line.to_string(); - let line_parsed = line_parsed.trim_end_matches('"').to_string(); - let line_parsed = line_parsed.trim_start_matches('"').to_string(); - - linker_args.push(line_parsed); - } - - linker_args - } - }; - - let mut link_args = Vec::new(); - - // Parse through linker args for `.o` or `.rlib` files. - let mut object_files: Vec = Vec::new(); - for item in linker_args { - // Get the working directory so it isn't lost. - // When rust calls the linker it doesn't pass the working dir so we need to recover it. - // "{MG_WORKDIR_ARG_NAME}path" - if item.starts_with(MG_ARG_NAME) { - let split: Vec<_> = item.split('=').collect(); - link_args.push(split[1].to_string()); - continue; - } - - if item.ends_with(".o") || item.ends_with(".rlib") { - object_files.push(PathBuf::from(item)); - } - } - - if object_files.is_empty() { - return None; - } - - Some((link_args, object_files)) -} - -/// Calls cargo to build the project with a linker intercept script. -/// -/// The linker intercept script will call the current executable with the specified subcommand -/// and a list of arguments provided by rustc. -pub fn start_linker_intercept( - subcommand: &str, - args: I, - link_args: Option, -) -> Result<(), std::io::Error> -where - I: IntoIterator, - I::Item: AsRef, - J: IntoIterator, - J::Item: ToString, -{ - let exec_path = std::env::current_exe().unwrap(); - - let mut cmd = std::process::Command::new("cargo"); - - cmd.arg("rustc"); - cmd.args(args); - cmd.arg("--"); - - // Build a temporary redirect script. - let script_path = create_linker_script(exec_path, subcommand).unwrap(); - let linker_arg = format!("-Clinker={}", script_path.display()); - cmd.arg(linker_arg); - - // Handle passing any arguments back to the current executable. - if let Some(link_args) = link_args { - let link_args: Vec = link_args.into_iter().map(|x| x.to_string()).collect(); - for link_arg in link_args { - let arg = format!("-Clink-arg={}{}", MG_ARG_NAME, link_arg); - cmd.arg(arg); - } - } - - cmd.stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .spawn()? - .wait()?; - Ok(()) -} - -const LINK_SCRIPT_NAME: &str = "mg-link"; - -/// Creates a temporary script that re-routes rustc linker args to a subcommand of an executable. -fn create_linker_script(exec: PathBuf, subcommand: &str) -> Result { - #[cfg(windows)] - let (script, ext) = ( - format!("echo off\n{} {} %*", exec.display(), subcommand), - "bat", - ); - #[cfg(not(windows))] - let (script, ext) = ( - format!("#!/usr/bin/env bash\n{} {} $@", exec.display(), subcommand), - "sh", - ); - - let temp_path = std::env::temp_dir(); - let out_name = format!("{LINK_SCRIPT_NAME}.{ext}"); - let out = temp_path.join(out_name); - fs::write(&out, script)?; - - // Set executable permissions. - let mut perms = fs::metadata(&out)?.permissions(); - - // We give windows RW and implied X perms. - // Clippy complains on any platform about this even if it's not *nix. - // https://rust-lang.github.io/rust-clippy/master/index.html#permissions_set_readonly_false - #[cfg(windows)] - #[allow(clippy::permissions_set_readonly_false)] - perms.set_readonly(false); - - // We give nix user-RWX perms. - #[cfg(not(windows))] - { - use std::os::unix::fs::PermissionsExt; - perms.set_mode(0o700); - } - fs::set_permissions(&out, perms)?; - - Ok(out) -} - -/// Deletes the temporary script created by `create_linker_script`. -pub fn delete_linker_script() -> Result<(), std::io::Error> { - #[cfg(windows)] - let ext = "bat"; - #[cfg(not(windows))] - let ext = "sh"; - - let temp_path = std::env::temp_dir(); - let file_name = format!("{LINK_SCRIPT_NAME}.{ext}"); - let file = temp_path.join(file_name); - fs::remove_file(file) -} diff --git a/packages/cli/src/assets/manifest.rs b/packages/cli/src/assets/manifest.rs index c40582d32b..6e256d0c21 100644 --- a/packages/cli/src/assets/manifest.rs +++ b/packages/cli/src/assets/manifest.rs @@ -1,292 +1,192 @@ -use manganis_core::LinkSection; -use object::{Object, ObjectSection}; -use std::path::PathBuf; -use std::{collections::HashMap, fs}; +use manganis_core::{LinkSection, ResourceAsset}; +use object::{read::archive::ArchiveFile, File as ObjectFile, Object, ObjectSection}; +use std::{collections::HashMap, path::PathBuf}; -// pub use railwind::warning::Warning as TailwindWarning; -// use crate::{file::process_file, process_folder}; -// use manganis_common::{linker, AssetType}; - -// get the text containing all the asset descriptions -// in the "link section" of the binary -fn get_string_manganis(file: &object::File) -> Option { - for section in file.sections() { - let Ok(section_name) = section.name() else { - continue; - }; - - // Check if the link section matches the asset section for one of the platforms we support. This may not be the current platform if the user is cross compiling - let matches = LinkSection::ALL - .iter() - .any(|x| x.link_section == section_name); - - if !matches { - continue; - } - - let bytes = section.uncompressed_data().ok()?; - - // Some platforms (e.g. macOS) start the manganis section with a null byte, we need to filter that out before we deserialize the JSON - return Some( - std::str::from_utf8(&bytes) - .ok()? - .chars() - .filter(|c| !c.is_control()) - .collect::(), - ); - } - - None -} +use crate::link::InterceptedArgs; /// A manifest of all assets collected from dependencies +/// +/// This will be filled in primarly by incremental compilation artifacts. #[derive(Debug, PartialEq, Default, Clone)] pub struct AssetManifest { - pub(crate) assets: Vec, - pub(crate) asset_map: HashMap, -} - -#[derive(Debug, PartialEq, Clone)] -pub enum AssetType { - File(PathBuf), - Folder(PathBuf), + /// Map of asset pathbuf to its + pub(crate) assets: HashMap, } impl AssetManifest { /// Creates a new asset manifest - pub fn new(assets: Vec) -> Self { - let mut asset_map = HashMap::new(); - for asset in assets.iter() { - match asset { - AssetType::File(path) => asset_map.insert(path.clone(), asset.clone()), - AssetType::Folder(path) => asset_map.insert(path.clone(), asset.clone()), - }; + pub fn new() -> Self { + Self { + ..Default::default() } - Self { assets, asset_map } } - /// Returns all assets collected from dependencies - pub fn assets(&self) -> &Vec { - &self.assets + /// Fill this manifest from the intercepted rustc args used to link the app together + pub fn add_from_linker_intercept(&mut self, args: InterceptedArgs) { + // Attempt to load the arg as a command file, otherwise just use the args themselves + // This is because windows will pass in `@linkerargs.txt` as a source of linker args + if let Some(command) = args.args.iter().find(|arg| arg.starts_with('@')).cloned() { + self.add_from_command_file(args, &command); + } else { + self.add_from_linker_args(args); + } } - // /// Returns the HTML that should be injected into the head of the page - // pub fn head(&self) -> String { - // let mut head = String::new(); - // for asset in &self.assets { - // if let crate::AssetType::Resource(file) = asset { - // match file.options() { - // crate::FileOptions::Css(css_options) => { - // if css_options.preload() { - // if let Ok(asset_path) = file.served_location() { - // head.push_str(&format!( - // "\n" - // )) - // } - // } - // } - // crate::FileOptions::Image(image_options) => { - // if image_options.preload() { - // if let Ok(asset_path) = file.served_location() { - // head.push_str(&format!( - // "\n" - // )) - // } - // } - // } - // crate::FileOptions::Js(js_options) => { - // if js_options.preload() { - // if let Ok(asset_path) = file.served_location() { - // head.push_str(&format!( - // "\n" - // )) - // } - // } - // } - // _ => {} - // } - // } - // } - // head - // } -} - -// /// An extension trait CLI support for the asset manifest -// pub trait AssetManifestExt { -// /// Load a manifest from a list of Manganis JSON strings. -// /// -// /// The asset descriptions are stored inside a manifest file that is produced when the linker is intercepted. -// fn load(json: Vec) -> Self; -// /// Load a manifest from the assets propogated through object files. -// /// -// /// The asset descriptions are stored inside a manifest file that is produced when the linker is intercepted. -// fn load_from_objects(object_paths: Vec) -> Self; -// /// Optimize and copy all assets in the manifest to a folder -// fn copy_static_assets_to(&self, location: impl Into) -> anyhow::Result<()>; -// /// Collect all tailwind classes and generate string with the output css -// fn collect_tailwind_css( -// &self, -// include_preflight: bool, -// warnings: &mut Vec, -// ) -> String; -// } - -// impl AssetManifestExt for AssetManifest { -// fn load(json: Vec) -> Self { -// let mut all_assets = Vec::new(); - -// // Collect all assets for each manganis string found. -// for item in json { -// let mut assets = deserialize_assets(item.as_str()); -// all_assets.append(&mut assets); -// } - -// // If we don't see any manganis assets used in the binary, just return an empty manifest -// if all_assets.is_empty() { -// return Self::default(); -// }; - -// Self::new(all_assets) -// } + /// Fill this manifest from the contents of a linker command file. + /// + /// Rustc will pass a file as link args to linkers on windows instead of args directly. + /// + /// We actually need to read that file and then pull out the args directly. + pub fn add_from_command_file(&mut self, args: InterceptedArgs, arg: &str) { + let path = arg.trim().trim_start_matches('@'); + let file_binary = std::fs::read(path).unwrap(); + + // This may be a utf-16le file. Let's try utf-8 first. + let content = match String::from_utf8(file_binary.clone()) { + Ok(s) => s, + Err(_) => { + // Convert Vec to Vec to convert into a String + let binary_u16le: Vec = file_binary + .chunks_exact(2) + .map(|a| u16::from_le_bytes([a[0], a[1]])) + .collect(); + + String::from_utf16_lossy(&binary_u16le) + } + }; -// fn load_from_objects(object_files: Vec) -> Self { -// let json = get_json_from_object_files(object_files); -// Self::load(json) -// } + // Gather linker args + let mut linker_args = Vec::new(); + let lines = content.lines(); + for line in lines { + // Remove quotes from the line - windows link args files are quoted + let line_parsed = line.to_string(); + let line_parsed = line_parsed.trim_end_matches('"').to_string(); + let line_parsed = line_parsed.trim_start_matches('"').to_string(); + linker_args.push(line_parsed); + } -// fn copy_static_assets_to(&self, location: impl Into) -> anyhow::Result<()> { -// let location = location.into(); -// match std::fs::create_dir_all(&location) { -// Ok(_) => {} -// Err(err) => { -// tracing::error!("Failed to create directory for static assets: {}", err); -// return Err(err.into()); -// } -// } + self.add_from_linker_args(InterceptedArgs { + args: linker_args, + ..args + }); + } -// self.assets().iter().try_for_each(|asset| { -// match asset { -// AssetType::Resource(file_asset) => { -// tracing::info!("Optimizing and bundling {:?}", file_asset); -// tracing::trace!("Copying asset from {:?} to {:?}", file_asset, location); -// match process_file(file_asset, &location) { -// Ok(_) => {} -// Err(err) => { -// tracing::error!("Failed to copy static asset: {}", err); -// return Err(err); -// } -// } + pub fn add_from_linker_args(&mut self, args: InterceptedArgs) { + // Parse through linker args for `.o` or `.rlib` files. + for item in args.args { + if item.ends_with(".o") || item.ends_with(".rlib") { + self.add_from_object_path(args.work_dir.join(PathBuf::from(item))); + } + } + } -// // tracing::info!("Copying folder asset {}", folder_asset); -// // match process_folder(folder_asset, &location) { -// // Ok(_) => {} -// // Err(err) => { -// // tracing::error!("Failed to copy static asset: {}", err); -// // return Err(err); -// // } -// // } -// } + /// Fill this manifest with a file object/rlib files, typically extracted from the linker intercepted + pub fn add_from_object_path(&mut self, path: PathBuf) { + let Some(ext) = path.extension() else { + return; + }; -// _ => {} -// } -// Ok::<(), anyhow::Error>(()) -// }) -// } + let Some(ext) = ext.to_str() else { + return; + }; -// // fn collect_tailwind_css( -// // self: &AssetManifest, -// // include_preflight: bool, -// // warnings: &mut Vec, -// // ) -> String { -// // let mut all_classes = String::new(); + let data = std::fs::read(path.clone()).expect("Failed to read asset optimization file"); -// // for asset in self.assets() { -// // if let AssetType::Tailwind(classes) = asset { -// // all_classes.push_str(classes.classes()); -// // all_classes.push(' '); -// // } -// // } + match ext { + // Parse an unarchived object file + "o" => { + let object = object::File::parse(&*data).unwrap(); + self.add_from_object_file(&object); + } -// // let source = railwind::Source::String(all_classes, railwind::CollectionOptions::String); + // Parse an rlib as a collection of objects + "rlib" => { + let archive = object::read::archive::ArchiveFile::parse(&*data).unwrap(); + self.add_from_archive_file(&archive, &data); + } + _ => {} + } + } -// // let css = railwind::parse_to_string(source, include_preflight, warnings); + /// Fill this manifest from an rlib / ar file that contains many object files and their entryies + pub fn add_from_archive_file(&mut self, archive: &ArchiveFile, data: &[u8]) { + // Look through each archive member for object files. + // Read the archive member's binary data (we know it's an object file) + // And parse it with the normal `object::File::parse` to find the manganis string. + for member in archive.members() { + let member = member.unwrap(); + let name = String::from_utf8_lossy(member.name()).to_string(); + + // Check if the archive member is an object file and parse it. + if name.ends_with(".o") { + let data = member.data(&*data).unwrap(); + let object = object::File::parse(data).unwrap(); + self.add_from_object_file(&object); + } + } + } -// // crate::file::minify_css(&css) -// // } -// } + /// Fill this manifest with whatever tables might come from the object file + pub fn add_from_object_file(&mut self, obj: &ObjectFile) -> Option<()> { + for section in obj.sections() { + let Ok(section_name) = section.name() else { + continue; + }; -fn deserialize_assets(json: &str) -> Vec { - todo!() - // let deserializer = serde_json::Deserializer::from_str(json); - // deserializer - // .into_iter::() - // .flat_map(|x| x.ok()) - // // .map(|x| x.unwrap()) - // .collect() -} + // Check if the link section matches the asset section for one of the platforms we support. This may not be the current platform if the user is cross compiling + let matches = LinkSection::ALL + .iter() + .any(|x| x.link_section == section_name); -/// Extract JSON Manganis strings from a list of object files. -pub fn get_json_from_object_files(object_paths: Vec) -> Vec { - let mut all_json = Vec::new(); + if !matches { + continue; + } - for path in object_paths { - let Some(ext) = path.extension() else { - continue; - }; + let bytes = section.uncompressed_data().ok()?; - let Some(ext) = ext.to_str() else { - continue; - }; + let as_str = std::str::from_utf8(&bytes) + .ok()? + .chars() + .filter(|c| !c.is_control()) + .collect::(); - let is_rlib = match ext { - "rlib" => true, - "o" => false, - _ => continue, - }; + let stream = serde_json::Deserializer::from_str(&as_str).into_iter::(); - // Read binary data and try getting assets from manganis string - let binary_data = fs::read(path).unwrap(); + for as_resource in stream { + let as_resource = as_resource.unwrap(); - // rlibs are archives with object files inside. - let mut data = match is_rlib { - false => { - // Parse an unarchived object file. We use a Vec to match the return types. - let file = object::File::parse(&*binary_data).unwrap(); - let mut data = Vec::new(); - if let Some(string) = get_string_manganis(&file) { - data.push(string); - } - data + // Some platforms (e.g. macOS) start the manganis section with a null byte, we need to filter that out before we deserialize the JSON + self.assets + .insert(as_resource.absolute.clone(), as_resource); } - true => { - let file = object::read::archive::ArchiveFile::parse(&*binary_data).unwrap(); + } - // rlibs can contain many object files so we collect each manganis string here. - let mut manganis_strings = Vec::new(); + None + } - // Look through each archive member for object files. - // Read the archive member's binary data (we know it's an object file) - // And parse it with the normal `object::File::parse` to find the manganis string. - for member in file.members() { - let member = member.unwrap(); - let name = String::from_utf8_lossy(member.name()).to_string(); + /// Copy the assest from this manifest to a target folder + /// + /// If `optimize` is enabled, then we will run the optimizer for this asset. + /// + /// The output file is guaranteed to be the destination + the ResourceAsset bundle name + /// + /// Will not actually copy the asset if the source asset hasn't changed? + pub fn copy_asset_to(&self, destination: PathBuf, target_asset: PathBuf, optimize: bool) { + let src = self.assets.get(&target_asset).unwrap(); - // Check if the archive member is an object file and parse it. - if name.ends_with(".o") { - let data = member.data(&*binary_data).unwrap(); - let o_file = object::File::parse(data).unwrap(); - if let Some(manganis_str) = get_string_manganis(&o_file) { - manganis_strings.push(manganis_str); - } - } - } + let local = src.absolute.clone(); - manganis_strings - } - }; + if !local.exists() { + panic!("Specified asset does not exist while trying to copy {target_asset:?} to {destination:?}") + } - all_json.append(&mut data); - } + // If there's no optimizaton while copying this asset, we simply std::fs::copy and call it a day + if !optimize { + std::fs::copy(local, destination.join(&src.bundled)).expect("Failed to copy asset"); + return; + } - all_json + // Otherwise, let's attempt to optimize the thing + } } diff --git a/packages/cli/src/builder/assets.rs b/packages/cli/src/builder/assets.rs index ff02a3eef9..758c2b91ff 100644 --- a/packages/cli/src/builder/assets.rs +++ b/packages/cli/src/builder/assets.rs @@ -1,78 +1,144 @@ use super::BuildRequest; use super::TargetPlatform; -use crate::assets::{copy_dir_to, AssetManifest}; -use crate::builder::progress::CargoBuildResult; -use crate::builder::progress::Stage; -use crate::builder::progress::UpdateBuildProgress; -use crate::builder::progress::UpdateStage; +use crate::builder::{progress::UpdateBuildProgress, BuildMessage, MessageType}; +use crate::builder::{progress::UpdateStage, MessageSource}; use crate::config::Platform; -use crate::link::LinkCommand; use crate::Result; +use crate::{ + assets::{copy_dir_to, AssetManifest}, + link::LINK_OUTPUT_ENV_VAR, +}; +use crate::{builder::progress::Stage, link::InterceptedArgs}; use anyhow::Context; +use core::str; use futures_channel::mpsc::UnboundedSender; -use std::fs::create_dir_all; -use std::path::PathBuf; +use manganis_core::ResourceAsset; +use rayon::prelude::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator}; +use std::{ + env::current_exe, + fs::{self, create_dir_all}, + io::Read, + sync::{atomic::AtomicUsize, Arc}, +}; +use std::{ + io::{BufWriter, Write}, + path::Path, +}; +use std::{path::PathBuf, process::Stdio}; use tokio::process::Command; - -type ProgressChannel = UnboundedSender; +use tracing::Level; impl BuildRequest { - pub async fn collect_assets( - &mut self, - cargo_args: Vec, - ) -> anyhow::Result> { + /// Run the linker intercept and then fill in our AssetManifest from the incremental artifacts + /// + /// This will execute `dx` with an env var set to force `dx` to operate as a linker, and then + /// traverse the .o and .rlib files rustc passes that new `dx` instance, collecting the link + /// tables marked by manganis and parsing them as a ResourceAsset. + pub async fn collect_assets(&mut self, cargo_args: Vec) -> anyhow::Result<()> { // If this is the server build, the client build already copied any assets we need if self.target_platform == TargetPlatform::Server { - return Ok(None); + return Ok(()); } // If assets are skipped, we don't need to collect them if self.build_arguments.skip_assets { - return Ok(None); + return Ok(()); } - Ok(None) - - // // Start Manganis linker intercept. - // let linker_args = vec![format!("{}", self.target_out_dir().display())]; - - // // Don't block the main thread - manganis should not be running its own std process but it's - // // fine to wrap it here at the top - // let build = self.clone(); - // let mut progress = progress.clone(); - // tokio::task::spawn_blocking(move || { - // manganis_cli_support::start_linker_intercept( - // &LinkCommand::command_name(), - // cargo_args, - // Some(linker_args), - // )?; - // let assets = asset_manifest(&build); - // // Collect assets from the asset manifest the linker intercept created - // process_assets(&build, &assets, &mut progress)?; - // // Create the __assets_head.html file for bundling - // create_assets_head(&build, &assets)?; - - // Ok(Some(assets)) - // }) - // .await - // .unwrap() + // Create a temp file to put the output of the args + // We need to do this since rustc won't actually print the link args to stdout, so we need to + // give `dx` a file to dump its env::args into + let tmp_file = tempfile::NamedTempFile::new()?; + + // Run `cargo rustc` again, but this time with a custom linker (dx) and an env var to force + // `dx` to act as a linker + // + // Pass in the tmp_file as the env var itself + tokio::process::Command::new("cargo") + .env(LINK_OUTPUT_ENV_VAR, tmp_file.path()) + .arg("rustc") + .args(cargo_args) + .arg("--offline") + .arg("--") + .arg(format!("-Clinker={}", current_exe().unwrap().display())) + .arg("-Csave-temps=y") + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .output() + .await?; + + // Read the contents of the temp file + let args = std::fs::read_to_string(tmp_file.path()).expect("Failed to read linker output"); + + // Parse them as a Vec which is just our informal format for link args in the cli + // Todo: this might be wrong-ish on windows? The format is weird + let args = + serde_json::from_str::(&args).expect("Failed to parse linker output"); + + self.assets.add_from_linker_intercept(args); + + Ok(()) } pub fn copy_assets_dir(&self) -> anyhow::Result<()> { tracing::info!("Copying public assets to the output directory..."); - let out_dir = self.target_out_dir(); + + let static_asset_output_dir = self.target_out_dir(); + std::fs::create_dir_all(&static_asset_output_dir) + .context("Failed to create static asset output directory")?; + + // todo: join the entire asset dir here let asset_dir = self.krate.asset_dir(); + let assets = self.assets.assets.keys().collect::>(); - if asset_dir.is_dir() { - // Only pre-compress the assets from the web build. Desktop assets are not served, so they don't need to be pre_compressed - let pre_compress = self.targeting_web() - && self - .krate - .should_pre_compress_web_assets(self.build_arguments.release); + let assets_finished = AtomicUsize::new(0); + let asset_count = assets.len(); + let manifest = &self.assets; + let platform = self.target_platform; - copy_dir_to(asset_dir, out_dir, pre_compress)?; - } + assets + .par_iter() + .enumerate() + .try_for_each(|(_idx, asset)| { + let mut progress = self.progress.clone(); + + // Update the progress + _ = progress.start_send(UpdateBuildProgress { + stage: Stage::OptimizingAssets, + update: UpdateStage::AddMessage(BuildMessage { + level: Level::INFO, + message: MessageType::Text(format!( + "Optimized static asset {}", + asset.display() + )), + source: MessageSource::Build, + }), + platform, + }); + + manifest.copy_asset_to(static_asset_output_dir.clone(), asset.to_path_buf(), false); + + let finished = assets_finished.fetch_add(1, std::sync::atomic::Ordering::SeqCst); + + _ = progress.start_send(UpdateBuildProgress { + stage: Stage::OptimizingAssets, + update: UpdateStage::SetProgress(finished as f64 / asset_count as f64), + platform, + }); + + // idx, &assets_finished + Ok(()) as anyhow::Result<()> + })?; + + if self.should_precompress_assets() {} Ok(()) } + + fn should_precompress_assets(&self) -> bool { + self.targeting_web() + && self + .krate + .should_pre_compress_web_assets(self.build_arguments.release) + } } diff --git a/packages/cli/src/builder/bundle.rs b/packages/cli/src/builder/bundle.rs new file mode 100644 index 0000000000..b730386deb --- /dev/null +++ b/packages/cli/src/builder/bundle.rs @@ -0,0 +1,13 @@ +/// The processed bundle infomrmation +#[derive(Clone)] +pub enum AppBundle { + MacOS, + Ios, + Fullstack, + Spa, + Msi, + Wix, + Deb, + Rpm, + AppImage, +} diff --git a/packages/cli/src/builder/cargo.rs b/packages/cli/src/builder/cargo.rs index e9eff429e1..be1a9ef60d 100644 --- a/packages/cli/src/builder/cargo.rs +++ b/packages/cli/src/builder/cargo.rs @@ -106,8 +106,10 @@ impl BuildRequest { // Create the build command let (cmd, cargo_args) = self.prepare_build_command()?; - // Run the build command with a pretty loader + // We want to provide helpful data - maybe we can do this earlier? let crate_count = self.get_unit_count_estimate().await; + + // Run the build command with a pretty loader let cargo_result = self.build_cargo(crate_count, cmd).await?; // Post process the build result @@ -164,6 +166,8 @@ impl BuildRequest { // Make sure we set the exeutable self.executable = Some(output_path.canonicalize()?); + // And then copy over the asset dir into the bundle + // todo: this will eventually become a full bundle step self.copy_assets_dir()?; // If this is a web build, run web post processing steps @@ -178,7 +182,7 @@ impl BuildRequest { pub fn target_out_dir(&self) -> PathBuf { let out_dir = self.krate.out_dir(); match self.build_arguments.platform { - Some(Platform::Fullstack | Platform::StaticGeneration) => match self.target_platform { + Some(Platform::Fullstack) => match self.target_platform { TargetPlatform::Web => out_dir.join("public"), TargetPlatform::Desktop => out_dir.join("desktop"), _ => out_dir, diff --git a/packages/cli/src/builder/fullstack.rs b/packages/cli/src/builder/fullstack.rs index 0c05c3e5e6..e0adf8a2c1 100644 --- a/packages/cli/src/builder/fullstack.rs +++ b/packages/cli/src/builder/fullstack.rs @@ -9,7 +9,31 @@ use std::io::Write; use super::{BuildReason, TargetPlatform, UpdateBuildProgress}; +static CLIENT_PROFILE: &str = "dioxus-client"; +static SERVER_PROFILE: &str = "dioxus-server"; + impl BuildRequest { + pub(crate) fn new_single( + reason: BuildReason, + krate: DioxusCrate, + arguments: Build, + progress: UnboundedSender, + platform: TargetPlatform, + ) -> Self { + Self { + progress, + reason, + krate, + target_platform: platform, + build_arguments: arguments, + target_dir: Default::default(), + rust_flags: Default::default(), + executable: Default::default(), + assets: Default::default(), + child: None, + } + } + pub(crate) fn new_fullstack( config: DioxusCrate, build_arguments: Build, @@ -19,8 +43,8 @@ impl BuildRequest { initialize_profiles(&config)?; Ok(vec![ - Self::new_client(serve, &config, &build_arguments, progress.clone()), - Self::new_server(serve, &config, &build_arguments, progress), + Self::new_client(serve, &config, build_arguments.clone(), progress.clone()), + Self::new_server(serve, &config, build_arguments, progress), ]) } @@ -51,16 +75,16 @@ impl BuildRequest { executable: None, assets: Default::default(), progress, + child: None, } } fn new_server( serve: BuildReason, config: &DioxusCrate, - build: &Build, + mut build: Build, progress: UnboundedSender, ) -> Self { - let mut build = build.clone(); if build.profile.is_none() { build.profile = Some(CLIENT_PROFILE.to_string()); } @@ -78,10 +102,9 @@ impl BuildRequest { fn new_client( serve: BuildReason, config: &DioxusCrate, - build: &Build, + mut build: Build, progress: UnboundedSender, ) -> Self { - let mut build = build.clone(); if build.profile.is_none() { build.profile = Some(SERVER_PROFILE.to_string()); } @@ -97,9 +120,6 @@ impl BuildRequest { } } -static CLIENT_PROFILE: &str = "dioxus-client"; -static SERVER_PROFILE: &str = "dioxus-server"; - // The `opt-level=2` increases build times, but can noticeably decrease time // between saving changes and being able to interact with an app. The "overall" // time difference (between having and not having the optimization) can be diff --git a/packages/cli/src/builder/handle.rs b/packages/cli/src/builder/handle.rs new file mode 100644 index 0000000000..feed326836 --- /dev/null +++ b/packages/cli/src/builder/handle.rs @@ -0,0 +1,10 @@ +use tokio::process::Child; + +/// A handle to a running app +pub struct AppHandle { + // child: Option, + // stdout: Lines>, + // stderr: Lines>, + // stdout_line: String, + // stderr_line: String, +} diff --git a/packages/cli/src/builder/mod.rs b/packages/cli/src/builder/mod.rs index eabf79d4d1..44d16acdba 100644 --- a/packages/cli/src/builder/mod.rs +++ b/packages/cli/src/builder/mod.rs @@ -1,19 +1,17 @@ +use crate::build::Build; +use crate::config::Platform; use crate::Result; use crate::{assets::AssetManifest, dioxus_crate::DioxusCrate}; -use crate::{build::Build, config}; -use crate::{cli::serve::ServeArguments, config::Platform}; use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender}; -use futures_util::stream::select_all; use futures_util::StreamExt; -pub use platform::TargetArch; pub use platform::TargetPlatform; -use std::{net::SocketAddr, path::Path}; -use std::{path::PathBuf, process::Stdio}; -use tokio::process::{Child, Command}; +use std::path::PathBuf; mod assets; +mod bundle; mod cargo; mod fullstack; +mod handle; mod platform; mod prepare_html; mod progress; @@ -23,12 +21,11 @@ pub use progress::{ BuildMessage, MessageSource, MessageType, Stage, UpdateBuildProgress, UpdateStage, }; -/// A request for a project to be built +/// An app that's built, bundled, processed, and a handle to its running app, if it exists /// /// As the build progresses, we'll fill in fields like assets, executable, entitlements, etc /// -/// This request will be then passed to the bundler to create a final bundled app -#[derive(Clone)] +/// If the app needs to be bundled, we'll add the bundle info here too pub struct BuildRequest { /// Whether the build is for serving the application pub reason: BuildReason, @@ -54,6 +51,11 @@ pub struct BuildRequest { /// The assets manifest - starts empty and will be populated as we go pub assets: AssetManifest, + /// The child process of this running app that has yet to be spawned. + /// + /// We might need to finangle this into something else + pub child: Option, + /// Status channel to send our progress updates to pub progress: UnboundedSender, } @@ -74,34 +76,28 @@ impl BuildRequest { progress: UnboundedSender, ) -> crate::Result> { let build_arguments: Build = build_arguments.into(); - let platform = build_arguments.platform(); + let single_platform = |platform| { - let dioxus_crate = dioxus_crate.clone(); - - let request = Self { - reason: serve, - krate: dioxus_crate, - build_arguments: build_arguments.clone(), - target_platform: platform, - rust_flags: Default::default(), - target_dir: Default::default(), - executable: Default::default(), - assets: Default::default(), - progress: progress.clone(), - }; - - vec![request] + let req = Self::new_single( + serve, + dioxus_crate.clone(), + build_arguments.clone(), + progress.clone(), + platform, + ); + Ok(vec![req]) }; - Ok(match platform { + match build_arguments.platform() { Platform::Liveview => single_platform(TargetPlatform::Liveview), Platform::Web => single_platform(TargetPlatform::Web), Platform::Desktop => single_platform(TargetPlatform::Desktop), - Platform::StaticGeneration | Platform::Fullstack => { - Self::new_fullstack(dioxus_crate.clone(), build_arguments, serve, progress)? + Platform::Mobile => single_platform(TargetPlatform::Mobile), + + Platform::Fullstack => { + Self::new_fullstack(dioxus_crate.clone(), build_arguments, serve, progress) } - _ => unimplemented!("Unknown platform: {platform:?}"), - }) + } } pub(crate) async fn build_all_parallel( @@ -137,9 +133,4 @@ impl BuildRequest { Ok(all_results) } - - /// Check if the build is targeting the web platform - pub fn targeting_web(&self) -> bool { - self.target_platform == TargetPlatform::Web - } } diff --git a/packages/cli/src/builder/platform.rs b/packages/cli/src/builder/platform.rs index 3f820f02ba..e9bfd4a3f3 100644 --- a/packages/cli/src/builder/platform.rs +++ b/packages/cli/src/builder/platform.rs @@ -7,8 +7,8 @@ use std::str::FromStr; pub enum TargetPlatform { Web, Desktop, - Server, Mobile, + Server, Liveview, } diff --git a/packages/cli/src/builder/web.rs b/packages/cli/src/builder/web.rs index 66f55660d5..34039956ad 100644 --- a/packages/cli/src/builder/web.rs +++ b/packages/cli/src/builder/web.rs @@ -1,4 +1,4 @@ -use super::BuildRequest; +use super::{BuildRequest, TargetPlatform}; use crate::assets::pre_compress_folder; use crate::builder::progress::Stage; use crate::builder::progress::UpdateBuildProgress; @@ -192,4 +192,9 @@ impl BuildRequest { Ok(()) } + + /// Check if the build is targeting the web platform + pub fn targeting_web(&self) -> bool { + self.target_platform == TargetPlatform::Web + } } diff --git a/packages/cli/src/cli/link.rs b/packages/cli/src/cli/link.rs index fe3a2ac27d..d6d129da46 100644 --- a/packages/cli/src/cli/link.rs +++ b/packages/cli/src/cli/link.rs @@ -1,41 +1,42 @@ -use crate::{ - assets::{self, get_json_from_object_files, linker_intercept}, - error::Result, -}; -use clap::Parser; -use std::{fs, path::PathBuf}; - -#[derive(Clone, Debug, Parser)] -#[clap(name = "link", hide = true)] -pub struct LinkCommand { - // Allow us to accept any argument after `dx link` - #[clap(trailing_var_arg = true, allow_hyphen_values = true)] - pub args: Vec, -} +use std::{env::current_dir, path::PathBuf}; -impl LinkCommand { - pub fn link(self) -> Result<()> { - let Some((link_args, object_files)) = linker_intercept(self.args) else { - tracing::warn!("Invalid linker arguments."); - return Ok(()); - }; +use serde::{Deserialize, Serialize}; - // Parse object files, deserialize JSON, & create a file to propagate JSON. - let json = get_json_from_object_files(object_files); - let parsed = serde_json::to_string(&json).unwrap(); +/// The env var that will be set by the linker intercept cmd to indicate that we should act as a linker +pub const LINK_OUTPUT_ENV_VAR: &str = "dx-magic-link-file"; + +/// Should we act as a linker? +/// +/// Just check if the magic env var is set +pub fn should_link() -> bool { + std::env::var(LINK_OUTPUT_ENV_VAR).is_ok() +} + +#[derive(Serialize, Deserialize)] +pub struct InterceptedArgs { + pub work_dir: PathBuf, + pub args: Vec, +} - let out_dir = PathBuf::from(link_args.first().unwrap()); - fs::create_dir_all(&out_dir).unwrap(); +/// Write the incoming linker args to a file +/// +/// The file will be given by the dx-magic-link-arg env var itself, so we use +/// it both for determining if we should act as a linker and the for the file name itself. +/// +/// This will panic if it fails +pub fn dump_link_args() -> anyhow::Result<()> { + let output = std::env::var(LINK_OUTPUT_ENV_VAR).expect("Missing env var with target file"); - let path = out_dir.join(assets::MG_JSON_OUT); - fs::write(path, parsed).unwrap(); + // get the args and then dump them to the file + let args: Vec<_> = std::env::args().collect(); + let escaped = serde_json::to_string(&InterceptedArgs { + args, + work_dir: current_dir().unwrap(), + }) + .expect("Failed to escape env args"); - Ok(()) - } + // write the file + std::fs::write(output, escaped).expect("Failed to write output file"); - /// We need to pass the subcommand name to Manganis so this - /// helps centralize where we set the subcommand "name". - pub fn command_name() -> String { - "link".to_string() - } + Ok(()) } diff --git a/packages/cli/src/cli/mod.rs b/packages/cli/src/cli/mod.rs index 4ce83e12a1..1616c8c560 100644 --- a/packages/cli/src/cli/mod.rs +++ b/packages/cli/src/cli/mod.rs @@ -82,10 +82,6 @@ pub enum Commands { /// Dioxus config file controls. #[clap(subcommand)] Config(config::Config), - - /// Handles parsing of linker arguments for linker-based systems - /// such as Manganis and binary patching. - Link(link::LinkCommand), } impl Display for Commands { @@ -101,7 +97,6 @@ impl Display for Commands { Commands::Autoformat(_) => write!(f, "fmt"), Commands::Check(_) => write!(f, "check"), Commands::Bundle(_) => write!(f, "bundle"), - Commands::Link(_) => write!(f, "link"), } } } diff --git a/packages/cli/src/cli/serve.rs b/packages/cli/src/cli/serve.rs index 381f5f44bd..1475224b17 100644 --- a/packages/cli/src/cli/serve.rs +++ b/packages/cli/src/cli/serve.rs @@ -115,6 +115,10 @@ impl Serve { crate::serve::serve_all(self, dioxus_crate, log_control).await?; Ok(()) } + + pub fn should_hotreload(&self) -> bool { + self.server_arguments.hot_reload.unwrap_or(true) + } } impl Deref for Serve { diff --git a/packages/cli/src/config.rs b/packages/cli/src/config.rs index 549ce285f2..daec03f3b4 100644 --- a/packages/cli/src/config.rs +++ b/packages/cli/src/config.rs @@ -2,12 +2,12 @@ mod app; mod bundle; mod desktop; mod dioxus_config; -// mod fullstack; -// mod liveview; mod platform; mod serve; -// mod static_generation; mod web; +// mod fullstack; +// mod liveview; +// mod static_generation; pub use app::*; pub use bundle::*; diff --git a/packages/cli/src/config/platform.rs b/packages/cli/src/config/platform.rs index 8624bcc75d..d809ea3eb4 100644 --- a/packages/cli/src/config/platform.rs +++ b/packages/cli/src/config/platform.rs @@ -38,11 +38,6 @@ pub enum Platform { #[serde(rename = "fullstack")] Fullstack, - /// Targeting the static generation platform using SSR and Dioxus-Fullstack - #[clap(name = "static-generation")] - #[serde(rename = "static-generation")] - StaticGeneration, - /// Targeting the static generation platform using SSR and Dioxus-Fullstack #[clap(name = "liveview")] #[serde(rename = "liveview")] @@ -66,7 +61,6 @@ impl FromStr for Platform { "web" => Ok(Self::Web), "desktop" => Ok(Self::Desktop), "fullstack" => Ok(Self::Fullstack), - "static-generation" => Ok(Self::StaticGeneration), "liveview" => Ok(Self::Liveview), _ => Err(UnknownPlatformError), } @@ -82,12 +76,7 @@ impl Display for Platform { impl Platform { /// All platforms the dioxus CLI supports - pub const ALL: &'static [Self] = &[ - Platform::Web, - Platform::Desktop, - Platform::Fullstack, - Platform::StaticGeneration, - ]; + pub const ALL: &'static [Self] = &[Platform::Web, Platform::Desktop, Platform::Fullstack]; /// Get the feature name for the platform in the dioxus crate pub fn feature_name(&self) -> &str { @@ -95,7 +84,6 @@ impl Platform { Platform::Web => "web", Platform::Desktop => "desktop", Platform::Fullstack => "fullstack", - Platform::StaticGeneration => "static-generation", Platform::Liveview => "liveview", Platform::Mobile => "mobile", } diff --git a/packages/cli/src/main.rs b/packages/cli/src/main.rs index 1befa2d32a..f0327c344e 100644 --- a/packages/cli/src/main.rs +++ b/packages/cli/src/main.rs @@ -3,28 +3,23 @@ #![doc(html_favicon_url = "https://avatars.githubusercontent.com/u/79236386")] pub mod assets; +pub mod builder; pub mod bundle_utils; +pub mod cli; pub mod config; +pub mod dioxus_crate; pub mod dx_build_info; +pub mod error; +pub mod metadata; pub mod serve; +pub mod settings; pub mod tools; pub mod tracer; -pub mod cli; pub use cli::*; - -pub mod error; -pub use error::*; - -pub(crate) mod builder; - -mod dioxus_crate; pub use dioxus_crate::*; - -mod settings; -pub(crate) use settings::*; - -pub(crate) mod metadata; +pub use error::*; +pub use settings::*; use anyhow::Context; use clap::Parser; @@ -33,8 +28,12 @@ use Commands::*; #[tokio::main] async fn main() -> anyhow::Result<()> { - let args = Cli::parse(); + // If we have a magic env var set, we want to operate as a linker instead. + if link::should_link() { + return link::dump_link_args(); + } + let args = Cli::parse(); let log_control = tracer::build_tracing(); match args.action { @@ -63,10 +62,6 @@ async fn main() -> anyhow::Result<()> { .await .context(error_wrapper("Error checking RSX")), - Link(opts) => opts - .link() - .context(error_wrapper("Error with linker passthrough")), - Build(mut opts) => opts .run() .await diff --git a/packages/cli/src/serve/builder.rs b/packages/cli/src/serve/builder.rs index f640b19c49..34ef9ddc57 100644 --- a/packages/cli/src/serve/builder.rs +++ b/packages/cli/src/serve/builder.rs @@ -8,7 +8,7 @@ use crate::Result; use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender}; use futures_util::future::OptionFuture; use futures_util::StreamExt; -use std::process::Stdio; +use std::{collections::HashMap, process::Stdio}; use tokio::{ process::{Child, Command}, task::JoinHandle, @@ -19,7 +19,8 @@ use super::update::ServeUpdate; /// A handle to ongoing builds and then the spawned tasks themselves pub struct Builder { /// The results of the build - build_results: Option>>>, + ongoing: Option>>>, + tx: UnboundedSender, rx: UnboundedReceiver, @@ -30,21 +31,26 @@ pub struct Builder { serve: Serve, /// The children of the build process - pub children: Vec<(TargetPlatform, Child)>, + pub finished: HashMap, } impl Builder { - /// Create a new builder - pub fn new(config: &DioxusCrate, serve: &Serve) -> Self { + /// Create a new builder and immediately start a build + pub fn start(config: &DioxusCrate, serve: &Serve) -> Result { let (tx, rx) = futures_channel::mpsc::unbounded(); - Self { + + let mut builder = Self { tx, rx, - build_results: None, + ongoing: None, config: config.clone(), serve: serve.clone(), - children: Vec::new(), - } + finished: Default::default(), + }; + + builder.build()?; + + Ok(builder) } /// Start a new build - killing the current one if it exists @@ -78,7 +84,7 @@ impl Builder { }); } - self.build_results = Some(tokio::spawn(async move { + self.ongoing = Some(tokio::spawn(async move { let mut all_results = Vec::new(); while let Some(result) = set.join_next().await { let res = result.map_err(|err| { @@ -94,53 +100,61 @@ impl Builder { } /// Wait for any new updates to the builder - either it completed or gave us a message etc + /// + /// Also listen for any input from the app's handle pub async fn wait(&mut self) -> ServeUpdate { // Wait for build progress let next = next_or_pending(self.rx.next()); // The ongoing builds directly - let results: OptionFuture<_> = self.build_results.as_mut().into(); + let results: OptionFuture<_> = self.ongoing.as_mut().into(); let results = next_or_pending(results); - // The process exits - let children_empty = self.children.is_empty(); - let process_exited = self - .children - .iter_mut() - .map(|(target, child)| Box::pin(async move { (*target, child.wait().await) })); - - let process_exited = async move { - match children_empty { - true => return futures_util::future::pending().await, - false => futures_util::future::select_all(process_exited).await, - } - }; - - // Wait for the next build result - tokio::select! { - build_results = results => { - self.build_results = None; - - // If we have a build result, bubble it up to the main loop - match build_results { - Ok(Ok(build_results)) => ServeUpdate::BuildReady { results: build_results }, - Err(_ee) => ServeUpdate::BuildFailed { err: crate::Error::BuildFailed("Build join failed".to_string()) }, - Ok(Err(ee)) => ServeUpdate::BuildFailed { err: ee.into() }, - } - } - update = next => { - // If we have a build progress, send it to the screen - ServeUpdate::Progress { update } - } - ((target, exit_status), _, _) = process_exited => { - ServeUpdate::ProcessExited { status: exit_status, target_platform: target } - } - } + todo!("wait for builds to be finished") + + // // The process exits + // let children_empty = self.children.is_empty(); + // let process_exited = self + // .children + // .iter_mut() + // .map(|(target, child)| Box::pin(async move { (*target, child.wait().await) })); + + // let process_exited = async move { + // match children_empty { + // true => return futures_util::future::pending().await, + // false => futures_util::future::select_all(process_exited).await, + // } + // }; + + // // Wait for the next build result + // tokio::select! { + // build_results = results => { + // self.ongoing = None; + + // // If we have a build result, bubble it up to the main loop + // match build_results { + // Ok(Ok(build_results)) => ServeUpdate::BuildReady { }, + // Err(_ee) => ServeUpdate::BuildFailed { err: crate::Error::BuildFailed("Build join failed".to_string()) }, + // Ok(Err(ee)) => ServeUpdate::BuildFailed { err: ee.into() }, + // } + // } + // update = next => { + // // If we have a build progress, send it to the screen + // ServeUpdate::Progress { update } + // } + // ((target, exit_status), _, _) = process_exited => { + // ServeUpdate::ProcessExited { status: exit_status, target_platform: target } + // } + // } } /// Shutdown the current build process pub(crate) fn shutdown(&mut self) { - for (_, mut child) in self.children.drain(..) { + for (_target, app) in self.finished.drain() { + let Some(mut child) = app.child else { + continue; + }; + // Gracefully shtudown the desktop app // It might have a receiver to do some cleanup stuff if let Some(pid) = child.id() { @@ -169,7 +183,7 @@ impl Builder { _ = child.start_kill(); } - if let Some(tasks) = self.build_results.take() { + if let Some(tasks) = self.ongoing.take() { tasks.abort(); } } diff --git a/packages/cli/src/serve/detect.rs b/packages/cli/src/serve/detect.rs new file mode 100644 index 0000000000..504731da14 --- /dev/null +++ b/packages/cli/src/serve/detect.rs @@ -0,0 +1,32 @@ +/// Detects if `dx` is being ran in a WSL environment. +/// +/// We determine this based on whether the keyword `microsoft` or `wsl` is contained within the [`WSL_1`] or [`WSL_2`] files. +/// This may fail in the future as it isn't guaranteed by Microsoft. +/// See https://github.com/microsoft/WSL/issues/423#issuecomment-221627364 +pub(crate) fn is_wsl() -> bool { + const WSL_1: &str = "/proc/sys/kernel/osrelease"; + const WSL_2: &str = "/proc/version"; + const WSL_KEYWORDS: [&str; 2] = ["microsoft", "wsl"]; + + // Test 1st File + if let Ok(content) = std::fs::read_to_string(WSL_1) { + let lowercase = content.to_lowercase(); + for keyword in WSL_KEYWORDS { + if lowercase.contains(keyword) { + return true; + } + } + } + + // Test 2nd File + if let Ok(content) = std::fs::read_to_string(WSL_2) { + let lowercase = content.to_lowercase(); + for keyword in WSL_KEYWORDS { + if lowercase.contains(keyword) { + return true; + } + } + } + + false +} diff --git a/packages/cli/src/serve/mod.rs b/packages/cli/src/serve/mod.rs index e9a01bdd66..e0bfcc4069 100644 --- a/packages/cli/src/serve/mod.rs +++ b/packages/cli/src/serve/mod.rs @@ -1,30 +1,27 @@ -use std::future::{poll_fn, Future, IntoFuture}; -use std::task::Poll; - use crate::builder::{Stage, TargetPlatform, UpdateBuildProgress, UpdateStage}; use crate::cli::serve::Serve; use crate::dioxus_crate::DioxusCrate; -use crate::tracer::CLILogControl; use crate::Result; -use futures_util::FutureExt; -use tokio::task::yield_now; mod builder; +mod detect; mod hot_reloading_file_map; mod logs_tab; mod output; mod proxy; mod server; mod update; +mod util; mod watcher; use builder::*; use output::*; use server::*; -use update::ServeUpdate; +use update::*; +use util::*; use watcher::*; -/// For *all* builds the CLI spins up a dedicated webserver, file watcher, and build infrastructure to serve the project. +/// For *all* builds, the CLI spins up a dedicated webserver, file watcher, and build infrastructure to serve the project. /// /// This includes web, desktop, mobile, fullstack, etc. /// @@ -44,38 +41,32 @@ use watcher::*; /// the websocket logic from the webview for thinner builds. /// /// Todos(Jon): -/// - I'd love to be able to configure the CLI while it's running so we can change settingaon the fly. -/// This would require some light refactoring and potentially pulling in something like ratatui. -/// - Build a custom subscriber for logs by tools within this +/// - I'd love to be able to configure the CLI while it's running so we can change settings on the fly. /// - Handle logs from the build engine separately? -/// - Consume logs from the wasm for web/fullstack /// - I want us to be able to detect a `server_fn` in the project and then upgrade from a static server /// to a dynamic one on the fly. pub async fn serve_all( serve: Serve, - dioxus_crate: DioxusCrate, - log_control: CLILogControl, + krate: DioxusCrate, + log_control: crate::tracer::CLILogControl, ) -> Result<()> { - let mut builder = Builder::new(&dioxus_crate, &serve); - - // Start the first build - builder.build()?; - - let mut server = DevServer::start(&serve, &dioxus_crate); - let mut watcher = Watcher::start(&serve, &dioxus_crate); + // Start each component of the devserver. + // Starting the builder will queue up a build + let mut builder = Builder::start(&krate, &serve)?; + let mut server = DevServer::start(&serve, &krate); + let mut watcher = Watcher::start(&serve, &krate); let mut screen = Output::start(&serve, log_control).expect("Failed to open terminal logger"); - let hotreload = serve.server_arguments.hot_reload.unwrap_or(true); loop { - // Make sure we don't hog the CPU: these loop { select! {} } blocks can starve the executor - yield_now().await; + // Make sure we don't hog the CPU: these loop { select! {} } blocks can starve the executor if we're not careful + tokio::task::yield_now().await; // Draw the state of the server to the screen - screen.render(&serve, &dioxus_crate, &builder, &server, &watcher); + screen.render(&serve, &krate, &builder, &server, &watcher); // And then wait for any updates before redrawing let msg = tokio::select! { - msg = watcher.wait(), if hotreload => msg, + msg = watcher.wait(), if serve.should_hotreload() => msg, msg = server.wait() => msg, msg = builder.wait() => msg, msg = screen.wait() => match msg { @@ -90,11 +81,11 @@ pub async fn serve_all( continue; } - let changed_files = watcher.dequeue_changed_files(&dioxus_crate); + let changed_files = watcher.dequeue_changed_files(&krate); // if change is hotreloadable, hotreload it // and then send that update to all connected clients - if let Some(hr) = watcher.attempt_hot_reload(&dioxus_crate, changed_files) { + if let Some(hr) = watcher.attempt_hot_reload(&krate, changed_files) { // Only send a hotreload message for templates and assets - otherwise we'll just get a full rebuild if hr.templates.is_empty() && hr.assets.is_empty() { continue; @@ -155,37 +146,37 @@ pub async fn serve_all( } } - ServeUpdate::BuildFailed { err } => { + ServeUpdate::BuildFailed { err, target } => { server.send_build_error(err).await; } - ServeUpdate::BuildReady { results } => { - if !results.is_empty() { - builder.children.clear(); - } - - // If we have a build result, open it - for build_result in results.iter() { - let child = server.open(build_result); - - match child { - Ok(Some(child_proc)) => builder - .children - .push((build_result.target_platform, child_proc)), - Err(e) => { - tracing::error!("Failed to open build result: {e}"); - break; - } - _ => {} - } - } - - // Make sure we immediately capture the stdout/stderr of the executable - - // otherwise it'll clobber our terminal output - screen.new_ready_app(&mut builder, results); - - // And then finally tell the server to reload - server.send_reload_command().await; + ServeUpdate::BuildReady { target } => { + // if !results.is_empty() { + // builder.children.clear(); + // } + + // // If we have a build result, open it + // for build_result in results.iter() { + // let child = server.open(build_result); + + // match child { + // Ok(Some(child_proc)) => builder + // .children + // .push((build_result.target_platform, child_proc)), + // Err(e) => { + // tracing::error!("Failed to open build result: {e}"); + // break; + // } + // _ => {} + // } + // } + + // // Make sure we immediately capture the stdout/stderr of the executable - + // // otherwise it'll clobber our terminal output + // screen.new_ready_app(&mut builder, results); + + // // And then finally tell the server to reload + // server.send_reload_command().await; } // If the process exited *cleanly*, we can exit @@ -193,22 +184,23 @@ pub async fn serve_all( status, target_platform, } => { - // Then remove the child process - builder - .children - .retain(|(platform, _)| *platform != target_platform); - match status { - Ok(status) => { - if status.success() { - break; - } else { - tracing::error!("Application exited with status: {status}"); - } - } - Err(e) => { - tracing::error!("Application exited with error: {e}"); - } - } + todo!("process exited") + // // Then remove the child process + // builder + // .children + // .retain(|(platform, _)| *platform != target_platform); + // match status { + // Ok(status) => { + // if status.success() { + // break; + // } else { + // tracing::error!("Application exited with status: {status}"); + // } + // } + // Err(e) => { + // tracing::error!("Application exited with error: {e}"); + // } + // } } ServeUpdate::TuiInput { rebuild } => { @@ -221,28 +213,10 @@ pub async fn serve_all( } } - // Run our cleanup logic here - maybe printing as we go? - // todo: more printing, logging, error handling in this phase - _ = screen.shutdown(); + // Kill the clients first _ = server.shutdown().await; - builder.shutdown(); + _ = screen.shutdown(); + _ = builder.shutdown(); Ok(()) } - -// Grab the output of a future that returns an option or wait forever -pub(crate) fn next_or_pending(f: F) -> impl Future -where - F: IntoFuture>, -{ - let pinned = f.into_future().fuse(); - let mut pinned = Box::pin(pinned); - poll_fn(move |cx| { - let next = pinned.as_mut().poll(cx); - match next { - Poll::Ready(Some(next)) => Poll::Ready(next), - _ => Poll::Pending, - } - }) - .fuse() -} diff --git a/packages/cli/src/serve/output.rs b/packages/cli/src/serve/output.rs index 2a743dda05..640be51f99 100644 --- a/packages/cli/src/serve/output.rs +++ b/packages/cli/src/serve/output.rs @@ -92,7 +92,6 @@ pub struct Output { _dx_version: String, interactive: bool, pub(crate) build_progress: BuildProgress, - running_apps: HashMap, is_cli_release: bool, platform: Platform, @@ -177,7 +176,6 @@ impl Output { platform, fly_modal_open: false, build_progress: Default::default(), - running_apps: HashMap::new(), scroll: 0, term_height: 0, num_lines_with_wrapping: 0, @@ -189,48 +187,50 @@ impl Output { /// Add a message from stderr to the logs fn push_stderr(&mut self, platform: TargetPlatform, stderr: String) { - self.set_tab(Tab::BuildLog); - - self.running_apps - .get_mut(&platform) - .unwrap() - .output - .as_mut() - .unwrap() - .stderr_line - .push_str(&stderr); - self.build_progress - .build_logs - .get_mut(&platform) - .unwrap() - .messages - .push(BuildMessage { - level: Level::ERROR, - message: MessageType::Text(stderr), - source: MessageSource::App, - }); + todo!() + // self.set_tab(Tab::BuildLog); + + // self.running_apps + // .get_mut(&platform) + // .unwrap() + // .output + // .as_mut() + // .unwrap() + // .stderr_line + // .push_str(&stderr); + // self.build_progress + // .build_logs + // .get_mut(&platform) + // .unwrap() + // .messages + // .push(BuildMessage { + // level: Level::ERROR, + // message: MessageType::Text(stderr), + // source: MessageSource::App, + // }); } /// Add a message from stdout to the logs fn push_stdout(&mut self, platform: TargetPlatform, stdout: String) { - self.running_apps - .get_mut(&platform) - .unwrap() - .output - .as_mut() - .unwrap() - .stdout_line - .push_str(&stdout); - self.build_progress - .build_logs - .get_mut(&platform) - .unwrap() - .messages - .push(BuildMessage { - level: Level::INFO, - message: MessageType::Text(stdout), - source: MessageSource::App, - }); + todo!() + // self.running_apps + // .get_mut(&platform) + // .unwrap() + // .output + // .as_mut() + // .unwrap() + // .stdout_line + // .push_str(&stdout); + // self.build_progress + // .build_logs + // .get_mut(&platform) + // .unwrap() + // .messages + // .push(BuildMessage { + // level: Level::INFO, + // message: MessageType::Text(stdout), + // source: MessageSource::App, + // }); } /// Wait for either the ctrl_c handler or the next event @@ -245,67 +245,69 @@ impl Output { { next_or_pending(async move { f.await.ok().flatten() }) } + let user_input = async { let events = self.events.as_mut()?; events.next().await }; - let user_input = ok_and_some(user_input.map(|e| e.transpose())); - - let has_running_apps = !self.running_apps.is_empty(); - let next_stdout = self.running_apps.values_mut().map(|app| { - let future = async move { - let (stdout, stderr) = match &mut app.output { - Some(out) => ( - ok_and_some(out.stdout.next_line()), - ok_and_some(out.stderr.next_line()), - ), - None => return futures_util::future::pending().await, - }; - - tokio::select! { - line = stdout => (app.result.target_platform, Some(line), None), - line = stderr => (app.result.target_platform, None, Some(line)), - } - }; - Box::pin(future) - }); - - let next_stdout = async { - if has_running_apps { - select_all(next_stdout).await.0 - } else { - futures_util::future::pending().await - } - }; - - let tui_log_rx = &mut self.log_control.tui_rx; - let next_tui_log = next_or_pending(tui_log_rx.next()); - tokio::select! { - (platform, stdout, stderr) = next_stdout => { - if let Some(stdout) = stdout { - self.push_stdout(platform, stdout); - } - if let Some(stderr) = stderr { - self.push_stderr(platform, stderr); - } - }, - - // Handle internal CLI tracing logs. - log = next_tui_log => { - self.push_log(LogSource::Internal, BuildMessage { - level: Level::INFO, - message: MessageType::Text(log), - source: MessageSource::Dev, - }); - } + let user_input = ok_and_some(user_input.map(|e| e.transpose())); - event = user_input => { - if self.handle_events(event).await? { - return Ok(ServeUpdate::TuiInput { rebuild: true }); - } - } - } + // let has_running_apps = !self.running_apps.is_empty(); + // let next_stdout = self.running_apps.values_mut().map(|app| { + // let future = async move { + // let (stdout, stderr) = match &mut app.output { + // Some(out) => ( + // ok_and_some(out.stdout.next_line()), + // ok_and_some(out.stderr.next_line()), + // ), + // None => return futures_util::future::pending().await, + // }; + + // tokio::select! { + // line = stdout => (app.result.target_platform, Some(line), None), + // line = stderr => (app.result.target_platform, None, Some(line)), + // } + // }; + // Box::pin(future) + // }); + + // let next_stdout = async { + // if has_running_apps { + // select_all(next_stdout).await.0 + // } else { + // futures_util::future::pending().await + // } + // }; + + // let tui_log_rx = &mut self.log_control.tui_rx; + // let next_tui_log = next_or_pending(tui_log_rx.next()); + + // tokio::select! { + // (platform, stdout, stderr) = next_stdout => { + // if let Some(stdout) = stdout { + // self.push_stdout(platform, stdout); + // } + // if let Some(stderr) = stderr { + // self.push_stderr(platform, stderr); + // } + // }, + + // // Handle internal CLI tracing logs. + // log = next_tui_log => { + // self.push_log(LogSource::Internal, BuildMessage { + // level: Level::INFO, + // message: MessageType::Text(log), + // source: MessageSource::Dev, + // }); + // } + + // event = user_input => { + // if self.handle_events(event).await? { + // return Ok(ServeUpdate::TuiInput { rebuild: true }); + // } + // } + // } Ok(ServeUpdate::TuiInput { rebuild: false }) } @@ -525,42 +527,43 @@ impl Output { } } - pub fn new_ready_app(&mut self, build_engine: &mut Builder, results: Vec) { - for result in results { - let out = build_engine - .children - .iter_mut() - .find_map(|(platform, child)| { - if platform == &result.target_platform { - let stdout = child.stdout.take().unwrap(); - let stderr = child.stderr.take().unwrap(); - Some((stdout, stderr)) - } else { - None - } - }); - - let platform = result.target_platform; - - let stdout = out.map(|(stdout, stderr)| RunningAppOutput { - stdout: BufReader::new(stdout).lines(), - stderr: BufReader::new(stderr).lines(), - stdout_line: String::new(), - stderr_line: String::new(), - }); - - let app = RunningApp { - result, - output: stdout, - }; - - self.running_apps.insert(platform, app); - - // Finish the build progress for the platform that just finished building - if let Some(build) = self.build_progress.build_logs.get_mut(&platform) { - build.stage = Stage::Finished; - } - } + pub fn new_ready_app(&mut self, build_engine: &mut Builder, target: TargetPlatform) { + todo!() + // for result in results { + // let out = build_engine + // .finished + // .iter_mut() + // .find_map(|(platform, child)| { + // if platform == &result.target_platform { + // let stdout = child.stdout.take().unwrap(); + // let stderr = child.stderr.take().unwrap(); + // Some((stdout, stderr)) + // } else { + // None + // } + // }); + + // let platform = result.target_platform; + + // let stdout = out.map(|(stdout, stderr)| RunningAppOutput { + // stdout: BufReader::new(stdout).lines(), + // stderr: BufReader::new(stderr).lines(), + // stdout_line: String::new(), + // stderr_line: String::new(), + // }); + + // let app = RunningApp { + // result, + // output: stdout, + // }; + + // self.running_apps.insert(platform, app); + + // // Finish the build progress for the platform that just finished building + // if let Some(build) = self.build_progress.build_logs.get_mut(&platform) { + // build.stage = Stage::Finished; + // } + // } } pub fn render( @@ -978,15 +981,3 @@ async fn rustc_version() -> String { }) .unwrap_or_else(|| "".to_string()) } - -pub struct RunningApp { - result: BuildRequest, - output: Option, -} - -struct RunningAppOutput { - stdout: Lines>, - stderr: Lines>, - stdout_line: String, - stderr_line: String, -} diff --git a/packages/cli/src/serve/server.rs b/packages/cli/src/serve/server.rs index 0224dc2154..b0c727d097 100644 --- a/packages/cli/src/serve/server.rs +++ b/packages/cli/src/serve/server.rs @@ -41,7 +41,6 @@ use std::{ net::{IpAddr, SocketAddr}, }; use std::{path::Path, process::Stdio}; -use syn::LitCStr; use tokio::process::{Child, Command}; use tokio::task::JoinHandle; use tower::ServiceBuilder; @@ -51,11 +50,6 @@ use tower_http::{ ServiceBuilderExt, }; -pub enum ServerUpdate { - NewConnection, - Message(Message), -} - #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(tag = "type", content = "data")] enum Status { @@ -123,7 +117,7 @@ impl DevServer { // If we're serving a fullstack app, we need to find a port to proxy to let fullstack_port = if matches!( serve.build_arguments.platform(), - Platform::Liveview | Platform::Fullstack | Platform::StaticGeneration + Platform::Liveview | Platform::Fullstack ) { get_available_port(addr.ip()) } else { @@ -132,7 +126,7 @@ impl DevServer { let fullstack_address = fullstack_port.map(|port| SocketAddr::new(addr.ip(), port)); - let router = setup_router( + let router = Self::setup_router( serve, cfg, hot_reload_sockets_tx, @@ -467,54 +461,53 @@ impl DevServer { // # xcrun devicectl device process resume --device "${DEVICE_UUID}" --pid "${STATUS_PID}" > "${XCRUN_DEVICE_PROCESS_RESUME_LOG_DIR}" 2>&1 todo!("Open mobile apps") } -} - -/// Sets up and returns a router -/// -/// Steps include: -/// - Setting up cors -/// - Setting up the proxy to the endpoint specified in the config -/// - Setting up the file serve service -/// - Setting up the websocket endpoint for devtools -fn setup_router( - serve: &Serve, - config: &DioxusCrate, - hot_reload_sockets: UnboundedSender, - build_status_sockets: UnboundedSender, - fullstack_address: Option, - build_status: SharedStatus, -) -> Result { - let mut router = Router::new(); - let platform = serve.build_arguments.platform(); - - // Setup proxy for the endpoint specified in the config - for proxy_config in config.dioxus_config.web.proxy.iter() { - router = super::proxy::add_proxy(router, proxy_config)?; - } - - // server the dir if it's web, otherwise let the fullstack server itself handle it - match platform { - Platform::Web => { - // Route file service to output the .wasm and assets if this is a web build - let base_path = format!( - "/{}", - config - .dioxus_config - .web - .app - .base_path - .as_deref() - .unwrap_or_default() - .trim_matches('/') - ); - router = router.nest_service(&base_path, build_serve_dir(serve, config)); + /// Sets up and returns a router + /// + /// Steps include: + /// - Setting up cors + /// - Setting up the proxy to the endpoint specified in the config + /// - Setting up the file serve service + /// - Setting up the websocket endpoint for devtools + fn setup_router( + serve: &Serve, + config: &DioxusCrate, + hot_reload_sockets: UnboundedSender, + build_status_sockets: UnboundedSender, + fullstack_address: Option, + build_status: SharedStatus, + ) -> Result { + let mut router = Router::new(); + let platform = serve.build_arguments.platform(); + + // Setup proxy for the endpoint specified in the config + for proxy_config in config.dioxus_config.web.proxy.iter() { + router = super::proxy::add_proxy(router, proxy_config)?; } - Platform::Liveview | Platform::Fullstack | Platform::StaticGeneration => { - // For fullstack and static generation, forward all requests to the server - let address = fullstack_address.unwrap(); - router = router.nest_service("/",super::proxy::proxy_to( + // server the dir if it's web, otherwise let the fullstack server itself handle it + match platform { + Platform::Web => { + // Route file service to output the .wasm and assets if this is a web build + let base_path = format!( + "/{}", + config + .dioxus_config + .web + .app + .base_path + .as_deref() + .unwrap_or_default() + .trim_matches('/') + ); + + router = router.nest_service(&base_path, build_serve_dir(serve, config)); + } + Platform::Liveview | Platform::Fullstack => { + // For fullstack and static generation, forward all requests to the server + let address = fullstack_address.unwrap(); + + router = router.nest_service("/",super::proxy::proxy_to( format!("http://{address}").parse().unwrap(), true, |error| { @@ -527,18 +520,18 @@ fn setup_router( .unwrap() }, )); + } + _ => {} } - _ => {} - } - // Setup middleware to intercept html requests if the build status is "Building" - router = router.layer(middleware::from_fn_with_state( - build_status, - build_status_middleware, - )); + // Setup middleware to intercept html requests if the build status is "Building" + router = router.layer(middleware::from_fn_with_state( + build_status, + build_status_middleware, + )); - // Setup websocket endpoint - and pass in the extension layer immediately after - router = router.nest( + // Setup websocket endpoint - and pass in the extension layer immediately after + router = router.nest( "/_dioxus", Router::new() .route( @@ -561,17 +554,18 @@ fn setup_router( .layer(Extension(build_status_sockets)), ); - // Setup cors - router = router.layer( - CorsLayer::new() - // allow `GET` and `POST` when accessing the resource - .allow_methods([Method::GET, Method::POST]) - // allow requests from any origin - .allow_origin(Any) - .allow_headers(Any), - ); + // Setup cors + router = router.layer( + CorsLayer::new() + // allow `GET` and `POST` when accessing the resource + .allow_methods([Method::GET, Method::POST]) + // allow requests from any origin + .allow_origin(Any) + .allow_headers(Any), + ); - Ok(router) + Ok(router) + } } fn build_serve_dir(serve: &Serve, cfg: &DioxusCrate) -> axum::routing::MethodRouter { diff --git a/packages/cli/src/serve/update.rs b/packages/cli/src/serve/update.rs index 7ad4d64c4b..db741a68ad 100644 --- a/packages/cli/src/serve/update.rs +++ b/packages/cli/src/serve/update.rs @@ -14,10 +14,11 @@ pub enum ServeUpdate { }, BuildReady { - results: Vec, + target: TargetPlatform, }, BuildFailed { + target: TargetPlatform, err: crate::Error, }, diff --git a/packages/cli/src/serve/util.rs b/packages/cli/src/serve/util.rs new file mode 100644 index 0000000000..b103a0ed50 --- /dev/null +++ b/packages/cli/src/serve/util.rs @@ -0,0 +1,25 @@ +use crate::builder::{Stage, TargetPlatform, UpdateBuildProgress, UpdateStage}; +use crate::cli::serve::Serve; +use crate::dioxus_crate::DioxusCrate; +use crate::Result; +use futures_util::FutureExt; +use std::future::{poll_fn, Future, IntoFuture}; +use std::task::Poll; +use tokio::task::yield_now; + +// Grab the output of a future that returns an option or wait forever +pub(crate) fn next_or_pending(f: F) -> impl Future +where + F: IntoFuture>, +{ + let pinned = f.into_future().fuse(); + let mut pinned = Box::pin(pinned); + poll_fn(move |cx| { + let next = pinned.as_mut().poll(cx); + match next { + Poll::Ready(Some(next)) => Poll::Ready(next), + _ => Poll::Pending, + } + }) + .fuse() +} diff --git a/packages/cli/src/serve/watcher.rs b/packages/cli/src/serve/watcher.rs index ead44e29cf..3ce323b229 100644 --- a/packages/cli/src/serve/watcher.rs +++ b/packages/cli/src/serve/watcher.rs @@ -1,6 +1,4 @@ -use std::collections::{HashMap, HashSet}; -use std::{fs, path::PathBuf, time::Duration}; - +use super::detect::is_wsl; use super::{hot_reloading_file_map::HotreloadError, update::ServeUpdate}; use crate::serve::hot_reloading_file_map::FileMap; use crate::{cli::serve::Serve, dioxus_crate::DioxusCrate}; @@ -13,6 +11,8 @@ use notify::{ event::{MetadataKind, ModifyKind}, Config, EventKind, }; +use std::collections::{HashMap, HashSet}; +use std::{fs, path::PathBuf, time::Duration}; /// This struct stores the file watcher and the filemap for the project. /// @@ -211,11 +211,11 @@ impl Watcher { // If the path is ignored, don't watch it if self.ignore.matched(path, path.is_dir()).is_ignore() { - tracing::trace!("Ignoring update to file: {:?}", path); + tracing::info!("Ignoring update to file: {:?}", path); continue; } - tracing::trace!("Enqueuing hotreload update to file: {:?}", path); + tracing::info!("Enqueuing hotreload update to file: {:?}", path); modified_files.push(path.clone()); } @@ -366,39 +366,6 @@ fn is_allowed_notify_event(event: ¬ify::Event) -> bool { } } -const WSL_1: &str = "/proc/sys/kernel/osrelease"; -const WSL_2: &str = "/proc/version"; -const WSL_KEYWORDS: [&str; 2] = ["microsoft", "wsl"]; - -/// Detects if `dx` is being ran in a WSL environment. -/// -/// We determine this based on whether the keyword `microsoft` or `wsl` is contained within the [`WSL_1`] or [`WSL_2`] files. -/// This may fail in the future as it isn't guaranteed by Microsoft. -/// See https://github.com/microsoft/WSL/issues/423#issuecomment-221627364 -fn is_wsl() -> bool { - // Test 1st File - if let Ok(content) = fs::read_to_string(WSL_1) { - let lowercase = content.to_lowercase(); - for keyword in WSL_KEYWORDS { - if lowercase.contains(keyword) { - return true; - } - } - } - - // Test 2nd File - if let Ok(content) = fs::read_to_string(WSL_2) { - let lowercase = content.to_lowercase(); - for keyword in WSL_KEYWORDS { - if lowercase.contains(keyword) { - return true; - } - } - } - - false -} - #[test] fn test_is_backup_file() { assert!(is_backup_file(PathBuf::from("examples/test.rs~"))); diff --git a/packages/devtools/Cargo.toml b/packages/devtools/Cargo.toml index 9d818a64bf..4b6680dbe3 100644 --- a/packages/devtools/Cargo.toml +++ b/packages/devtools/Cargo.toml @@ -10,8 +10,6 @@ description = "Hot reloading utilities for Dioxus" keywords = ["dom", "ui", "gui", "react", "hot-reloading"] [dependencies] -dioxus-rsx = { workspace = true } -dioxus-html = { workspace = true } dioxus-signals = { workspace = true } dioxus-core = { workspace = true, features = ["serialize"] } dioxus-devtools-types = { workspace = true } diff --git a/packages/interpreter/src/write_native_mutations.rs b/packages/interpreter/src/write_native_mutations.rs index 0fd196dbe1..afb5d33e03 100644 --- a/packages/interpreter/src/write_native_mutations.rs +++ b/packages/interpreter/src/write_native_mutations.rs @@ -1,8 +1,7 @@ use crate::unified_bindings::Interpreter as Channel; use dioxus_core::{Template, TemplateAttribute, TemplateNode, WriteMutations}; use dioxus_core_types::event_bubbles; -use dioxus_html::event_bubbles; -use sledgehammer_utils::rustc_hash::FxHashMap; +use rustc_hash::FxHashMap; /// The state needed to apply mutations to a channel. This state should be kept across all mutations for the app #[derive(Default)] diff --git a/packages/manganis-core/Cargo.toml b/packages/manganis-core/Cargo.toml index a8ced4f569..e4b10aa6d5 100644 --- a/packages/manganis-core/Cargo.toml +++ b/packages/manganis-core/Cargo.toml @@ -3,4 +3,6 @@ name = "manganis-core" edition = "2021" version.workspace = true -[dependencies] \ No newline at end of file +[dependencies] +serde_json = "1.0" +serde = { workspace = true, features = ["derive"] } diff --git a/packages/manganis-core/src/asset.rs b/packages/manganis-core/src/asset.rs new file mode 100644 index 0000000000..bd5461af85 --- /dev/null +++ b/packages/manganis-core/src/asset.rs @@ -0,0 +1,117 @@ +use serde::{Deserialize, Serialize}; +use std::{ + hash::{Hash, Hasher}, + path::PathBuf, + time::SystemTime, +}; + +/// The location we'll write to the link section - needs to be serializable +/// +/// This basically is 1:1 with `manganis/Asset` but with more metadata to be useful to the macro and cli +#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] +pub struct ResourceAsset { + /// The input path `/assets/blah.css` + pub input: PathBuf, + + /// The canonicalized asset + /// + /// `Users/dioxus/dev/app/assets/blah.css` + pub absolute: PathBuf, + + /// The post-bundle name of the asset - do we include the `assets` name? + /// + /// `blahcss123.css` + pub bundled: String, +} + +/// The maximum length of a path segment +const MAX_PATH_LENGTH: usize = 128; + +/// The length of the hash in the output path +const HASH_SIZE: usize = 16; + +#[derive(Debug)] +pub struct AssetError {} +impl ResourceAsset { + pub fn parse_any(raw: &str) -> Result { + // get the location where the asset is absolute, relative to + // + // IE + // /users/dioxus/dev/app/ + // is the root of + // /users/dioxus/dev/app/assets/blah.css + let mfst_dir = std::env::var("CARGO_MANIFEST_DIR") + .map(PathBuf::from) + .unwrap(); + + // 1. the input file should be a pathbuf + let input = PathBuf::from(raw); + + // 2. + let absolute = mfst_dir + .join(raw.trim_start_matches('/')) + .canonicalize() + .unwrap(); + + let bundled = Self::make_unique_name(absolute.clone()); + + Ok(Self { + input, + absolute, + bundled, + }) + } + + pub fn make_unique_name(file_path: PathBuf) -> String { + // Create a hasher + let mut hash = std::collections::hash_map::DefaultHasher::new(); + + // Open the file to get its options + let file = std::fs::File::open(&file_path).unwrap(); + let metadata = file.metadata().unwrap(); + let modified = metadata + .modified() + .unwrap_or_else(|_| SystemTime::UNIX_EPOCH); + + // Hash a bunch of metadata + // name, options, modified time, and maybe the version of our crate + // Hash the last time the file was updated and the file source. If either of these change, we need to regenerate the unique name + modified.hash(&mut hash); + file_path.hash(&mut hash); + + let uuid = hash.finish(); + let extension = file_path + .extension() + .map(|f| f.to_string_lossy()) + .map(|e| format!(".{e}")) + .unwrap_or_default(); + let file_name = Self::normalize_file_name(file_path); + + let out = format!("{file_name}{uuid:x}{extension}"); + assert!(out.len() <= MAX_PATH_LENGTH); + out + } + + fn normalize_file_name(location: PathBuf) -> String { + let file_name = location.file_name().unwrap(); + let last_segment = file_name.to_string_lossy(); + let extension = location.extension(); + let mut file_name = Self::to_alphanumeric_string_lossy(&last_segment); + + let extension_len = extension.map(|e| e.len() + 1).unwrap_or_default(); + let extension_and_hash_size = extension_len + HASH_SIZE; + + // If the file name is too long, we need to truncate it + if file_name.len() + extension_and_hash_size > MAX_PATH_LENGTH { + file_name = file_name[..MAX_PATH_LENGTH - extension_and_hash_size].to_string(); + } + + file_name + } + + fn to_alphanumeric_string_lossy(name: &str) -> String { + name.chars() + .filter(|c| c.is_alphanumeric()) + .collect::() + } +} diff --git a/packages/manganis-core/src/folder.rs b/packages/manganis-core/src/folder.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/manganis-core/src/lib.rs b/packages/manganis-core/src/lib.rs index e86444cf42..d4e991ad7e 100644 --- a/packages/manganis-core/src/lib.rs +++ b/packages/manganis-core/src/lib.rs @@ -1,69 +1,8 @@ -/// Information about the manganis link section for a given platform -#[derive(Debug, Clone, Copy)] -pub struct LinkSection { - /// The link section we pass to the static - pub link_section: &'static str, - /// The name of the section we find in the binary - pub name: &'static str, -} - -impl LinkSection { - /// The list of link sections for all supported platforms - pub const ALL: &'static [&'static LinkSection] = - &[Self::WASM, Self::MACOS, Self::WINDOWS, Self::ILLUMOS]; - - /// Returns the link section used in linux, android, fuchsia, psp, freebsd, and wasm32 - pub const WASM: &'static LinkSection = &LinkSection { - link_section: "manganis", - name: "manganis", - }; - - /// Returns the link section used in macOS, iOS, tvOS - pub const MACOS: &'static LinkSection = &LinkSection { - link_section: "__DATA,manganis,regular,no_dead_strip", - name: "manganis", - }; - - /// Returns the link section used in windows - pub const WINDOWS: &'static LinkSection = &LinkSection { - link_section: "mg", - name: "mg", - }; - - /// Returns the link section used in illumos - pub const ILLUMOS: &'static LinkSection = &LinkSection { - link_section: "set_manganis", - name: "set_manganis", - }; - - /// The link section used on the current platform - pub const CURRENT: &'static LinkSection = { - #[cfg(any( - target_os = "none", - target_os = "linux", - target_os = "android", - target_os = "fuchsia", - target_os = "psp", - target_os = "freebsd", - target_arch = "wasm32" - ))] - { - Self::WASM - } - - #[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos"))] - { - Self::MACOS - } - - #[cfg(target_os = "windows")] - { - Self::WINDOWS - } - - #[cfg(target_os = "illumos")] - { - Self::ILLUMOS - } - }; -} +mod asset; +mod folder; +mod linker; +mod options; + +pub use asset::*; +pub use linker::*; +pub use options::*; diff --git a/packages/manganis-core/src/linker.rs b/packages/manganis-core/src/linker.rs new file mode 100644 index 0000000000..e86444cf42 --- /dev/null +++ b/packages/manganis-core/src/linker.rs @@ -0,0 +1,69 @@ +/// Information about the manganis link section for a given platform +#[derive(Debug, Clone, Copy)] +pub struct LinkSection { + /// The link section we pass to the static + pub link_section: &'static str, + /// The name of the section we find in the binary + pub name: &'static str, +} + +impl LinkSection { + /// The list of link sections for all supported platforms + pub const ALL: &'static [&'static LinkSection] = + &[Self::WASM, Self::MACOS, Self::WINDOWS, Self::ILLUMOS]; + + /// Returns the link section used in linux, android, fuchsia, psp, freebsd, and wasm32 + pub const WASM: &'static LinkSection = &LinkSection { + link_section: "manganis", + name: "manganis", + }; + + /// Returns the link section used in macOS, iOS, tvOS + pub const MACOS: &'static LinkSection = &LinkSection { + link_section: "__DATA,manganis,regular,no_dead_strip", + name: "manganis", + }; + + /// Returns the link section used in windows + pub const WINDOWS: &'static LinkSection = &LinkSection { + link_section: "mg", + name: "mg", + }; + + /// Returns the link section used in illumos + pub const ILLUMOS: &'static LinkSection = &LinkSection { + link_section: "set_manganis", + name: "set_manganis", + }; + + /// The link section used on the current platform + pub const CURRENT: &'static LinkSection = { + #[cfg(any( + target_os = "none", + target_os = "linux", + target_os = "android", + target_os = "fuchsia", + target_os = "psp", + target_os = "freebsd", + target_arch = "wasm32" + ))] + { + Self::WASM + } + + #[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos"))] + { + Self::MACOS + } + + #[cfg(target_os = "windows")] + { + Self::WINDOWS + } + + #[cfg(target_os = "illumos")] + { + Self::ILLUMOS + } + }; +} diff --git a/packages/manganis-core/src/options.rs b/packages/manganis-core/src/options.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/manganis-macro/Cargo.toml b/packages/manganis-macro/Cargo.toml index 702adde198..e74cc7b899 100644 --- a/packages/manganis-macro/Cargo.toml +++ b/packages/manganis-macro/Cargo.toml @@ -15,9 +15,10 @@ proc-macro = true [dependencies] proc-macro2 = { version = "1.0" } quote = "1.0" -serde_json = "1.0" syn = { version = "2.0", features = ["full", "extra-traits"] } +serde_json = "1.0" serde = { workspace = true, features = ["derive"] } +manganis-core = { workspace = true } [features] default = [] diff --git a/packages/manganis-macro/src/asset.rs b/packages/manganis-macro/src/asset.rs index 2bf8c065ea..7fbb25c041 100644 --- a/packages/manganis-macro/src/asset.rs +++ b/packages/manganis-macro/src/asset.rs @@ -1,8 +1,5 @@ use core::panic; -// use manganis_common::{ -// CssOptions, FileOptions, FontOptions, ImageOptions, JsOptions, JsonOptions, MetadataAsset, -// ResourceAsset, TailwindAsset, UnknownFileOptions, VideoOptions, -// }; +use manganis_core::ResourceAsset; use proc_macro::TokenStream; use proc_macro2::Ident; use proc_macro2::TokenStream as TokenStream2; @@ -11,50 +8,22 @@ use serde::{Deserialize, Serialize}; use std::{collections::HashMap, fs::File, path::Path, sync::atomic::AtomicBool}; use std::{path::PathBuf, sync::atomic::Ordering}; use syn::{ - parenthesized, parse::Parse, parse_macro_input, punctuated::Punctuated, token::Token, Expr, - ExprLit, Lit, LitStr, PatLit, Token, + parenthesized, + parse::{Parse, ParseStream}, + parse_macro_input, + punctuated::Punctuated, + token::Token, + Expr, ExprLit, Lit, LitStr, PatLit, Token, }; +use crate::asset_options::MethodCallOption; + pub struct AssetParser { + /// The source of the trailing builder pattern option_source: TokenStream2, - resource: ResourceAsset, - name: Option, - // parsed_options: Option, -} - -#[derive(Serialize, Deserialize)] -struct ResourceAsset { - pub input: PathBuf, - pub local: Option, - pub bundled: PathBuf, -} -#[derive(Debug)] -struct AssetError {} -impl ResourceAsset { - fn parse_any(input: &str) -> Result { - let absolute = std::env::var("CARGO_MANIFEST_DIR") - .map(|s| s.into()) - .unwrap_or_else(|_| std::env::current_dir().unwrap()); - let absolute = absolute.join(input.trim_start_matches('/')); - - let input = PathBuf::from(input); - let local = Some(absolute.clone()); - let bundled = input.clone(); - // let local = match input.canonicalized().is_file() { - // true => Some(input.canonicalized()), - // false => None, - // }; - // let bundled = match input.canonicalized().is_file() { - // true => input.canonicalized(), - // false => input, - // }; - Ok(Self { - input, - local, - bundled, - }) - } + /// The asset itself + asset: ResourceAsset, } impl Parse for AssetParser { @@ -65,17 +34,18 @@ impl Parse for AssetParser { // asset!("myfile.png") // ``` // - // To narrow the type, use a call to get the refined type + // To narrow the type, use a method call to get the refined type // ``` // asset!( - // image("myfile.png") + // "myfile.png" + // .image() // .format(ImageType::Jpg) // .size(512, 512) // ) // ``` // // But we need to decide the hint first before parsing the options - fn parse(input: syn::parse::ParseStream) -> syn::Result { + fn parse(input: ParseStream) -> syn::Result { // Get the source of the macro, excluding the first token let option_source = { let fork = input.fork(); @@ -88,22 +58,17 @@ impl Parse for AssetParser { let src = src.value(); let resource = ResourceAsset::parse_any(&src).unwrap(); - fn parse_call(input: syn::parse::ParseStream) -> syn::Result { - let ident = input.parse::()?; + fn parse_call(input: ParseStream) -> syn::Result { + let method = input.parse::()?; let content; parenthesized!(content in input); - // Parse as puncutated literals - let lits = Punctuated::::parse_separated_nonempty(&content)?; + let args = Punctuated::::parse_separated_nonempty(&content)?; - Ok(MethodCallOption { - method: ident, - args: lits, - }) + Ok(MethodCallOption { method, args }) } let mut options = vec![]; - let name = None; while !input.is_empty() { let option = parse_call(input); @@ -111,59 +76,60 @@ impl Parse for AssetParser { options.push(option); } else { // todo: make sure we toss a warning in the output - let remaining: TokenStream2 = input.parse()?; + let _remaining: TokenStream2 = input.parse()?; } } - // let parsed_options = MethodCalls::new(options); - Ok(Self { option_source, - resource, - name, - // parsed_options,a + asset: resource, }) } } impl ToTokens for AssetParser { + // Need to generate: + // + // - 1. absolute file path on the user's system: `/users/dioxus/dev/project/assets/blah.css` + // - 2. original input in case that's useful: `../blah.css` + // - 3. path relative to the CARGO_MANIFEST_DIR - and then we'll add a `/`: `/assets/blah.css + // - 4. file from which this macro was called: `/users/dioxus/dev/project/src/lib.rs` + // - 5: The link section containing all this data + // - 6: the input tokens such that the builder gets validated by the const code + // - 7: the bundled name `/blahcss123.css` + // + // Not that we'll use everything, but at least we have this metadata for more post-processing. + // + // For now, `2` and `3` will be the same since we don't support relative paths... a bit of + // a limitation from rust itself. We technically could support them but not without some hoops + // to jump through fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { - let option_source = &self.option_source; - let asset = &self.resource; - let link_section = crate::generate_link_section(&asset); - let input = asset.input.display().to_string(); - let bundled = asset.bundled.display().to_string(); - - let local = match asset.local.as_ref() { - Some(local) => { - let local = local.display().to_string(); - quote! { #local } - } - None => { - todo!("relative paths are not supported yet") - // quote! { - // { - // // ensure it exists by throwing away the include_bytes - // static _BLAH: &[u8] = include_bytes!(#input); - - // // But then pass along the path - // concat!(env!("CARGO_MANIFEST_DIR"), "/", file!(), "//", #input) - // } - // } - } - }; + // 1. the link section itself + let link_section = crate::generate_link_section(&self.asset); + + // 2. original + let input = self.asset.input.display().to_string(); - let manifest_dir: PathBuf = std::env::var("CARGO_MANIFEST_DIR").unwrap().into(); - let displayed_manifest_dir = manifest_dir.display().to_string(); + // 3. resolved on the user's system + let local = self.asset.absolute.display().to_string(); + + // 4. bundled + let bundled = self.asset.bundled.to_string(); + + // 5. source tokens + let option_source = &self.option_source; tokens.extend(quote! { Asset::new( { #link_section - manganis::AssetSource { + manganis::Asset { + // "/assets/blah.css" input: #input, - source_file: concat!(#displayed_manifest_dir, "/", file!()), + + // "/users/dioxus/dev/app/assets/blah.css" local: #local, + bundled: #bundled, } } @@ -171,45 +137,3 @@ impl ToTokens for AssetParser { }) } } - -struct MethodCalls { - options: Vec, -} - -/// A builder method in the form of `.method(arg1, arg2)` -struct MethodCallOption { - method: syn::Ident, - args: Punctuated, -} - -impl MethodCalls { - // fn new(args: Vec) -> Option { - // let asset_type = args.first()?.method.to_string(); - - // let stack = args - // .into_iter() - // .skip(1) - // .map(|x| (x.method.to_string(), x.args.into_iter().collect::>())) - // .collect::>>(); - - // let opts = match asset_type.as_str() { - // "image" => { - // let mut opts = ImageOptions::new(manganis_common::ImageType::Avif, Some((32, 32))); - // // opts.set_preload(preload); - // // opts.set_url_encoded(url_encoded); - // // opts.set_low_quality_preview(low_quality_preview); - // FileOptions::Image(opts) - // } - - // "video" => FileOptions::Video(VideoOptions::new(todo!())), - // "font" => FileOptions::Font(FontOptions::new(todo!())), - // "css" => FileOptions::Css(CssOptions::new()), - // "js" => FileOptions::Js(JsOptions::new(todo!())), - // "json" => FileOptions::Json(JsonOptions::new()), - // other => FileOptions::Other(UnknownFileOptions::new(todo!())), - // }; - - // Some(opts) - // None - // } -} diff --git a/packages/manganis-macro/src/asset_options.rs b/packages/manganis-macro/src/asset_options.rs new file mode 100644 index 0000000000..cda33625d3 --- /dev/null +++ b/packages/manganis-macro/src/asset_options.rs @@ -0,0 +1,77 @@ +use core::panic; +use manganis_core::ResourceAsset; +use proc_macro::TokenStream; +use proc_macro2::Ident; +use proc_macro2::TokenStream as TokenStream2; +use quote::{quote, quote_spanned, ToTokens}; +use serde::{Deserialize, Serialize}; +use std::{collections::HashMap, fs::File, path::Path, sync::atomic::AtomicBool}; +use std::{path::PathBuf, sync::atomic::Ordering}; +use syn::{ + parenthesized, + parse::{Parse, ParseStream}, + parse_macro_input, + punctuated::Punctuated, + token::Token, + Expr, ExprLit, Lit, LitStr, PatLit, Token, +}; + +pub struct MethodCalls { + pub options: Vec, +} + +/// A builder method in the form of `.method(arg1, arg2)` +pub struct MethodCallOption { + pub method: syn::Ident, + pub args: Punctuated, +} + +impl MethodCalls { + // fn new(args: Vec) -> Option { + // let asset_type = args.first()?.method.to_string(); + + // let stack = args + // .into_iter() + // .skip(1) + // .map(|x| (x.method.to_string(), x.args.into_iter().collect::>())) + // .collect::>>(); + + // let opts = match asset_type.as_str() { + // "image" => { + // let mut opts = ImageOptions::new(manganis_common::ImageType::Avif, Some((32, 32))); + // // opts.set_preload(preload); + // // opts.set_url_encoded(url_encoded); + // // opts.set_low_quality_preview(low_quality_preview); + // FileOptions::Image(opts) + // } + + // "video" => FileOptions::Video(VideoOptions::new(todo!())), + // "font" => FileOptions::Font(FontOptions::new(todo!())), + // "css" => FileOptions::Css(CssOptions::new()), + // "js" => FileOptions::Js(JsOptions::new(todo!())), + // "json" => FileOptions::Json(JsonOptions::new()), + // other => FileOptions::Other(UnknownFileOptions::new(todo!())), + // }; + + // Some(opts) + // None + // } +} + +// let local = match asset.local.as_ref() { +// Some(local) => { +// let local = local.display().to_string(); +// quote! { #local } +// } +// None => { +// todo!("relative paths are not supported yet") +// // quote! { +// // { +// // // ensure it exists by throwing away the include_bytes +// // static _BLAH: &[u8] = include_bytes!(#input); +// // // But then pass along the path +// // concat!(env!("CARGO_MANIFEST_DIR"), "/", file!(), "//", #input) +// // } +// // } +// } +// }; diff --git a/packages/manganis-macro/src/lib.rs b/packages/manganis-macro/src/lib.rs index 6313e33621..46f73bf840 100644 --- a/packages/manganis-macro/src/lib.rs +++ b/packages/manganis-macro/src/lib.rs @@ -7,49 +7,13 @@ use proc_macro2::TokenStream as TokenStream2; use quote::{quote, quote_spanned, ToTokens}; use serde::Serialize; use std::sync::atomic::AtomicBool; -use std::sync::atomic::Ordering; use syn::{parse::Parse, parse_macro_input, LitStr}; -pub(crate) mod asset; - -static LOG_FILE_FRESH: AtomicBool = AtomicBool::new(false); - -fn trace_to_file() { - // // If this is the first time the macro is used in the crate, set the subscriber to write to a file - // if !LOG_FILE_FRESH.fetch_or(true, Ordering::Relaxed) { - // let path = macro_log_file(); - // std::fs::create_dir_all(path.parent().unwrap()).unwrap(); - // let file = std::fs::OpenOptions::new() - // .create(true) - // .write(true) - // .truncate(true) - // .open(path) - // .unwrap(); - // tracing_subscriber::fmt::fmt().with_writer(file).init(); - // } -} - -/// this new approach will store the assets descriptions *inside the executable*. -/// The trick is to use the `link_section` attribute. -/// We force rust to store a json representation of the asset description -/// inside a particular region of the binary, with the label "manganis". -/// After linking, the "manganis" sections of the different executables will be merged. -fn generate_link_section(asset: &impl Serialize) -> TokenStream2 { - let position = proc_macro2::Span::call_site(); - - let asset_description = serde_json::to_string(asset).unwrap(); - - let len = asset_description.as_bytes().len(); - let asset_bytes = syn::LitByteStr::new(asset_description.as_bytes(), position); - - let section_name = syn::LitStr::new(LinkSection::CURRENT.link_section, position); +pub(crate) mod asset; +pub(crate) mod asset_options; +pub(crate) mod linker; - quote! { - #[link_section = #section_name] - #[used] - static ASSET: [u8; #len] = * #asset_bytes; - } -} +use linker::generate_link_section; /// The mg macro collects assets that will be included in the final binary /// @@ -99,14 +63,9 @@ fn generate_link_section(asset: &impl Serialize) -> TokenStream2 { /// ``` #[proc_macro] pub fn asset(input: TokenStream) -> TokenStream { - trace_to_file(); let asset = parse_macro_input!(input as asset::AssetParser); - quote! { - #asset - } - .into_token_stream() - .into() + quote! { #asset }.into_token_stream().into() } /// // You can also collect arbitrary key-value pairs. The meaning of these pairs is determined by the CLI that processes your assets @@ -129,8 +88,6 @@ pub fn meta(input: TokenStream) -> TokenStream { } } - trace_to_file(); - todo!() // let md = parse_macro_input!(input as MetadataValue); @@ -145,73 +102,3 @@ pub fn meta(input: TokenStream) -> TokenStream { // .into_token_stream() // .into() } - -/// Information about the manganis link section for a given platform -#[derive(Debug, Clone, Copy)] -struct LinkSection { - /// The link section we pass to the static - pub link_section: &'static str, - /// The name of the section we find in the binary - pub name: &'static str, -} - -impl LinkSection { - /// The list of link sections for all supported platforms - pub const ALL: &'static [&'static LinkSection] = - &[Self::WASM, Self::MACOS, Self::WINDOWS, Self::ILLUMOS]; - - /// Returns the link section used in linux, android, fuchsia, psp, freebsd, and wasm32 - pub const WASM: &'static LinkSection = &LinkSection { - link_section: "manganis", - name: "manganis", - }; - - /// Returns the link section used in macOS, iOS, tvOS - pub const MACOS: &'static LinkSection = &LinkSection { - link_section: "__DATA,manganis,regular,no_dead_strip", - name: "manganis", - }; - - /// Returns the link section used in windows - pub const WINDOWS: &'static LinkSection = &LinkSection { - link_section: "mg", - name: "mg", - }; - - /// Returns the link section used in illumos - pub const ILLUMOS: &'static LinkSection = &LinkSection { - link_section: "set_manganis", - name: "set_manganis", - }; - - /// The link section used on the current platform - pub const CURRENT: &'static LinkSection = { - #[cfg(any( - target_os = "none", - target_os = "linux", - target_os = "android", - target_os = "fuchsia", - target_os = "psp", - target_os = "freebsd", - target_arch = "wasm32" - ))] - { - Self::WASM - } - - #[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos"))] - { - Self::MACOS - } - - #[cfg(target_os = "windows")] - { - Self::WINDOWS - } - - #[cfg(target_os = "illumos")] - { - Self::ILLUMOS - } - }; -} diff --git a/packages/manganis-macro/src/linker.rs b/packages/manganis-macro/src/linker.rs new file mode 100644 index 0000000000..de77ab9240 --- /dev/null +++ b/packages/manganis-macro/src/linker.rs @@ -0,0 +1,100 @@ +use proc_macro::TokenStream; +use proc_macro2::Ident; +use proc_macro2::TokenStream as TokenStream2; +use quote::{quote, quote_spanned, ToTokens}; +use serde::Serialize; +use std::sync::atomic::AtomicBool; +use syn::{parse::Parse, parse_macro_input, LitStr}; + +/// this new approach will store the assets descriptions *inside the executable*. +/// The trick is to use the `link_section` attribute. +/// We force rust to store a json representation of the asset description +/// inside a particular region of the binary, with the label "manganis". +/// After linking, the "manganis" sections of the different executables will be merged. +pub fn generate_link_section(asset: &impl Serialize) -> TokenStream2 { + let position = proc_macro2::Span::call_site(); + + let asset_description = serde_json::to_string(asset).unwrap(); + + let len = asset_description.as_bytes().len(); + + let asset_bytes = syn::LitByteStr::new(asset_description.as_bytes(), position); + + let section_name = syn::LitStr::new(LinkSection::CURRENT.link_section, position); + + quote! { + #[link_section = #section_name] + #[used] + static ASSET: [u8; #len] = * #asset_bytes; + } +} + +/// Information about the manganis link section for a given platform +#[derive(Debug, Clone, Copy)] +struct LinkSection { + /// The link section we pass to the static + pub link_section: &'static str, + /// The name of the section we find in the binary + pub name: &'static str, +} + +impl LinkSection { + /// The list of link sections for all supported platforms + pub const ALL: &'static [&'static LinkSection] = + &[Self::WASM, Self::MACOS, Self::WINDOWS, Self::ILLUMOS]; + + /// Returns the link section used in linux, android, fuchsia, psp, freebsd, and wasm32 + pub const WASM: &'static LinkSection = &LinkSection { + link_section: "manganis", + name: "manganis", + }; + + /// Returns the link section used in macOS, iOS, tvOS + pub const MACOS: &'static LinkSection = &LinkSection { + link_section: "__DATA,manganis,regular,no_dead_strip", + name: "manganis", + }; + + /// Returns the link section used in windows + pub const WINDOWS: &'static LinkSection = &LinkSection { + link_section: "mg", + name: "mg", + }; + + /// Returns the link section used in illumos + pub const ILLUMOS: &'static LinkSection = &LinkSection { + link_section: "set_manganis", + name: "set_manganis", + }; + + /// The link section used on the current platform + pub const CURRENT: &'static LinkSection = { + #[cfg(any( + target_os = "none", + target_os = "linux", + target_os = "android", + target_os = "fuchsia", + target_os = "psp", + target_os = "freebsd", + target_arch = "wasm32" + ))] + { + Self::WASM + } + + #[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos"))] + { + Self::MACOS + } + + #[cfg(target_os = "windows")] + { + Self::WINDOWS + } + + #[cfg(target_os = "illumos")] + { + Self::ILLUMOS + } + }; +} diff --git a/packages/manganis/src/builder.rs b/packages/manganis/src/builder.rs index f0123c7a5d..3eb92932d7 100644 --- a/packages/manganis/src/builder.rs +++ b/packages/manganis/src/builder.rs @@ -1,99 +1,37 @@ -use std::path::{Path, PathBuf}; - use dioxus_core_types::DioxusFormattable; +use std::path::PathBuf; /// Asset -#[derive(Debug, PartialEq, Clone, Copy, Hash)] +#[derive(Debug, PartialEq, PartialOrd, Clone, Copy, Hash)] pub struct Asset { - src: AssetSource, -} - -impl From for String { - fn from(value: Asset) -> Self { - value.to_string() - } -} -impl From for Option { - fn from(value: Asset) -> Self { - Some(value.to_string()) - } -} - -impl Asset { - /// - pub const fn new(src: AssetSource) -> Self { - Self { src } - } - - /// - pub const fn folder(self) -> FolderAsset { - FolderAsset::new(self.src) - } - - /// Convert this asset into an image asset - pub const fn image(self) -> ImageAsset { - ImageAsset::new(self.src) - } - - /// - pub const fn video(self) -> VideoAsset { - VideoAsset::new(self.src) - } - - /// - pub const fn json(self) -> JsonAsset { - JsonAsset::new(self.src) - } + /// The input URI given to the macro + pub input: &'static str, - /// - pub const fn css(self) -> CssAsset { - CssAsset::new(self.src) - } + /// The absolute path to the asset on the filesystem + pub local: &'static str, + /// The asset location after its been bundled /// - pub const fn javascript(self) -> JavaScriptAsset { - JavaScriptAsset::new(self.src) - } + /// `blah123.css`` + pub bundled: &'static str, +} - /// - pub const fn typescript(self) -> TypeScriptAsset { - TypeScriptAsset::new(self.src) +impl Asset { + /// Create a new asset + pub const fn new(self) -> Self { + self } /// Get the path to the asset pub fn path(&self) -> PathBuf { - PathBuf::from(self.src.input.to_string()) + PathBuf::from(self.input.to_string()) } /// Get the path to the asset pub fn relative_path(&self) -> PathBuf { - PathBuf::from(self.src.input.trim_start_matches("/").to_string()) - } -} - -impl std::fmt::Display for Asset { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.src.resolve().display()) + PathBuf::from(self.input.trim_start_matches("/").to_string()) } -} -/// -#[derive(Debug, PartialEq, PartialOrd, Clone, Copy, Hash)] -pub struct AssetSource { - /// The input URI given to the macro - pub input: &'static str, - - /// The sourcefile of the asset - pub source_file: &'static str, - - /// The absolute path to the asset on the filesystem - pub local: &'static str, - - /// - pub bundled: &'static str, -} - -impl AssetSource { /// Return a canonicalized path to the asset pub fn resolve(&self) -> PathBuf { // if we're running with cargo in the loop, we can use the absolute path. @@ -102,14 +40,37 @@ impl AssetSource { return PathBuf::from(self.local); } - // Otherwise, we need to resolve the bundled path against the basepath. - // on native this will be the bundled path + // todo: actually properly resolve this base_path() - .unwrap_or_else(|| std::env::current_dir().unwrap()) - .join(self.input.trim_start_matches('/')) + .unwrap_or_else(|| std::env::current_dir().unwrap_or("/".into())) + .join(PathBuf::from(self.bundled.trim_start_matches('/'))) + } +} + +impl From for String { + fn from(value: Asset) -> Self { + value.to_string() + } +} +impl From for Option { + fn from(value: Asset) -> Self { + Some(value.to_string()) + } +} + +impl std::fmt::Display for Asset { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.resolve().display()) + } +} + +impl DioxusFormattable for Asset { + fn format(&self) -> std::borrow::Cow<'static, str> { + std::borrow::Cow::Owned(self.to_string()) } } +#[allow(unreachable_code)] fn base_path() -> Option { // Use the prescence of the bundle to determine if we're in dev mode // todo: for other platforms, we should check their bundles too. This currently only works for macOS and iOS @@ -140,252 +101,11 @@ fn base_path() -> Option { return dunce::canonicalize(resources_dir).ok(); } - // todo: this needs to be real canonicalizations - // let root; - - // #[cfg(target_os = "wasm32-unknown-unknown")] - // { - // root = "/".to_string(); - // } - - // #[cfg(not(target_os = "wasm32-unknown-unknown"))] - // { - // root = std::env::current_dir().unwrap(); - // } - - // let var_base_path = std::env::var("MANGANIS_BASE_PATH") - // .unwrap_or_else(|| "/".to_string()) - // .map(|p| PathBuf::from(p)); - - // var_base_path.unwrap_or(root) - None -} - -/// -pub struct CssAsset { - src: AssetSource, -} - -impl CssAsset { - /// - pub const fn new(src: AssetSource) -> Self { - Self { src } - } - - /// - pub const fn minify(self, minify: bool) -> Self { - todo!() - } -} - -/// -pub struct VideoAsset { - src: AssetSource, -} - -impl VideoAsset { - /// - pub const fn new(src: AssetSource) -> Self { - Self { src } - } -} - -/// -/// -pub struct JsonAsset { - src: AssetSource, -} -impl JsonAsset { - /// - pub const fn new(src: AssetSource) -> Self { - Self { src } - } - - /// Make the json preloaded - /// - /// > **Note**: This will do nothing outside of the `asset!` macro - /// - /// Preloading json will make the json start to load as soon as possible. This is useful for json that will be run soon after the page loads or json that may not be used immediately, but should start loading sooner - /// - /// ```rust - /// const _: &str = manganis::asset!(json("assets/data.json").preload()); - /// ``` - #[allow(unused)] - pub const fn preload(self) -> Self { - self - } - - /// Make the json URL encoded - /// - /// > **Note**: This will do nothing outside of the `asset!` macro - /// - /// URL encoding an image inlines the data of the json into the URL. This is useful for small json files that should load as soon as the html is parsed - /// - /// ```rust - /// const _: &str = manganis::asset!(json("assets/data.json").url_encoded()); - /// ``` - #[allow(unused)] - pub const fn url_encoded(self) -> Self { - self - } -} - -/// -/// -/// -pub struct JavaScriptAsset { - src: AssetSource, -} -impl JavaScriptAsset { - /// - pub const fn new(src: AssetSource) -> Self { - Self { src } - } -} - -/// -/// -pub struct TypeScriptAsset { - src: AssetSource, -} - -impl TypeScriptAsset { - /// - pub const fn new(src: AssetSource) -> Self { - Self { src } - } -} -/// -pub struct FolderAsset { - src: AssetSource, -} - -impl FolderAsset { - /// - pub const fn new(src: AssetSource) -> Self { - Self { src } - } - - /// - pub const fn build(self) -> FolderAsset { - FolderAsset { src: self.src } - } -} - -/// Asset -#[derive(Debug, PartialEq, Clone, Copy, Hash)] -pub struct ImageAsset { - src: AssetSource, -} - -impl DioxusFormattable for ImageAsset { - fn format(&self) -> std::borrow::Cow<'static, str> { - std::borrow::Cow::Owned(self.to_string()) - } -} -impl DioxusFormattable for Asset { - fn format(&self) -> std::borrow::Cow<'static, str> { - std::borrow::Cow::Owned(self.to_string()) - } -} - -impl std::fmt::Display for ImageAsset { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.src.resolve().display()) - } -} - -/// The type of an image. You can read more about the tradeoffs between image formats [here](https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types) -#[derive(Debug, PartialEq, PartialOrd, Clone, Copy, Hash)] -pub enum ImageType { - /// A png image. Png images cannot contain transparency and tend to compress worse than other formats - Png, - /// A jpg image. Jpg images can contain transparency and tend to compress better than png images - Jpg, - /// A webp image. Webp images can contain transparency and tend to compress better than jpg images - Webp, - /// An avif image. Avif images can compress slightly better than webp images but are not supported by all browsers - Avif, -} - -impl ImageAsset { - /// - pub const fn new(src: AssetSource) -> Self { - Self { src } - } - - /// - pub const fn build(self) -> ImageAsset { - ImageAsset { src: self.src } - } - - /// Sets the format of the image - /// - /// > **Note**: This will do nothing outside of the `asset!` macro - /// - /// Choosing the right format can make your site load much faster. Webp and avif images tend to be a good default for most images - /// - /// ```rust - /// const _: manganis::ImageAsset = manganis::asset!(image("https://avatars.githubusercontent.com/u/79236386?s=48&v=4").format(ImageType::Webp)); - /// ``` - #[allow(unused)] - pub const fn format(self, format: ImageType) -> Self { - self - } - - /// Sets the size of the image - /// - /// > **Note**: This will do nothing outside of the `asset!` macro - /// - /// If you only use the image in one place, you can set the size of the image to the size it will be displayed at. This will make the image load faster - /// - /// ```rust - /// const _: manganis::ImageAsset = manganis::asset!(image("https://avatars.githubusercontent.com/u/79236386?s=48&v=4").size(512, 512)); - /// ``` - #[allow(unused)] - pub const fn size(self, x: u32, y: u32) -> Self { - self - } - - /// Make the image use a low quality preview - /// - /// > **Note**: This will do nothing outside of the `asset!` macro - /// - /// A low quality preview is a small version of the image that will load faster. This is useful for large images on mobile devices that may take longer to load - /// - /// ```rust - /// const _: manganis::ImageAsset = manganis::asset!(image("https://avatars.githubusercontent.com/u/79236386?s=48&v=4").low_quality_preview()); - /// ``` - #[allow(unused)] - pub const fn low_quality_preview(self) -> Self { - self - } - - /// Make the image preloaded - /// - /// > **Note**: This will do nothing outside of the `asset!` macro - /// - /// Preloading an image will make the image start to load as soon as possible. This is useful for images that will be displayed soon after the page loads or images that may not be visible immediately, but should start loading sooner - /// - /// ```rust - /// const _: manganis::ImageAsset = manganis::asset!(image("https://avatars.githubusercontent.com/u/79236386?s=48&v=4").preload()); - /// ``` - #[allow(unused)] - pub const fn preload(self) -> Self { - self + // web-wasm + #[cfg(target_os = "wasm32-unknown-unknown")] + { + return = Some(PathBuf::from("/")) } - /// Make the image URL encoded - /// - /// > **Note**: This will do nothing outside of the `asset!` macro - /// - /// URL encoding an image inlines the data of the image into the URL. This is useful for small images that should load as soon as the html is parsed - /// - /// ```rust - /// const _: manganis::ImageAsset = manganis::asset!(image("https://avatars.githubusercontent.com/u/79236386?s=48&v=4").url_encoded()); - /// ``` - #[allow(unused)] - pub const fn url_encoded(self) -> Self { - self - } + None } diff --git a/packages/manganis/src/folder.rs b/packages/manganis/src/folder.rs index a5c1e2697f..8731c5d511 100644 --- a/packages/manganis/src/folder.rs +++ b/packages/manganis/src/folder.rs @@ -7,6 +7,20 @@ pub struct FolderAsset { src: Asset, } +impl Asset { + /// + pub const fn folder(self) -> FolderAsset { + FolderAsset::new(self) + } +} + +impl FolderAsset { + /// + pub const fn new(src: Asset) -> Self { + Self { src } + } +} + /// pub struct FolderAssetBuilder; diff --git a/packages/manganis/src/images.rs b/packages/manganis/src/images.rs index e05626ed07..b7ef40cdf2 100644 --- a/packages/manganis/src/images.rs +++ b/packages/manganis/src/images.rs @@ -1,3 +1,5 @@ +use dioxus_core_types::DioxusFormattable; + use crate::Asset; /// An image asset that is built by the [`asset!`] macro @@ -11,6 +13,13 @@ pub struct ImageAsset { caption: Option<&'static str>, } +impl Asset { + /// Convert this asset into an image asset + pub const fn image(self) -> ImageAsset { + ImageAsset::new(self) + } +} + impl ImageAsset { /// Creates a new image asset pub const fn new(path: Asset) -> Self { @@ -61,6 +70,12 @@ impl std::fmt::Display for ImageAsset { } } +impl DioxusFormattable for ImageAsset { + fn format(&self) -> std::borrow::Cow<'static, str> { + std::borrow::Cow::Owned(self.to_string()) + } +} + /// The type of an image. You can read more about the tradeoffs between image formats [here](https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types) #[derive(Debug, PartialEq, PartialOrd, Clone, Copy, Hash)] pub enum ImageType { diff --git a/packages/manganis/src/lib.rs b/packages/manganis/src/lib.rs index f0c056104c..9680e40c97 100644 --- a/packages/manganis/src/lib.rs +++ b/packages/manganis/src/lib.rs @@ -4,53 +4,11 @@ #[cfg(feature = "macro")] pub use manganis_macro::*; -// mod asset; -// pub use asset::*; +mod folder; +pub use folder::*; -// mod csss; -// pub use csss::*; - -// mod files; -// pub use files::*; - -// mod folder; -// pub use folder::*; - -// mod fonts; -// pub use fonts::*; - -// mod images; -// pub use images::*; - -// mod jsons; -// pub use jsons::*; - -// mod jss; -// pub use jss::*; +mod images; +pub use images::*; mod builder; pub use builder::*; - -/// A trait for something that can be used in the `asset!` macro -/// -/// > **Note**: These types will do nothing outside of the `asset!` macro -pub trait ForMgMacro: __private::Sealed + Sync + Send {} - -mod __private { - use super::*; - - pub trait Sealed {} - - // impl Sealed for FolderAssetBuilder {} - // impl Sealed for FontAssetBuilder {} - // impl Sealed for ImageAssetBuilder {} - // impl Sealed for JsAssetBuilder {} - // impl Sealed for JsonAssetBuilder {} - // impl Sealed for CssAssetBuilder {} - impl Sealed for &'static str {} -} - -// impl ForMgMacro for FolderAssetBuilder {} -// impl ForMgMacro for ImageAssetBuilder {} -// impl ForMgMacro for FontAssetBuilder {} -// impl ForMgMacro for &'static str {} diff --git a/packages/manganis/src/new.rs b/packages/manganis/src/new.rs new file mode 100644 index 0000000000..61c88a281a --- /dev/null +++ b/packages/manganis/src/new.rs @@ -0,0 +1,141 @@ +/// +pub const fn video(self) -> VideoAsset { + VideoAsset::new(self.src) +} + +/// +pub const fn json(self) -> JsonAsset { + JsonAsset::new(self.src) +} + +/// +pub const fn css(self) -> CssAsset { + CssAsset::new(self.src) +} + +/// +pub const fn javascript(self) -> JavaScriptAsset { + JavaScriptAsset::new(self.src) +} + +/// +pub const fn typescript(self) -> TypeScriptAsset { + TypeScriptAsset::new(self.src) +} + +/// +pub struct CssAsset { + src: AssetSource, +} + +impl CssAsset { + /// + pub const fn new(src: AssetSource) -> Self { + Self { src } + } + + /// + pub const fn minify(self, minify: bool) -> Self { + todo!() + } +} + +/// +pub struct VideoAsset { + src: AssetSource, +} + +impl VideoAsset { + /// + pub const fn new(src: AssetSource) -> Self { + Self { src } + } +} + +/// +/// +pub struct JsonAsset { + src: AssetSource, +} +impl JsonAsset { + /// + pub const fn new(src: AssetSource) -> Self { + Self { src } + } + + /// Make the json preloaded + /// + /// > **Note**: This will do nothing outside of the `asset!` macro + /// + /// Preloading json will make the json start to load as soon as possible. This is useful for json that will be run soon after the page loads or json that may not be used immediately, but should start loading sooner + /// + /// ```rust + /// const _: &str = manganis::asset!(json("assets/data.json").preload()); + /// ``` + #[allow(unused)] + pub const fn preload(self) -> Self { + self + } + + /// Make the json URL encoded + /// + /// > **Note**: This will do nothing outside of the `asset!` macro + /// + /// URL encoding an image inlines the data of the json into the URL. This is useful for small json files that should load as soon as the html is parsed + /// + /// ```rust + /// const _: &str = manganis::asset!(json("assets/data.json").url_encoded()); + /// ``` + #[allow(unused)] + pub const fn url_encoded(self) -> Self { + self + } +} + +/// +/// +/// +pub struct JavaScriptAsset { + src: AssetSource, +} +impl JavaScriptAsset { + /// + pub const fn new(src: AssetSource) -> Self { + Self { src } + } +} + +/// +/// +pub struct TypeScriptAsset { + src: AssetSource, +} + +impl TypeScriptAsset { + /// + pub const fn new(src: AssetSource) -> Self { + Self { src } + } +} +/// +pub struct FolderAsset { + src: AssetSource, +} + +impl FolderAsset { + /// + pub const fn new(src: AssetSource) -> Self { + Self { src } + } + + /// + pub const fn build(self) -> FolderAsset { + FolderAsset { src: self.src } + } +} + +/// Asset +#[derive(Debug, PartialEq, Clone, Copy, Hash)] +pub struct ImageAsset { + src: AssetSource, +} diff --git a/packages/web/Cargo.toml b/packages/web/Cargo.toml index 7fc0ce09c2..5ca763a00c 100644 --- a/packages/web/Cargo.toml +++ b/packages/web/Cargo.toml @@ -32,7 +32,7 @@ js-sys = "0.3.56" rustc-hash = { workspace = true } serde = { workspace = true, optional = true } serde_json = { workspace = true, optional = true } -serde-wasm-bindgen = { version = "0.5.0", optional = true } +serde-wasm-bindgen = { version = "0.6.5", optional = true } tracing = { workspace = true } wasm-bindgen = { workspace = true } wasm-bindgen-futures = "0.4.29" @@ -42,12 +42,10 @@ version = "0.3.56" features = [ "AnimationEvent", "ClipboardEvent", - "CloseEvent", "Comment", "CompositionEvent", "console", "CustomEvent", - "CustomEvent", "DataTransfer", "Document", "DragEvent", @@ -79,7 +77,7 @@ features = [ lazy-js-bundle = { workspace = true } [features] -default = ["panic_hook", "full"] +default = ["panic_hook"] full = [ "devtools", "mounted", diff --git a/packages/web/src/devtools.rs b/packages/web/src/devtools.rs index 59fd12b46e..ada0ed0ff9 100644 --- a/packages/web/src/devtools.rs +++ b/packages/web/src/devtools.rs @@ -13,7 +13,7 @@ use futures_channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender}; use js_sys::JsString; use wasm_bindgen::JsCast; use wasm_bindgen::{closure::Closure, JsValue}; -use web_sys::{window, CloseEvent, MessageEvent, WebSocket}; +use web_sys::{window, Event, MessageEvent, WebSocket}; const POLL_INTERVAL_MIN: i32 = 250; const POLL_INTERVAL_MAX: i32 = 4000; @@ -111,11 +111,13 @@ fn make_ws(tx: UnboundedSender, poll_interval: i32, reload: bool) // Set the onclose handler to reload the page if the connection is closed ws.set_onclose(Some( - Closure::::new(move |e: CloseEvent| { + Closure::::new(move |e: Event| { // Firefox will send a 1001 code when the connection is closed because the page is reloaded // Only firefox will trigger the onclose event when the page is reloaded manually: https://stackoverflow.com/questions/10965720/should-websocket-onclose-be-triggered-by-user-navigation-or-refresh // We should not reload the page in this case - if e.code() == 1001 { + if js_sys::Reflect::get(&e, &"code".into()).map(|f| f.as_f64().unwrap_or(0.0)) + == Ok(1001.0) + { return; } From 67eb0c300d1aff50f4ff9a10b4b4261bb78598f1 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Sun, 1 Sep 2024 22:00:25 -0700 Subject: [PATCH 057/139] switch to unbounded_send --- packages/cli/src/assets.rs | 3 +- packages/cli/src/assets/manifest.rs | 19 +++++++-- packages/cli/src/builder/assets.rs | 37 +++++++++++------ packages/cli/src/builder/cargo.rs | 19 ++++----- packages/cli/src/builder/fullstack.rs | 8 ++-- packages/cli/src/builder/progress.rs | 8 ++-- packages/cli/src/builder/web.rs | 4 +- packages/cli/src/cli/link.rs | 3 ++ packages/cli/src/cli/serve.rs | 11 ++--- packages/cli/src/dioxus_crate.rs | 3 +- packages/cli/src/main.rs | 3 +- packages/cli/src/serve/builder.rs | 4 +- packages/cli/src/serve/mod.rs | 34 ++++++++------- packages/cli/src/serve/output.rs | 7 +++- packages/cli/src/serve/watcher.rs | 60 +++++++++++++-------------- packages/manganis-core/src/asset.rs | 3 +- 16 files changed, 127 insertions(+), 99 deletions(-) diff --git a/packages/cli/src/assets.rs b/packages/cli/src/assets.rs index 490dcc8912..9cc242af32 100644 --- a/packages/cli/src/assets.rs +++ b/packages/cli/src/assets.rs @@ -18,8 +18,6 @@ use walkdir::WalkDir; mod file; mod manifest; -// pub use file::process_file; -// pub use folder::process_folder; pub use manifest::*; pub(crate) fn copy_dir_to( @@ -89,6 +87,7 @@ fn compressed_path(path: &Path) -> Option { } None => OsString::from("br"), }; + Some(path.with_extension(new_extension)) } diff --git a/packages/cli/src/assets/manifest.rs b/packages/cli/src/assets/manifest.rs index 6e256d0c21..52bd479278 100644 --- a/packages/cli/src/assets/manifest.rs +++ b/packages/cli/src/assets/manifest.rs @@ -164,7 +164,15 @@ impl AssetManifest { None } +} + +#[derive(Clone)] +pub struct OptimizeOptions { + pub precompress: bool, + pub enabled: bool, +} +impl AssetManifest { /// Copy the assest from this manifest to a target folder /// /// If `optimize` is enabled, then we will run the optimizer for this asset. @@ -172,7 +180,12 @@ impl AssetManifest { /// The output file is guaranteed to be the destination + the ResourceAsset bundle name /// /// Will not actually copy the asset if the source asset hasn't changed? - pub fn copy_asset_to(&self, destination: PathBuf, target_asset: PathBuf, optimize: bool) { + pub fn copy_asset_to( + &self, + destination: PathBuf, + target_asset: PathBuf, + optimize: &OptimizeOptions, + ) { let src = self.assets.get(&target_asset).unwrap(); let local = src.absolute.clone(); @@ -182,11 +195,11 @@ impl AssetManifest { } // If there's no optimizaton while copying this asset, we simply std::fs::copy and call it a day - if !optimize { + if !optimize.enabled { std::fs::copy(local, destination.join(&src.bundled)).expect("Failed to copy asset"); return; } - // Otherwise, let's attempt to optimize the thing + // Otherwise, let's attempt to optimize the the asset we're copying } } diff --git a/packages/cli/src/builder/assets.rs b/packages/cli/src/builder/assets.rs index 758c2b91ff..6262dbe599 100644 --- a/packages/cli/src/builder/assets.rs +++ b/packages/cli/src/builder/assets.rs @@ -1,9 +1,12 @@ use super::BuildRequest; use super::TargetPlatform; -use crate::builder::{progress::UpdateBuildProgress, BuildMessage, MessageType}; use crate::builder::{progress::UpdateStage, MessageSource}; use crate::config::Platform; use crate::Result; +use crate::{ + assets::OptimizeOptions, + builder::{progress::UpdateBuildProgress, BuildMessage, MessageType}, +}; use crate::{ assets::{copy_dir_to, AssetManifest}, link::LINK_OUTPUT_ENV_VAR, @@ -54,14 +57,17 @@ impl BuildRequest { // `dx` to act as a linker // // Pass in the tmp_file as the env var itself + // + // NOTE: that -Csave-temps=y is needed to prevent rustc from deleting the incremental cache... + // This might not be a "stable" way of keeping artifacts around, but it's in stable rustc tokio::process::Command::new("cargo") - .env(LINK_OUTPUT_ENV_VAR, tmp_file.path()) .arg("rustc") .args(cargo_args) - .arg("--offline") + .arg("--offline") /* don't use the network, should already be resolved */ .arg("--") - .arg(format!("-Clinker={}", current_exe().unwrap().display())) - .arg("-Csave-temps=y") + .arg(format!("-Clinker={}", current_exe().unwrap().display())) /* pass ourselves in */ + .env(LINK_OUTPUT_ENV_VAR, tmp_file.path()) /* but with the env var pointing to the temp file */ + .arg("-Csave-temps=y") /* don't delete the incremental cache */ .stdout(Stdio::piped()) .stderr(Stdio::piped()) .output() @@ -96,14 +102,17 @@ impl BuildRequest { let manifest = &self.assets; let platform = self.target_platform; + let options = OptimizeOptions { + precompress: self.should_precompress_assets(), + enabled: false, + }; + assets .par_iter() .enumerate() .try_for_each(|(_idx, asset)| { - let mut progress = self.progress.clone(); - // Update the progress - _ = progress.start_send(UpdateBuildProgress { + _ = self.progress.unbounded_send(UpdateBuildProgress { stage: Stage::OptimizingAssets, update: UpdateStage::AddMessage(BuildMessage { level: Level::INFO, @@ -116,22 +125,24 @@ impl BuildRequest { platform, }); - manifest.copy_asset_to(static_asset_output_dir.clone(), asset.to_path_buf(), false); + // Copy the asset into the bundled d + manifest.copy_asset_to( + static_asset_output_dir.clone(), + asset.to_path_buf(), + &options, + ); let finished = assets_finished.fetch_add(1, std::sync::atomic::Ordering::SeqCst); - _ = progress.start_send(UpdateBuildProgress { + _ = self.progress.unbounded_send(UpdateBuildProgress { stage: Stage::OptimizingAssets, update: UpdateStage::SetProgress(finished as f64 / asset_count as f64), platform, }); - // idx, &assets_finished Ok(()) as anyhow::Result<()> })?; - if self.should_precompress_assets() {} - Ok(()) } diff --git a/packages/cli/src/builder/cargo.rs b/packages/cli/src/builder/cargo.rs index be1a9ef60d..c4f7851034 100644 --- a/packages/cli/src/builder/cargo.rs +++ b/packages/cli/src/builder/cargo.rs @@ -1,9 +1,6 @@ use super::BuildRequest; use super::TargetPlatform; -use crate::builder::progress::CargoBuildResult; -use crate::builder::progress::Stage; -use crate::builder::progress::UpdateBuildProgress; -use crate::builder::progress::UpdateStage; +use crate::builder::progress::*; use crate::config::Platform; use crate::Result; use anyhow::Context; @@ -119,7 +116,7 @@ impl BuildRequest { tracing::info!("🚩 Build completed: [{}]", self.krate.out_dir().display()); - _ = self.progress.start_send(UpdateBuildProgress { + _ = self.progress.unbounded_send(UpdateBuildProgress { platform: self.target_platform, stage: Stage::Finished, update: UpdateStage::Start, @@ -133,7 +130,7 @@ impl BuildRequest { cargo_args: Vec, cargo_build_result: &CargoBuildResult, ) -> Result<()> { - _ = self.progress.start_send(UpdateBuildProgress { + _ = self.progress.unbounded_send(UpdateBuildProgress { stage: Stage::OptimizingAssets, update: UpdateStage::Start, platform: self.target_platform, @@ -181,13 +178,15 @@ impl BuildRequest { /// Get the output directory for a specific built target pub fn target_out_dir(&self) -> PathBuf { let out_dir = self.krate.out_dir(); - match self.build_arguments.platform { - Some(Platform::Fullstack) => match self.target_platform { + + if let Some(Platform::Fullstack) = self.build_arguments.platform { + match self.target_platform { TargetPlatform::Web => out_dir.join("public"), TargetPlatform::Desktop => out_dir.join("desktop"), _ => out_dir, - }, - _ => out_dir, + } + } else { + out_dir } } } diff --git a/packages/cli/src/builder/fullstack.rs b/packages/cli/src/builder/fullstack.rs index e0adf8a2c1..b700205643 100644 --- a/packages/cli/src/builder/fullstack.rs +++ b/packages/cli/src/builder/fullstack.rs @@ -1,11 +1,9 @@ -use futures_channel::mpsc::UnboundedSender; -use toml_edit::Item; - use crate::builder::Build; -use crate::dioxus_crate::DioxusCrate; - use crate::builder::BuildRequest; +use crate::dioxus_crate::DioxusCrate; +use futures_channel::mpsc::UnboundedSender; use std::io::Write; +use toml_edit::Item; use super::{BuildReason, TargetPlatform, UpdateBuildProgress}; diff --git a/packages/cli/src/builder/progress.rs b/packages/cli/src/builder/progress.rs index 9c9acb4f97..5dd076c3ff 100644 --- a/packages/cli/src/builder/progress.rs +++ b/packages/cli/src/builder/progress.rs @@ -149,7 +149,7 @@ impl BuildRequest { crate_count: usize, mut cmd: tokio::process::Command, ) -> anyhow::Result { - _ = self.progress.start_send(UpdateBuildProgress { + _ = self.progress.unbounded_send(UpdateBuildProgress { stage: Stage::Compiling, update: UpdateStage::Start, platform: self.target_platform, @@ -190,7 +190,7 @@ impl BuildRequest { match message { Message::CompilerMessage(msg) => { let message = msg.message; - _ = self.progress.start_send(UpdateBuildProgress { + _ = self.progress.unbounded_send(UpdateBuildProgress { stage: Stage::Compiling, update: UpdateStage::AddMessage(message.clone().into()), platform: self.target_platform, @@ -223,7 +223,7 @@ impl BuildRequest { output_location = Some(executable.into()); } else { let build_progress = units_compiled as f64 / crate_count as f64; - _ = self.progress.start_send(UpdateBuildProgress { + _ = self.progress.unbounded_send(UpdateBuildProgress { platform: self.target_platform, stage: Stage::Compiling, update: UpdateStage::SetProgress((build_progress).clamp(0.0, 1.00)), @@ -239,7 +239,7 @@ impl BuildRequest { } } Message::TextLine(line) => { - _ = self.progress.start_send(UpdateBuildProgress { + _ = self.progress.unbounded_send(UpdateBuildProgress { platform: self.target_platform, stage: Stage::Compiling, update: UpdateStage::AddMessage(BuildMessage { diff --git a/packages/cli/src/builder/web.rs b/packages/cli/src/builder/web.rs index 34039956ad..a153beb0de 100644 --- a/packages/cli/src/builder/web.rs +++ b/packages/cli/src/builder/web.rs @@ -54,7 +54,7 @@ impl BuildRequest { /// Post process the WASM build artifacts pub(crate) async fn post_process_web_build(&mut self) -> Result<()> { - _ = self.progress.start_send(UpdateBuildProgress { + _ = self.progress.unbounded_send(UpdateBuildProgress { stage: Stage::OptimizingWasm, update: UpdateStage::Start, platform: self.target_platform, @@ -104,7 +104,7 @@ impl BuildRequest { if let Ok(wasm_check_command) = Command::new("rustup").args(["show"]).output().await { let wasm_check_output = String::from_utf8(wasm_check_command.stdout).unwrap(); if !wasm_check_output.contains("wasm32-unknown-unknown") { - _ = self.progress.start_send(UpdateBuildProgress { + _ = self.progress.unbounded_send(UpdateBuildProgress { stage: Stage::InstallingWasmTooling, update: UpdateStage::Start, platform: self.target_platform, diff --git a/packages/cli/src/cli/link.rs b/packages/cli/src/cli/link.rs index d6d129da46..0de54a3379 100644 --- a/packages/cli/src/cli/link.rs +++ b/packages/cli/src/cli/link.rs @@ -24,6 +24,9 @@ pub struct InterceptedArgs { /// it both for determining if we should act as a linker and the for the file name itself. /// /// This will panic if it fails +/// +/// hmmmmmmmm tbh I'd rather just pass the object files back and do the parsing here, but the interface +/// is nicer to just bounce back the args and let the host do the parsing/canonicalization pub fn dump_link_args() -> anyhow::Result<()> { let output = std::env::var(LINK_OUTPUT_ENV_VAR).expect("Missing env var with target file"); diff --git a/packages/cli/src/cli/serve.rs b/packages/cli/src/cli/serve.rs index 1475224b17..4710f6032a 100644 --- a/packages/cli/src/cli/serve.rs +++ b/packages/cli/src/cli/serve.rs @@ -1,9 +1,6 @@ use crate::config::AddressArguments; -use crate::{ - settings::{self}, - tracer::CLILogControl, - DioxusCrate, -}; +use crate::settings; +use crate::DioxusCrate; use anyhow::Context; use build::Build; use std::ops::Deref; @@ -106,13 +103,13 @@ impl Serve { Ok(()) } - pub async fn serve(mut self, log_control: CLILogControl) -> anyhow::Result<()> { + pub async fn serve(mut self) -> anyhow::Result<()> { let mut dioxus_crate = DioxusCrate::new(&self.build_arguments.target_args) .context("Failed to load Dioxus workspace")?; self.resolve(&mut dioxus_crate)?; - crate::serve::serve_all(self, dioxus_crate, log_control).await?; + crate::serve::serve_all(self, dioxus_crate).await?; Ok(()) } diff --git a/packages/cli/src/dioxus_crate.rs b/packages/cli/src/dioxus_crate.rs index 57eda6d42d..6a1d713984 100644 --- a/packages/cli/src/dioxus_crate.rs +++ b/packages/cli/src/dioxus_crate.rs @@ -254,7 +254,8 @@ impl DioxusCrate { feature.into_iter().collect() } - /// Check if assets should be pre_compressed. This will only be true in release mode if the user has enabled pre_compress in the web config. + /// Check if assets should be pre_compressed. This will only be true in release mode if the user + /// has enabled pre_compress in the web config. pub fn should_pre_compress_web_assets(&self, release: bool) -> bool { self.dioxus_config.web.pre_compress && release } diff --git a/packages/cli/src/main.rs b/packages/cli/src/main.rs index f0327c344e..e501371c0a 100644 --- a/packages/cli/src/main.rs +++ b/packages/cli/src/main.rs @@ -34,7 +34,6 @@ async fn main() -> anyhow::Result<()> { } let args = Cli::parse(); - let log_control = tracer::build_tracing(); match args.action { Translate(opts) => opts @@ -72,7 +71,7 @@ async fn main() -> anyhow::Result<()> { .context(error_wrapper("Cleaning project failed")), Serve(opts) => opts - .serve(log_control) + .serve() .await .context(error_wrapper("Serving project failed")), diff --git a/packages/cli/src/serve/builder.rs b/packages/cli/src/serve/builder.rs index 34ef9ddc57..c46bd1398d 100644 --- a/packages/cli/src/serve/builder.rs +++ b/packages/cli/src/serve/builder.rs @@ -36,7 +36,7 @@ pub struct Builder { impl Builder { /// Create a new builder and immediately start a build - pub fn start(config: &DioxusCrate, serve: &Serve) -> Result { + pub fn start(serve: &Serve, config: &DioxusCrate) -> Result { let (tx, rx) = futures_channel::mpsc::unbounded(); let mut builder = Self { @@ -73,7 +73,7 @@ impl Builder { let platform = build_request.target_platform.clone(); let res = build_request.build().await; if let Err(err) = &res { - let _ = tx.start_send(UpdateBuildProgress { + let _ = tx.unbounded_send(UpdateBuildProgress { stage: crate::builder::Stage::Finished, update: crate::builder::UpdateStage::Failed(format!("{err}")), platform, diff --git a/packages/cli/src/serve/mod.rs b/packages/cli/src/serve/mod.rs index e0bfcc4069..5d65ca19bf 100644 --- a/packages/cli/src/serve/mod.rs +++ b/packages/cli/src/serve/mod.rs @@ -26,13 +26,17 @@ use watcher::*; /// This includes web, desktop, mobile, fullstack, etc. /// /// Platform specifics: -/// - Web: we need to attach a filesystem server to our devtools webserver to serve the project. We -/// want to emulate GithubPages here since most folks are deploying there and expect things like -/// basepath to match. -/// - Fullstack: We spin up the same dev server but in this case the fullstack server itself needs to -/// proxy all dev requests to our dev server -/// - Desktop: We spin up the dev server but without a filesystem server. -/// - Mobile: Basically the same as desktop. +/// ------------------- +/// - Web: we need to attach a filesystem server to our devtools webserver to serve the project. We +/// want to emulate GithubPages here since most folks are deploying there and expect things like +/// basepath to match. +/// - Fullstack: We spin up the same dev server but in this case the fullstack server itself needs to +/// proxy all dev requests to our dev server. Todo: we might just want "web" to use the +/// fullstack server by default, such that serving web on non-wasm platforms is just a +/// serving a proper server. +/// - Desktop: We spin up the dev server but without a filesystem server. +/// - Mobile: Basically the same as desktop. +/// /// /// Notes: /// - All filesystem changes are tracked here @@ -45,17 +49,17 @@ use watcher::*; /// - Handle logs from the build engine separately? /// - I want us to be able to detect a `server_fn` in the project and then upgrade from a static server /// to a dynamic one on the fly. -pub async fn serve_all( - serve: Serve, - krate: DioxusCrate, - log_control: crate::tracer::CLILogControl, -) -> Result<()> { +pub async fn serve_all(serve: Serve, krate: DioxusCrate) -> Result<()> { // Start each component of the devserver. - // Starting the builder will queue up a build - let mut builder = Builder::start(&krate, &serve)?; + // Start the screen first, since it'll start to swallow the tracing logs from the rest of the the cli + let mut screen = Output::start(&serve).expect("Failed to open terminal logger"); + + // Note that starting the builder will queue up a build immediately + let mut builder = Builder::start(&serve, &krate)?; + + // The watcher and devserver are started after but don't really matter in order let mut server = DevServer::start(&serve, &krate); let mut watcher = Watcher::start(&serve, &krate); - let mut screen = Output::start(&serve, log_control).expect("Failed to open terminal logger"); loop { // Make sure we don't hog the CPU: these loop { select! {} } blocks can starve the executor if we're not careful diff --git a/packages/cli/src/serve/output.rs b/packages/cli/src/serve/output.rs index 640be51f99..c479b93e2e 100644 --- a/packages/cli/src/serve/output.rs +++ b/packages/cli/src/serve/output.rs @@ -115,7 +115,12 @@ enum Tab { type TerminalBackend = Terminal>; impl Output { - pub fn start(cfg: &Serve, log_control: CLILogControl) -> io::Result { + pub fn start(cfg: &Serve) -> io::Result { + // Start a tracing instance just for serving. + // This ensures that any tracing we do while serving doesn't break the TUI itself, and instead is + // redirected to the serve process. + let log_control = crate::tracer::build_tracing(); + let interactive = std::io::stdout().is_tty() && cfg.interactive.unwrap_or(true); let mut events = None; diff --git a/packages/cli/src/serve/watcher.rs b/packages/cli/src/serve/watcher.rs index 3ce323b229..55d96f1eda 100644 --- a/packages/cli/src/serve/watcher.rs +++ b/packages/cli/src/serve/watcher.rs @@ -12,21 +12,21 @@ use notify::{ Config, EventKind, }; use std::collections::{HashMap, HashSet}; -use std::{fs, path::PathBuf, time::Duration}; +use std::{path::PathBuf, time::Duration}; /// This struct stores the file watcher and the filemap for the project. /// /// This is where we do workspace discovery and recursively listen for changes in Rust files and asset /// directories. pub struct Watcher { - _tx: UnboundedSender, rx: UnboundedReceiver, - _last_update_time: i64, - watcher: Box, queued_events: Vec, file_map: FileMap, ignore: Gitignore, applied_hot_reload_message: Option, + _tx: UnboundedSender, + _last_update_time: i64, + _watcher: Box, } impl Watcher { @@ -129,7 +129,7 @@ impl Watcher { Self { _tx: tx, rx, - watcher, + _watcher: watcher, file_map, ignore, queued_events: Vec::new(), @@ -292,33 +292,31 @@ impl Watcher { /// Store the hot reload changes for any future clients that connect fn add_hot_reload_message(&mut self, msg: &HotReloadMsg) { - match &mut self.applied_hot_reload_message { - Some(applied) => { - // Merge the assets, unknown files, and templates - // We keep the newer change if there is both a old and new change - let mut templates: HashMap = std::mem::take(&mut applied.templates) - .into_iter() - .map(|template| (template.location.clone(), template)) - .collect(); - let mut assets: HashSet = - std::mem::take(&mut applied.assets).into_iter().collect(); - let mut unknown_files: HashSet = - std::mem::take(&mut applied.unknown_files) - .into_iter() - .collect(); - for template in &msg.templates { - templates.insert(template.location.clone(), template.clone()); - } - assets.extend(msg.assets.iter().cloned()); - unknown_files.extend(msg.unknown_files.iter().cloned()); - applied.templates = templates.into_values().collect(); - applied.assets = assets.into_iter().collect(); - applied.unknown_files = unknown_files.into_iter().collect(); - } - None => { - self.applied_hot_reload_message = Some(msg.clone()); - } + let Some(applied) = &mut self.applied_hot_reload_message else { + self.applied_hot_reload_message = Some(msg.clone()); + return; + }; + + // Merge the assets, unknown files, and templates + // We keep the newer change if there is both a old and new change + let mut templates: HashMap = std::mem::take(&mut applied.templates) + .into_iter() + .map(|template| (template.location.clone(), template)) + .collect(); + let mut assets: HashSet = + std::mem::take(&mut applied.assets).into_iter().collect(); + let mut unknown_files: HashSet = std::mem::take(&mut applied.unknown_files) + .into_iter() + .collect(); + for template in &msg.templates { + templates.insert(template.location.clone(), template.clone()); } + + assets.extend(msg.assets.iter().cloned()); + unknown_files.extend(msg.unknown_files.iter().cloned()); + applied.templates = templates.into_values().collect(); + applied.assets = assets.into_iter().collect(); + applied.unknown_files = unknown_files.into_iter().collect(); } /// Ensure the changes we've received from the queue are actually legit changes to either assets or diff --git a/packages/manganis-core/src/asset.rs b/packages/manganis-core/src/asset.rs index bd5461af85..afdcde19d6 100644 --- a/packages/manganis-core/src/asset.rs +++ b/packages/manganis-core/src/asset.rs @@ -32,6 +32,7 @@ const HASH_SIZE: usize = 16; #[derive(Debug)] pub struct AssetError {} + impl ResourceAsset { pub fn parse_any(raw: &str) -> Result { // get the location where the asset is absolute, relative to @@ -62,7 +63,7 @@ impl ResourceAsset { }) } - pub fn make_unique_name(file_path: PathBuf) -> String { + fn make_unique_name(file_path: PathBuf) -> String { // Create a hasher let mut hash = std::collections::hash_map::DefaultHasher::new(); From 629aaf38fc8b3250d5b0b40ca1f3ecaa8667d145 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Mon, 2 Sep 2024 08:42:24 -0700 Subject: [PATCH 058/139] wip --- Cargo.lock | 3 + packages/cli/Cargo.toml | 1 + packages/cli/src/builder/assets.rs | 22 ++--- packages/cli/src/builder/cargo.rs | 11 +-- packages/cli/src/builder/mod.rs | 58 ++++++------ packages/cli/src/builder/progress.rs | 34 +++---- packages/cli/src/builder/web.rs | 2 +- packages/cli/src/serve/builder.rs | 129 +++++++++++---------------- packages/cli/src/serve/update.rs | 1 + 9 files changed, 114 insertions(+), 147 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 71cb83c06b..daf9646b5c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2429,6 +2429,7 @@ dependencies = [ "thiserror", "tokio", "tokio-stream", + "tokio-util", "toml", "toml_edit 0.22.20", "tower", @@ -9748,10 +9749,12 @@ checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" dependencies = [ "bytes", "futures-core", + "futures-io", "futures-sink", "futures-util", "hashbrown 0.14.5", "pin-project-lite", + "slab", "tokio", ] diff --git a/packages/cli/Cargo.toml b/packages/cli/Cargo.toml index fe93d09eb0..171a351908 100644 --- a/packages/cli/Cargo.toml +++ b/packages/cli/Cargo.toml @@ -140,6 +140,7 @@ tempfile = "3.3" # Extracting data from an executable object = {version="0.36.0", features=["wasm"]} +tokio-util = { version = "0.7.11", features = ["full"] } # [dev-dependencies] # tracing-subscriber = "0.3.18" diff --git a/packages/cli/src/builder/assets.rs b/packages/cli/src/builder/assets.rs index 6262dbe599..8a716a36aa 100644 --- a/packages/cli/src/builder/assets.rs +++ b/packages/cli/src/builder/assets.rs @@ -99,12 +99,13 @@ impl BuildRequest { let assets_finished = AtomicUsize::new(0); let asset_count = assets.len(); - let manifest = &self.assets; - let platform = self.target_platform; let options = OptimizeOptions { - precompress: self.should_precompress_assets(), enabled: false, + precompress: self.targeting_web() + && self + .krate + .should_pre_compress_web_assets(self.build_arguments.release), }; assets @@ -122,11 +123,11 @@ impl BuildRequest { )), source: MessageSource::Build, }), - platform, + platform: self.target_platform, }); - // Copy the asset into the bundled d - manifest.copy_asset_to( + // Copy the asset into the bundle directory + self.assets.copy_asset_to( static_asset_output_dir.clone(), asset.to_path_buf(), &options, @@ -137,7 +138,7 @@ impl BuildRequest { _ = self.progress.unbounded_send(UpdateBuildProgress { stage: Stage::OptimizingAssets, update: UpdateStage::SetProgress(finished as f64 / asset_count as f64), - platform, + platform: self.target_platform, }); Ok(()) as anyhow::Result<()> @@ -145,11 +146,4 @@ impl BuildRequest { Ok(()) } - - fn should_precompress_assets(&self) -> bool { - self.targeting_web() - && self - .krate - .should_pre_compress_web_assets(self.build_arguments.release) - } } diff --git a/packages/cli/src/builder/cargo.rs b/packages/cli/src/builder/cargo.rs index c4f7851034..99d3bea7ba 100644 --- a/packages/cli/src/builder/cargo.rs +++ b/packages/cli/src/builder/cargo.rs @@ -85,16 +85,9 @@ impl BuildRequest { Ok((cmd, cargo_args)) } - pub(crate) async fn build(mut self) -> Result { + pub(crate) async fn build(&mut self) -> Result<()> { tracing::info!("🚅 Running build [Desktop] command..."); - // Set up runtime guards - let mut dioxus_version = crate::dx_build_info::PKG_VERSION.to_string(); - if let Some(hash) = crate::dx_build_info::GIT_COMMIT_HASH_SHORT { - let hash = &hash.trim_start_matches('g')[..4]; - dioxus_version.push_str(&format!("-{hash}")); - } - // If this is a web, build make sure we have the web build tooling set up if self.targeting_web() { self.install_web_build_tooling().await?; @@ -122,7 +115,7 @@ impl BuildRequest { update: UpdateStage::Start, }); - Ok(self) + Ok(()) } async fn post_process_build( diff --git a/packages/cli/src/builder/mod.rs b/packages/cli/src/builder/mod.rs index 44d16acdba..9a28b09bd8 100644 --- a/packages/cli/src/builder/mod.rs +++ b/packages/cli/src/builder/mod.rs @@ -85,6 +85,7 @@ impl BuildRequest { progress.clone(), platform, ); + Ok(vec![req]) }; @@ -93,7 +94,6 @@ impl BuildRequest { Platform::Web => single_platform(TargetPlatform::Web), Platform::Desktop => single_platform(TargetPlatform::Desktop), Platform::Mobile => single_platform(TargetPlatform::Mobile), - Platform::Fullstack => { Self::new_fullstack(dioxus_crate.clone(), build_arguments, serve, progress) } @@ -105,32 +105,34 @@ impl BuildRequest { mut rx: UnboundedReceiver, ) -> Result> { let multi_platform_build = build_requests.len() > 1; - let mut set = tokio::task::JoinSet::new(); - - for build_request in build_requests { - set.spawn(async move { build_request.build().await }); - } - - // Watch the build progress as it comes in - while let Some(update) = rx.next().await { - if multi_platform_build { - let platform = update.platform; - print!("{platform} build: "); - update.to_std_out(); - } else { - update.to_std_out(); - } - } - - let mut all_results = Vec::new(); - - while let Some(result) = set.join_next().await { - all_results.push( - result - .map_err(|_| crate::Error::Unique("Failed to build project".to_owned()))??, - ); - } - - Ok(all_results) + // let mut set = tokio::task::JoinSet::new(); + + // for build_request in build_requests { + // set.spawn(async move { build_request.build().await }); + // } + + // // Watch the build progress as it comes in + // while let Some(update) = rx.next().await { + // if multi_platform_build { + // let platform = update.platform; + // print!("{platform} build: "); + // update.to_std_out(); + // } else { + // update.to_std_out(); + // } + // } + + todo!() + + // let mut all_results = Vec::new(); + + // while let Some(result) = set.join_next().await { + // all_results.push( + // result + // .map_err(|_| crate::Error::Unique("Failed to build project".to_owned()))??, + // ); + // } + + // Ok(all_results) } } diff --git a/packages/cli/src/builder/progress.rs b/packages/cli/src/builder/progress.rs index 5dd076c3ff..6c6e6fd786 100644 --- a/packages/cli/src/builder/progress.rs +++ b/packages/cli/src/builder/progress.rs @@ -101,10 +101,12 @@ pub enum MessageType { pub enum MessageSource { /// Represents any message from the running application. Renders `[app]` App, + /// Represents any generic message from the CLI. Renders `[dev]` /// /// Usage of Tracing inside of the CLI will be routed to this type. Dev, + /// Represents a message from the build process. Renders `[bld]` /// /// This is anything emitted from a build process such as cargo and optimizations. @@ -145,7 +147,7 @@ pub(crate) struct CargoBuildResult { impl BuildRequest { pub(crate) async fn build_cargo( - &mut self, + &self, crate_count: usize, mut cmd: tokio::process::Command, ) -> anyhow::Result { @@ -160,6 +162,7 @@ impl BuildRequest { .stderr(Stdio::piped()) .spawn() .context("Failed to spawn cargo build")?; + let stdout = child.stdout.take().unwrap(); let stderr = child.stderr.take().unwrap(); let stdout = tokio::io::BufReader::new(stdout); @@ -170,23 +173,23 @@ impl BuildRequest { let mut stderr = stderr.lines(); let mut units_compiled = 0; let mut errors = Vec::new(); + loop { let line = tokio::select! { - line = stdout.next_line() => { - line - } - line = stderr.next_line() => { - line - } + line = stdout.next_line() => line, + line = stderr.next_line() => line, }; + let Some(line) = line? else { break; }; + let mut deserializer = serde_json::Deserializer::from_str(line.trim()); deserializer.disable_recursion_limit(); let message = Message::deserialize(&mut deserializer).unwrap_or(Message::TextLine(line)); + match message { Message::CompilerMessage(msg) => { let message = msg.message; @@ -265,20 +268,19 @@ impl BuildRequest { units: Vec, } - let mut cmd = tokio::process::Command::new("cargo"); - cmd.arg("+nightly"); - cmd.arg("build"); - cmd.arg("--unit-graph"); - cmd.arg("-Z").arg("unstable-options"); - - cmd.args(self.build_arguments()); - - let output = cmd + let output = tokio::process::Command::new("cargo") + .arg("+nightly") + .arg("build") + .arg("--unit-graph") + .arg("-Z") + .arg("unstable-options") + .args(self.build_arguments()) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .output() .await .ok()?; + if !output.status.success() { return None; } diff --git a/packages/cli/src/builder/web.rs b/packages/cli/src/builder/web.rs index a153beb0de..c213f9a979 100644 --- a/packages/cli/src/builder/web.rs +++ b/packages/cli/src/builder/web.rs @@ -97,7 +97,7 @@ impl BuildRequest { } /// Check if the wasm32-unknown-unknown target is installed and try to install it if not - pub(crate) async fn install_web_build_tooling(&mut self) -> Result<()> { + pub(crate) async fn install_web_build_tooling(&self) -> Result<()> { // If the user has rustup, we can check if the wasm32-unknown-unknown target is installed // Otherwise we can just assume it is installed - which is not great... // Eventually we can poke at the errors and let the user know they need to install the target diff --git a/packages/cli/src/serve/builder.rs b/packages/cli/src/serve/builder.rs index c46bd1398d..991641dee3 100644 --- a/packages/cli/src/serve/builder.rs +++ b/packages/cli/src/serve/builder.rs @@ -11,15 +11,18 @@ use futures_util::StreamExt; use std::{collections::HashMap, process::Stdio}; use tokio::{ process::{Child, Command}, - task::JoinHandle, + task::{JoinHandle, JoinSet}, }; +use tokio_util::task::TaskTracker; use super::update::ServeUpdate; /// A handle to ongoing builds and then the spawned tasks themselves pub struct Builder { - /// The results of the build - ongoing: Option>>>, + /// Ongoing apps running in place + /// + /// They might be actively being being, running, or have exited. + pub running: HashMap, tx: UnboundedSender, rx: UnboundedReceiver, @@ -29,9 +32,6 @@ pub struct Builder { /// The arguments for the build serve: Serve, - - /// The children of the build process - pub finished: HashMap, } impl Builder { @@ -42,10 +42,10 @@ impl Builder { let mut builder = Self { tx, rx, - ongoing: None, + building: Default::default(), config: config.clone(), serve: serve.clone(), - finished: Default::default(), + running: Default::default(), }; builder.build()?; @@ -64,38 +64,23 @@ impl Builder { self.tx.clone(), )?; - let mut set = tokio::task::JoinSet::new(); - for build_request in build_requests { - let mut tx = self.tx.clone(); - - set.spawn(async move { - let platform = build_request.target_platform.clone(); - let res = build_request.build().await; - if let Err(err) = &res { - let _ = tx.unbounded_send(UpdateBuildProgress { - stage: crate::builder::Stage::Finished, - update: crate::builder::UpdateStage::Failed(format!("{err}")), - platform, + // Queue the build + let platform = build_request.target_platform.clone(); + self.building.spawn(async move { + // Run the build, but in a protected spawn, ensuring we can't produce panics and thus, joinerrors + let res = tokio::spawn(build_request.build()) + .await + .unwrap_or_else(|err| { + Err(crate::Error::Unique(format!( + "Panic while building project: {err:?}" + ))) }); - } - res + (platform, build_request) }); } - self.ongoing = Some(tokio::spawn(async move { - let mut all_results = Vec::new(); - while let Some(result) = set.join_next().await { - let res = result.map_err(|err| { - crate::Error::Unique(format!("Panic while building project: {err:?}")) - })??; - - all_results.push(res); - } - Ok(all_results) - })); - Ok(()) } @@ -103,54 +88,40 @@ impl Builder { /// /// Also listen for any input from the app's handle pub async fn wait(&mut self) -> ServeUpdate { - // Wait for build progress - let next = next_or_pending(self.rx.next()); - - // The ongoing builds directly - let results: OptionFuture<_> = self.ongoing.as_mut().into(); - let results = next_or_pending(results); - - todo!("wait for builds to be finished") - - // // The process exits - // let children_empty = self.children.is_empty(); - // let process_exited = self - // .children - // .iter_mut() - // .map(|(target, child)| Box::pin(async move { (*target, child.wait().await) })); - - // let process_exited = async move { - // match children_empty { - // true => return futures_util::future::pending().await, - // false => futures_util::future::select_all(process_exited).await, - // } - // }; - - // // Wait for the next build result - // tokio::select! { - // build_results = results => { - // self.ongoing = None; - - // // If we have a build result, bubble it up to the main loop - // match build_results { - // Ok(Ok(build_results)) => ServeUpdate::BuildReady { }, - // Err(_ee) => ServeUpdate::BuildFailed { err: crate::Error::BuildFailed("Build join failed".to_string()) }, - // Ok(Err(ee)) => ServeUpdate::BuildFailed { err: ee.into() }, - // } - // } - // update = next => { - // // If we have a build progress, send it to the screen - // ServeUpdate::Progress { update } - // } - // ((target, exit_status), _, _) = process_exited => { - // ServeUpdate::ProcessExited { status: exit_status, target_platform: target } - // } - // } + // Exits and stdout/stderr + let processes = self.running.iter_mut().filter_map(|(target, request)| { + let Some(child) = request.child else { + return None; + }; + + Some(Box::pin(async move { + // + (*target, child.wait().await) + })) + }); + + // Wait for the next build result + tokio::select! { + Some(update) = self.rx.next() => { + ServeUpdate::Progress { update } + } + + Some(Ok((target, build_result))) = self.building.join_next() => { + match build_result { + Ok(build_result) => ServeUpdate::BuildReady { target, request: build_result }, + Err(err) => ServeUpdate::BuildFailed { err, target }, + } + } + + ((target, exit_status), _, _) = futures_util::future::select_all(processes) => { + ServeUpdate::ProcessExited { status: exit_status, target_platform: target } + } + } } /// Shutdown the current build process pub(crate) fn shutdown(&mut self) { - for (_target, app) in self.finished.drain() { + for (_target, app) in self.running.drain() { let Some(mut child) = app.child else { continue; }; @@ -183,7 +154,7 @@ impl Builder { _ = child.start_kill(); } - if let Some(tasks) = self.ongoing.take() { + if let Some(tasks) = self.building.take() { tasks.abort(); } } diff --git a/packages/cli/src/serve/update.rs b/packages/cli/src/serve/update.rs index db741a68ad..cd7d496972 100644 --- a/packages/cli/src/serve/update.rs +++ b/packages/cli/src/serve/update.rs @@ -15,6 +15,7 @@ pub enum ServeUpdate { BuildReady { target: TargetPlatform, + request: BuildRequest, }, BuildFailed { From 7648b83f891dda78a1b9773920e46198891e3a8d Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Mon, 2 Sep 2024 08:42:34 -0700 Subject: [PATCH 059/139] add comment --- packages/cli/src/serve/builder.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/cli/src/serve/builder.rs b/packages/cli/src/serve/builder.rs index 991641dee3..5a44aa4d44 100644 --- a/packages/cli/src/serve/builder.rs +++ b/packages/cli/src/serve/builder.rs @@ -22,6 +22,8 @@ pub struct Builder { /// Ongoing apps running in place /// /// They might be actively being being, running, or have exited. + /// + /// When a new full rebuild occurs, we will keep these requests here pub running: HashMap, tx: UnboundedSender, From 4b65c3617723a165ae6eb6895a313ec9867ba8f5 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 4 Sep 2024 15:37:24 -0700 Subject: [PATCH 060/139] new structure is much cleaner --- packages/cli/.vscode/settings.json | 6 - packages/cli/src/assets.rs | 1029 +++++++++++++++++++++- packages/cli/src/assets/file.rs | 820 ----------------- packages/cli/src/assets/manifest.rs | 205 ----- packages/cli/src/builder/assets.rs | 141 ++- packages/cli/src/builder/builder.rs | 119 +++ packages/cli/src/builder/bundle.rs | 14 +- packages/cli/src/builder/cargo.rs | 242 +++-- packages/cli/src/builder/fullstack.rs | 165 ---- packages/cli/src/builder/mod.rs | 144 +-- packages/cli/src/builder/platform.rs | 110 ++- packages/cli/src/builder/prepare_html.rs | 17 +- packages/cli/src/builder/profiles.rs | 57 ++ packages/cli/src/builder/progress.rs | 60 +- packages/cli/src/builder/request.rs | 171 ++++ packages/cli/src/builder/result.rs | 40 + packages/cli/src/builder/web.rs | 99 ++- packages/cli/src/bundler.rs | 8 + packages/cli/src/bundler/android.rs | 0 packages/cli/src/bundler/app.rs | 43 + packages/cli/src/bundler/deb.rs | 0 packages/cli/src/bundler/ios.rs | 0 packages/cli/src/bundler/mac.rs | 0 packages/cli/src/bundler/web.rs | 0 packages/cli/src/bundler/win.rs | 0 packages/cli/src/cli/build.rs | 70 +- packages/cli/src/cli/bundle.rs | 6 +- packages/cli/src/cli/mod.rs | 4 +- packages/cli/src/cli/serve.rs | 50 +- packages/cli/src/config/app.rs | 2 +- packages/cli/src/config/platform.rs | 91 -- packages/cli/src/dioxus_crate.rs | 244 +++-- packages/cli/src/main.rs | 1 + packages/cli/src/serve/builder.rs | 162 ---- packages/cli/src/serve/mod.rs | 87 +- packages/cli/src/serve/output.rs | 69 +- packages/cli/src/serve/runner.rs | 43 + packages/cli/src/serve/server.rs | 120 ++- packages/cli/src/serve/update.rs | 41 +- packages/cli/src/serve/util.rs | 4 +- packages/cli/src/serve/watcher.rs | 83 +- 41 files changed, 2276 insertions(+), 2291 deletions(-) delete mode 100644 packages/cli/.vscode/settings.json delete mode 100644 packages/cli/src/assets/file.rs delete mode 100644 packages/cli/src/assets/manifest.rs create mode 100644 packages/cli/src/builder/builder.rs delete mode 100644 packages/cli/src/builder/fullstack.rs create mode 100644 packages/cli/src/builder/profiles.rs create mode 100644 packages/cli/src/builder/request.rs create mode 100644 packages/cli/src/builder/result.rs create mode 100644 packages/cli/src/bundler.rs create mode 100644 packages/cli/src/bundler/android.rs create mode 100644 packages/cli/src/bundler/app.rs create mode 100644 packages/cli/src/bundler/deb.rs create mode 100644 packages/cli/src/bundler/ios.rs create mode 100644 packages/cli/src/bundler/mac.rs create mode 100644 packages/cli/src/bundler/web.rs create mode 100644 packages/cli/src/bundler/win.rs create mode 100644 packages/cli/src/serve/runner.rs diff --git a/packages/cli/.vscode/settings.json b/packages/cli/.vscode/settings.json deleted file mode 100644 index 40461adda1..0000000000 --- a/packages/cli/.vscode/settings.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "Lua.diagnostics.globals": [ - "plugin_logger", - "PLUGIN_DOWNLOADER" - ] -} diff --git a/packages/cli/src/assets.rs b/packages/cli/src/assets.rs index 9cc242af32..24611e5c52 100644 --- a/packages/cli/src/assets.rs +++ b/packages/cli/src/assets.rs @@ -5,20 +5,233 @@ use crate::Result; use anyhow::Context; use brotli::enc::BrotliEncoderParams; use futures_channel::mpsc::UnboundedSender; +use manganis_core::{LinkSection, ResourceAsset}; +use object::{read::archive::ArchiveFile, File as ObjectFile, Object, ObjectSection}; use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; +use std::ffi::OsString; use std::fs; use std::path::Path; use std::sync::atomic::AtomicUsize; use std::sync::Arc; -use std::{ffi::OsString, path::PathBuf}; +use std::{collections::HashMap, path::PathBuf}; use std::{fs::File, io::Write}; use tracing::Level; use walkdir::WalkDir; +// use manganis_common::{FileOptions, FolderAsset}; +// use image::{DynamicImage, EncodableLayout}; +// use lightningcss::stylesheet::{MinifyOptions, ParserOptions, PrinterOptions, StyleSheet}; +// use manganis_common::{ +// CssOptions, FileOptions, ImageOptions, ImageType, JsOptions, JsonOptions, ResourceAsset, +// }; -mod file; -mod manifest; +use crate::link::InterceptedArgs; -pub use manifest::*; +/// A manifest of all assets collected from dependencies +/// +/// This will be filled in primarly by incremental compilation artifacts. +#[derive(Debug, PartialEq, Default, Clone)] +pub struct AssetManifest { + /// Map of asset pathbuf to its + pub(crate) assets: HashMap, +} + +#[derive(Clone)] +pub struct OptimizeOptions { + pub precompress: bool, + pub enabled: bool, +} + +impl AssetManifest { + /// Creates a new asset manifest + pub fn new() -> Self { + Self { + ..Default::default() + } + } + + pub fn new_from_linker_intercept(args: InterceptedArgs) -> Self { + let mut _self = Self::new(); + _self.add_from_linker_intercept(args); + _self + } + + /// Fill this manifest from the intercepted rustc args used to link the app together + pub fn add_from_linker_intercept(&mut self, args: InterceptedArgs) { + // Attempt to load the arg as a command file, otherwise just use the args themselves + // This is because windows will pass in `@linkerargs.txt` as a source of linker args + if let Some(command) = args.args.iter().find(|arg| arg.starts_with('@')).cloned() { + self.add_from_command_file(args, &command); + } else { + self.add_from_linker_args(args); + } + } + + /// Fill this manifest from the contents of a linker command file. + /// + /// Rustc will pass a file as link args to linkers on windows instead of args directly. + /// + /// We actually need to read that file and then pull out the args directly. + pub fn add_from_command_file(&mut self, args: InterceptedArgs, arg: &str) { + let path = arg.trim().trim_start_matches('@'); + let file_binary = std::fs::read(path).unwrap(); + + // This may be a utf-16le file. Let's try utf-8 first. + let content = match String::from_utf8(file_binary.clone()) { + Ok(s) => s, + Err(_) => { + // Convert Vec to Vec to convert into a String + let binary_u16le: Vec = file_binary + .chunks_exact(2) + .map(|a| u16::from_le_bytes([a[0], a[1]])) + .collect(); + + String::from_utf16_lossy(&binary_u16le) + } + }; + + // Gather linker args + let mut linker_args = Vec::new(); + let lines = content.lines(); + for line in lines { + // Remove quotes from the line - windows link args files are quoted + let line_parsed = line.to_string(); + let line_parsed = line_parsed.trim_end_matches('"').to_string(); + let line_parsed = line_parsed.trim_start_matches('"').to_string(); + linker_args.push(line_parsed); + } + + self.add_from_linker_args(InterceptedArgs { + args: linker_args, + ..args + }); + } + + pub fn add_from_linker_args(&mut self, args: InterceptedArgs) { + // Parse through linker args for `.o` or `.rlib` files. + for item in args.args { + if item.ends_with(".o") || item.ends_with(".rlib") { + self.add_from_object_path(args.work_dir.join(PathBuf::from(item))); + } + } + } + + /// Fill this manifest with a file object/rlib files, typically extracted from the linker intercepted + pub fn add_from_object_path(&mut self, path: PathBuf) { + let Some(ext) = path.extension() else { + return; + }; + + let Some(ext) = ext.to_str() else { + return; + }; + + let data = std::fs::read(path.clone()).expect("Failed to read asset optimization file"); + + match ext { + // Parse an unarchived object file + "o" => { + let object = object::File::parse(&*data).unwrap(); + self.add_from_object_file(&object); + } + + // Parse an rlib as a collection of objects + "rlib" => { + let archive = object::read::archive::ArchiveFile::parse(&*data).unwrap(); + self.add_from_archive_file(&archive, &data); + } + _ => {} + } + } + + /// Fill this manifest from an rlib / ar file that contains many object files and their entryies + pub fn add_from_archive_file(&mut self, archive: &ArchiveFile, data: &[u8]) { + // Look through each archive member for object files. + // Read the archive member's binary data (we know it's an object file) + // And parse it with the normal `object::File::parse` to find the manganis string. + for member in archive.members() { + let member = member.unwrap(); + let name = String::from_utf8_lossy(member.name()).to_string(); + + // Check if the archive member is an object file and parse it. + if name.ends_with(".o") { + let data = member.data(&*data).unwrap(); + let object = object::File::parse(data).unwrap(); + self.add_from_object_file(&object); + } + } + } + + /// Fill this manifest with whatever tables might come from the object file + pub fn add_from_object_file(&mut self, obj: &ObjectFile) -> Option<()> { + for section in obj.sections() { + let Ok(section_name) = section.name() else { + continue; + }; + + // Check if the link section matches the asset section for one of the platforms we support. This may not be the current platform if the user is cross compiling + let matches = LinkSection::ALL + .iter() + .any(|x| x.link_section == section_name); + + if !matches { + continue; + } + + let bytes = section.uncompressed_data().ok()?; + + let as_str = std::str::from_utf8(&bytes) + .ok()? + .chars() + .filter(|c| !c.is_control()) + .collect::(); + + let stream = serde_json::Deserializer::from_str(&as_str).into_iter::(); + + for as_resource in stream { + let as_resource = as_resource.unwrap(); + + // Some platforms (e.g. macOS) start the manganis section with a null byte, we need to filter that out before we deserialize the JSON + self.assets + .insert(as_resource.absolute.clone(), as_resource); + } + } + + None + } + + /// Copy the assest from this manifest to a target folder + /// + /// If `optimize` is enabled, then we will run the optimizer for this asset. + /// + /// The output file is guaranteed to be the destination + the ResourceAsset bundle name + /// + /// Will not actually copy the asset if the source asset hasn't changed? + pub fn copy_asset_to( + &self, + destination: &Path, + target_asset: &Path, + optimize: &OptimizeOptions, + ) { + let src = self.assets.get(target_asset).unwrap(); + + let local = src.absolute.clone(); + + if !local.exists() { + panic!("Specified asset does not exist while trying to copy {target_asset:?} to {destination:?}") + } + + // If there's no optimizaton while copying this asset, we simply std::fs::copy and call it a day + if !optimize.enabled { + std::fs::copy(local, destination.join(&src.bundled)).expect("Failed to copy asset"); + return; + } + + // Otherwise, let's attempt to optimize the the asset we're copying + } + + /// Sync the assets with the folder - will update any assets that have changed since the last sync + pub fn sync_with_folder(&self, destination: &Path) {} +} pub(crate) fn copy_dir_to( src_dir: PathBuf, @@ -123,3 +336,811 @@ pub(crate) fn pre_compress_folder(path: &Path, pre_compress: bool) -> std::io::R } Ok(()) } + +// use swc::{config::JsMinifyOptions, try_with_handler, BoolOrDataConfig}; +// use swc_common::{sync::Lrc, FileName}; +// use swc_common::{SourceMap, GLOBALS}; + +// pub trait Process { +// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()>; +// } + +// /// Process a specific file asset +// pub fn process_file(file: &ResourceAsset, output_folder: &Path) -> anyhow::Result<()> { +// todo!() +// // let location = file.location(); +// // let source = location.source(); +// // let output_path = output_folder.join(location.unique_name()); +// // file.options().process(source, &output_path) +// } + +// impl Process for FileOptions { +// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { +// if output_path.exists() { +// return Ok(()); +// } +// match self { +// Self::Other { .. } => { +// let bytes = source.read_to_bytes()?; +// std::fs::write(output_path, bytes).with_context(|| { +// format!( +// "Failed to write file to output location: {}", +// output_path.display() +// ) +// })?; +// } +// Self::Css(options) => { +// options.process(source, output_path)?; +// } +// Self::Js(options) => { +// options.process(source, output_path)?; +// } +// Self::Json(options) => { +// options.process(source, output_path)?; +// } +// Self::Image(options) => { +// options.process(source, output_path)?; +// } +// _ => todo!(), +// } + +// Ok(()) +// } +// } + +// impl Process for ImageOptions { +// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { +// let mut image = image::ImageReader::new(std::io::Cursor::new(&*source.read_to_bytes()?)) +// .with_guessed_format()? +// .decode()?; + +// if let Some(size) = self.size() { +// image = image.resize_exact(size.0, size.1, image::imageops::FilterType::Lanczos3); +// } + +// match self.ty() { +// ImageType::Png => { +// compress_png(image, output_path); +// } +// ImageType::Jpg => { +// compress_jpg(image, output_path)?; +// } +// ImageType::Avif => { +// if let Err(error) = image.save(output_path) { +// tracing::error!("Failed to save avif image: {} with path {}. You must have the avif feature enabled to use avif assets", error, output_path.display()); +// } +// } +// ImageType::Webp => { +// if let Err(err) = image.save(output_path) { +// tracing::error!("Failed to save webp image: {}. You must have the avif feature enabled to use webp assets", err); +// } +// } +// } + +// Ok(()) +// } +// } + +// fn compress_jpg(image: DynamicImage, output_location: &Path) -> anyhow::Result<()> { +// let mut comp = mozjpeg::Compress::new(mozjpeg::ColorSpace::JCS_EXT_RGBX); +// let width = image.width() as usize; +// let height = image.height() as usize; + +// comp.set_size(width, height); +// let mut comp = comp.start_compress(Vec::new())?; // any io::Write will work + +// comp.write_scanlines(image.to_rgba8().as_bytes())?; + +// let jpeg_bytes = comp.finish()?; + +// let file = std::fs::File::create(output_location)?; +// let w = &mut BufWriter::new(file); +// w.write_all(&jpeg_bytes)?; +// Ok(()) +// } + +// fn compress_png(image: DynamicImage, output_location: &Path) { +// // Image loading/saving is outside scope of this library +// let width = image.width() as usize; +// let height = image.height() as usize; +// let bitmap: Vec<_> = image +// .into_rgba8() +// .pixels() +// .map(|px| imagequant::RGBA::new(px[0], px[1], px[2], px[3])) +// .collect(); + +// // Configure the library +// let mut liq = imagequant::new(); +// liq.set_speed(5).unwrap(); +// liq.set_quality(0, 99).unwrap(); + +// // Describe the bitmap +// let mut img = liq.new_image(&bitmap[..], width, height, 0.0).unwrap(); + +// // The magic happens in quantize() +// let mut res = match liq.quantize(&mut img) { +// Ok(res) => res, +// Err(err) => panic!("Quantization failed, because: {err:?}"), +// }; + +// let (palette, pixels) = res.remapped(&mut img).unwrap(); + +// let file = std::fs::File::create(output_location).unwrap(); +// let w = &mut BufWriter::new(file); + +// let mut encoder = png::Encoder::new(w, width as u32, height as u32); +// encoder.set_color(png::ColorType::Rgba); +// let mut flattened_palette = Vec::new(); +// let mut alpha_palette = Vec::new(); +// for px in palette { +// flattened_palette.push(px.r); +// flattened_palette.push(px.g); +// flattened_palette.push(px.b); +// alpha_palette.push(px.a); +// } +// encoder.set_palette(flattened_palette); +// encoder.set_trns(alpha_palette); +// encoder.set_depth(png::BitDepth::Eight); +// encoder.set_color(png::ColorType::Indexed); +// encoder.set_compression(png::Compression::Best); +// let mut writer = encoder.write_header().unwrap(); +// writer.write_image_data(&pixels).unwrap(); +// writer.finish().unwrap(); +// } + +// impl Process for CssOptions { +// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { +// let css = source.read_to_string()?; + +// let css = if self.minify() { minify_css(&css) } else { css }; + +// std::fs::write(output_path, css).with_context(|| { +// format!( +// "Failed to write css to output location: {}", +// output_path.display() +// ) +// })?; + +// Ok(()) +// } +// } + +// pub(crate) fn minify_css(css: &str) -> String { +// let mut stylesheet = StyleSheet::parse(css, ParserOptions::default()).unwrap(); +// stylesheet.minify(MinifyOptions::default()).unwrap(); +// let printer = PrinterOptions { +// minify: true, +// ..Default::default() +// }; +// let res = stylesheet.to_css(printer).unwrap(); +// res.code +// } + +// pub(crate) fn minify_js(source: &ResourceAsset) -> anyhow::Result { +// todo!("disabled swc due to semver issues") +// // let cm = Arc::::default(); + +// // let js = source.read_to_string()?; +// // let c = swc::Compiler::new(cm.clone()); +// // let output = GLOBALS +// // .set(&Default::default(), || { +// // try_with_handler(cm.clone(), Default::default(), |handler| { +// // // let filename = Lrc::new(match source { +// // // manganis_common::ResourceAsset::Local(path) => { +// // // FileName::Real(path.canonicalized.clone()) +// // // } +// // // manganis_common::ResourceAsset::Remote(url) => FileName::Url(url.clone()), +// // // }); +// // let filename = todo!(); +// // let fm = cm.new_source_file(filename, js.to_string()); + +// // c.minify( +// // fm, +// // handler, +// // &JsMinifyOptions { +// // compress: BoolOrDataConfig::from_bool(true), +// // mangle: BoolOrDataConfig::from_bool(true), +// // ..Default::default() +// // }, +// // ) +// // .context("failed to minify javascript") +// // }) +// // }) +// // .map(|output| output.code); + +// // match output { +// // Ok(output) => Ok(output), +// // Err(err) => { +// // tracing::error!("Failed to minify javascript: {}", err); +// // Ok(js) +// // } +// // } +// } + +// impl Process for JsOptions { +// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { +// let js = if self.minify() { +// minify_js(source)? +// } else { +// source.read_to_string()? +// }; + +// std::fs::write(output_path, js).with_context(|| { +// format!( +// "Failed to write js to output location: {}", +// output_path.display() +// ) +// })?; + +// Ok(()) +// } +// } + +// pub(crate) fn minify_json(source: &str) -> anyhow::Result { +// // First try to parse the json +// let json: serde_json::Value = serde_json::from_str(source)?; +// // Then print it in a minified format +// let json = serde_json::to_string(&json)?; +// Ok(json) +// } + +// impl Process for JsonOptions { +// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { +// let source = source.read_to_string()?; +// let json = match minify_json(&source) { +// Ok(json) => json, +// Err(err) => { +// tracing::error!("Failed to minify json: {}", err); +// source +// } +// }; + +// std::fs::write(output_path, json).with_context(|| { +// format!( +// "Failed to write json to output location: {}", +// output_path.display() +// ) +// })?; + +// Ok(()) +// } +// } + +// /// Process a folder, optimizing and copying all assets into the output folder +// pub fn process_folder(folder: &FolderAsset, output_folder: &Path) -> anyhow::Result<()> { +// // Push the unique name of the folder to the output folder +// let output_folder = output_folder.join(folder.unique_name()); + +// if output_folder.exists() { +// return Ok(()); +// } + +// // .location() +// // // .source() +// // .as_path() +// let folder = folder.path(); + +// // Optimize and copy all assets in the folder in parallel +// process_folder_inner(folder, &output_folder) +// } + +// fn process_folder_inner(folder: &Path, output_folder: &Path) -> anyhow::Result<()> { +// // Create the folder +// std::fs::create_dir_all(output_folder)?; + +// // Then optimize children +// let files: Vec<_> = std::fs::read_dir(folder) +// .into_iter() +// .flatten() +// .flatten() +// .collect(); + +// files.par_iter().try_for_each(|file| { +// let file = file.path(); +// let metadata = file.metadata()?; +// let output_path = output_folder.join(file.strip_prefix(folder)?); +// if metadata.is_dir() { +// process_folder_inner(&file, &output_path) +// } else { +// process_file_minimal(&file, &output_path) +// } +// })?; + +// Ok(()) +// } + +// /// Optimize a file without changing any of its contents significantly (e.g. by changing the extension) +// fn process_file_minimal(input_path: &Path, output_path: &Path) -> anyhow::Result<()> { +// todo!() +// // let options = +// // FileOptions::default_for_extension(input_path.extension().and_then(|e| e.to_str())); +// // let source = input_path.to_path_buf(); +// // options.process(&source, output_path)?; +// // Ok(()) +// } + +// use image::{DynamicImage, EncodableLayout}; +// use lightningcss::stylesheet::{MinifyOptions, ParserOptions, PrinterOptions, StyleSheet}; +// use manganis_common::{ +// CssOptions, FileOptions, ImageOptions, ImageType, JsOptions, JsonOptions, ResourceAsset, +// }; + +// use swc::{config::JsMinifyOptions, try_with_handler, BoolOrDataConfig}; +// use swc_common::{sync::Lrc, FileName}; +// use swc_common::{SourceMap, GLOBALS}; + +// pub trait Process { +// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()>; +// } + +// /// Process a specific file asset +// pub fn process_file(file: &ResourceAsset, output_folder: &Path) -> anyhow::Result<()> { +// todo!() +// // let location = file.location(); +// // let source = location.source(); +// // let output_path = output_folder.join(location.unique_name()); +// // file.options().process(source, &output_path) +// } + +// impl Process for FileOptions { +// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { +// if output_path.exists() { +// return Ok(()); +// } +// match self { +// Self::Other { .. } => { +// let bytes = source.read_to_bytes()?; +// std::fs::write(output_path, bytes).with_context(|| { +// format!( +// "Failed to write file to output location: {}", +// output_path.display() +// ) +// })?; +// } +// Self::Css(options) => { +// options.process(source, output_path)?; +// } +// Self::Js(options) => { +// options.process(source, output_path)?; +// } +// Self::Json(options) => { +// options.process(source, output_path)?; +// } +// Self::Image(options) => { +// options.process(source, output_path)?; +// } +// _ => todo!(), +// } + +// Ok(()) +// } +// } + +// impl Process for ImageOptions { +// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { +// let mut image = image::ImageReader::new(std::io::Cursor::new(&*source.read_to_bytes()?)) +// .with_guessed_format()? +// .decode()?; + +// if let Some(size) = self.size() { +// image = image.resize_exact(size.0, size.1, image::imageops::FilterType::Lanczos3); +// } + +// match self.ty() { +// ImageType::Png => { +// compress_png(image, output_path); +// } +// ImageType::Jpg => { +// compress_jpg(image, output_path)?; +// } +// ImageType::Avif => { +// if let Err(error) = image.save(output_path) { +// tracing::error!("Failed to save avif image: {} with path {}. You must have the avif feature enabled to use avif assets", error, output_path.display()); +// } +// } +// ImageType::Webp => { +// if let Err(err) = image.save(output_path) { +// tracing::error!("Failed to save webp image: {}. You must have the avif feature enabled to use webp assets", err); +// } +// } +// } + +// Ok(()) +// } +// } + +// fn compress_jpg(image: DynamicImage, output_location: &Path) -> anyhow::Result<()> { +// let mut comp = mozjpeg::Compress::new(mozjpeg::ColorSpace::JCS_EXT_RGBX); +// let width = image.width() as usize; +// let height = image.height() as usize; + +// comp.set_size(width, height); +// let mut comp = comp.start_compress(Vec::new())?; // any io::Write will work + +// comp.write_scanlines(image.to_rgba8().as_bytes())?; + +// let jpeg_bytes = comp.finish()?; + +// let file = std::fs::File::create(output_location)?; +// let w = &mut BufWriter::new(file); +// w.write_all(&jpeg_bytes)?; +// Ok(()) +// } + +// fn compress_png(image: DynamicImage, output_location: &Path) { +// // Image loading/saving is outside scope of this library +// let width = image.width() as usize; +// let height = image.height() as usize; +// let bitmap: Vec<_> = image +// .into_rgba8() +// .pixels() +// .map(|px| imagequant::RGBA::new(px[0], px[1], px[2], px[3])) +// .collect(); + +// // Configure the library +// let mut liq = imagequant::new(); +// liq.set_speed(5).unwrap(); +// liq.set_quality(0, 99).unwrap(); + +// // Describe the bitmap +// let mut img = liq.new_image(&bitmap[..], width, height, 0.0).unwrap(); + +// // The magic happens in quantize() +// let mut res = match liq.quantize(&mut img) { +// Ok(res) => res, +// Err(err) => panic!("Quantization failed, because: {err:?}"), +// }; + +// let (palette, pixels) = res.remapped(&mut img).unwrap(); + +// let file = std::fs::File::create(output_location).unwrap(); +// let w = &mut BufWriter::new(file); + +// let mut encoder = png::Encoder::new(w, width as u32, height as u32); +// encoder.set_color(png::ColorType::Rgba); +// let mut flattened_palette = Vec::new(); +// let mut alpha_palette = Vec::new(); +// for px in palette { +// flattened_palette.push(px.r); +// flattened_palette.push(px.g); +// flattened_palette.push(px.b); +// alpha_palette.push(px.a); +// } +// encoder.set_palette(flattened_palette); +// encoder.set_trns(alpha_palette); +// encoder.set_depth(png::BitDepth::Eight); +// encoder.set_color(png::ColorType::Indexed); +// encoder.set_compression(png::Compression::Best); +// let mut writer = encoder.write_header().unwrap(); +// writer.write_image_data(&pixels).unwrap(); +// writer.finish().unwrap(); +// } + +// impl Process for CssOptions { +// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { +// let css = source.read_to_string()?; + +// let css = if self.minify() { minify_css(&css) } else { css }; + +// std::fs::write(output_path, css).with_context(|| { +// format!( +// "Failed to write css to output location: {}", +// output_path.display() +// ) +// })?; + +// Ok(()) +// } +// } + +// pub(crate) fn minify_css(css: &str) -> String { +// let mut stylesheet = StyleSheet::parse(css, ParserOptions::default()).unwrap(); +// stylesheet.minify(MinifyOptions::default()).unwrap(); +// let printer = PrinterOptions { +// minify: true, +// ..Default::default() +// }; +// let res = stylesheet.to_css(printer).unwrap(); +// res.code +// } + +// pub(crate) fn minify_js(source: &ResourceAsset) -> anyhow::Result { +// todo!("disabled swc due to semver issues") +// // let cm = Arc::::default(); + +// // let js = source.read_to_string()?; +// // let c = swc::Compiler::new(cm.clone()); +// // let output = GLOBALS +// // .set(&Default::default(), || { +// // try_with_handler(cm.clone(), Default::default(), |handler| { +// // // let filename = Lrc::new(match source { +// // // manganis_common::ResourceAsset::Local(path) => { +// // // FileName::Real(path.canonicalized.clone()) +// // // } +// // // manganis_common::ResourceAsset::Remote(url) => FileName::Url(url.clone()), +// // // }); +// // let filename = todo!(); +// // let fm = cm.new_source_file(filename, js.to_string()); + +// // c.minify( +// // fm, +// // handler, +// // &JsMinifyOptions { +// // compress: BoolOrDataConfig::from_bool(true), +// // mangle: BoolOrDataConfig::from_bool(true), +// // ..Default::default() +// // }, +// // ) +// // .context("failed to minify javascript") +// // }) +// // }) +// // .map(|output| output.code); + +// // match output { +// // Ok(output) => Ok(output), +// // Err(err) => { +// // tracing::error!("Failed to minify javascript: {}", err); +// // Ok(js) +// // } +// // } +// } + +// impl Process for JsOptions { +// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { +// let js = if self.minify() { +// minify_js(source)? +// } else { +// source.read_to_string()? +// }; + +// std::fs::write(output_path, js).with_context(|| { +// format!( +// "Failed to write js to output location: {}", +// output_path.display() +// ) +// })?; + +// Ok(()) +// } +// } + +// pub(crate) fn minify_json(source: &str) -> anyhow::Result { +// // First try to parse the json +// let json: serde_json::Value = serde_json::from_str(source)?; +// // Then print it in a minified format +// let json = serde_json::to_string(&json)?; +// Ok(json) +// } + +// impl Process for JsonOptions { +// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { +// let source = source.read_to_string()?; +// let json = match minify_json(&source) { +// Ok(json) => json, +// Err(err) => { +// tracing::error!("Failed to minify json: {}", err); +// source +// } +// }; + +// std::fs::write(output_path, json).with_context(|| { +// format!( +// "Failed to write json to output location: {}", +// output_path.display() +// ) +// })?; + +// Ok(()) +// } +// } + +// /// Returns the HTML that should be injected into the head of the page +// pub fn head(&self) -> String { +// let mut head = String::new(); +// for asset in &self.assets { +// if let crate::AssetType::Resource(file) = asset { +// match file.options() { +// crate::FileOptions::Css(css_options) => { +// if css_options.preload() { +// if let Ok(asset_path) = file.served_location() { +// head.push_str(&format!( +// "\n" +// )) +// } +// } +// } +// crate::FileOptions::Image(image_options) => { +// if image_options.preload() { +// if let Ok(asset_path) = file.served_location() { +// head.push_str(&format!( +// "\n" +// )) +// } +// } +// } +// crate::FileOptions::Js(js_options) => { +// if js_options.preload() { +// if let Ok(asset_path) = file.served_location() { +// head.push_str(&format!( +// "\n" +// )) +// } +// } +// } +// _ => {} +// } +// } +// } +// head +// } + +// /// An extension trait CLI support for the asset manifest +// pub trait AssetManifestExt { +// /// Load a manifest from a list of Manganis JSON strings. +// /// +// /// The asset descriptions are stored inside a manifest file that is produced when the linker is intercepted. +// fn load(json: Vec) -> Self; +// /// Load a manifest from the assets propogated through object files. +// /// +// /// The asset descriptions are stored inside a manifest file that is produced when the linker is intercepted. +// fn load_from_objects(object_paths: Vec) -> Self; +// /// Optimize and copy all assets in the manifest to a folder +// fn copy_static_assets_to(&self, location: impl Into) -> anyhow::Result<()>; +// /// Collect all tailwind classes and generate string with the output css +// fn collect_tailwind_css( +// &self, +// include_preflight: bool, +// warnings: &mut Vec, +// ) -> String; +// } + +// impl AssetManifestExt for AssetManifest { +// fn load(json: Vec) -> Self { +// let mut all_assets = Vec::new(); + +// // Collect all assets for each manganis string found. +// for item in json { +// let mut assets = deserialize_assets(item.as_str()); +// all_assets.append(&mut assets); +// } + +// // If we don't see any manganis assets used in the binary, just return an empty manifest +// if all_assets.is_empty() { +// return Self::default(); +// }; + +// Self::new(all_assets) +// } + +// fn load_from_objects(object_files: Vec) -> Self { +// let json = get_json_from_object_files(object_files); +// Self::load(json) +// } + +// fn copy_static_assets_to(&self, location: impl Into) -> anyhow::Result<()> { +// let location = location.into(); +// match std::fs::create_dir_all(&location) { +// Ok(_) => {} +// Err(err) => { +// tracing::error!("Failed to create directory for static assets: {}", err); +// return Err(err.into()); +// } +// } + +// self.assets().iter().try_for_each(|asset| { +// match asset { +// AssetType::Resource(file_asset) => { +// tracing::info!("Optimizing and bundling {:?}", file_asset); +// tracing::trace!("Copying asset from {:?} to {:?}", file_asset, location); +// match process_file(file_asset, &location) { +// Ok(_) => {} +// Err(err) => { +// tracing::error!("Failed to copy static asset: {}", err); +// return Err(err); +// } +// } + +// // tracing::info!("Copying folder asset {}", folder_asset); +// // match process_folder(folder_asset, &location) { +// // Ok(_) => {} +// // Err(err) => { +// // tracing::error!("Failed to copy static asset: {}", err); +// // return Err(err); +// // } +// // } +// } + +// _ => {} +// } +// Ok::<(), anyhow::Error>(()) +// }) +// } + +// // fn collect_tailwind_css( +// // self: &AssetManifest, +// // include_preflight: bool, +// // warnings: &mut Vec, +// // ) -> String { +// // let mut all_classes = String::new(); + +// // for asset in self.assets() { +// // if let AssetType::Tailwind(classes) = asset { +// // all_classes.push_str(classes.classes()); +// // all_classes.push(' '); +// // } +// // } + +// // let source = railwind::Source::String(all_classes, railwind::CollectionOptions::String); + +// // let css = railwind::parse_to_string(source, include_preflight, warnings); + +// // crate::file::minify_css(&css) +// // } +// } + +// The temp file name for passing manganis json from linker to current exec. +// pub const MG_JSON_OUT: &str = "mg-out"; + +// /// Create a head file that contains all of the imports for assets that the user project uses +// pub fn create_assets_head(build: &BuildRequest, manifest: &AssetManifest) -> Result<()> { +// let out_dir = build.target_out_dir(); +// std::fs::create_dir_all(&out_dir)?; +// let mut file = File::create(out_dir.join("__assets_head.html"))?; +// file.write_all(manifest.head().as_bytes())?; +// Ok(()) +// } + +// use crate::file::Process; + +// /// Process a folder, optimizing and copying all assets into the output folder +// pub fn process_folder(folder: &FolderAsset, output_folder: &Path) -> anyhow::Result<()> { +// // Push the unique name of the folder to the output folder +// let output_folder = output_folder.join(folder.unique_name()); + +// if output_folder.exists() { +// return Ok(()); +// } + +// // .location() +// // // .source() +// // .as_path() +// let folder = folder.path(); + +// // Optimize and copy all assets in the folder in parallel +// process_folder_inner(folder, &output_folder) +// } + +// fn process_folder_inner(folder: &Path, output_folder: &Path) -> anyhow::Result<()> { +// // Create the folder +// std::fs::create_dir_all(output_folder)?; + +// // Then optimize children +// let files: Vec<_> = std::fs::read_dir(folder) +// .into_iter() +// .flatten() +// .flatten() +// .collect(); + +// files.par_iter().try_for_each(|file| { +// let file = file.path(); +// let metadata = file.metadata()?; +// let output_path = output_folder.join(file.strip_prefix(folder)?); +// if metadata.is_dir() { +// process_folder_inner(&file, &output_path) +// } else { +// process_file_minimal(&file, &output_path) +// } +// })?; + +// Ok(()) +// } + +// /// Optimize a file without changing any of its contents significantly (e.g. by changing the extension) +// fn process_file_minimal(input_path: &Path, output_path: &Path) -> anyhow::Result<()> { +// todo!() +// // let options = +// // FileOptions::default_for_extension(input_path.extension().and_then(|e| e.to_str())); +// // let source = input_path.to_path_buf(); +// // options.process(&source, output_path)?; +// // Ok(()) +// } diff --git a/packages/cli/src/assets/file.rs b/packages/cli/src/assets/file.rs deleted file mode 100644 index 1922154b85..0000000000 --- a/packages/cli/src/assets/file.rs +++ /dev/null @@ -1,820 +0,0 @@ -use anyhow::Context; -// use manganis_common::{FileOptions, FolderAsset}; -use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; -// use image::{DynamicImage, EncodableLayout}; -// use lightningcss::stylesheet::{MinifyOptions, ParserOptions, PrinterOptions, StyleSheet}; -// use manganis_common::{ -// CssOptions, FileOptions, ImageOptions, ImageType, JsOptions, JsonOptions, ResourceAsset, -// }; -use std::{ - io::{BufWriter, Write}, - path::Path, - sync::Arc, -}; -// use swc::{config::JsMinifyOptions, try_with_handler, BoolOrDataConfig}; -// use swc_common::{sync::Lrc, FileName}; -// use swc_common::{SourceMap, GLOBALS}; - -// pub trait Process { -// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()>; -// } - -// /// Process a specific file asset -// pub fn process_file(file: &ResourceAsset, output_folder: &Path) -> anyhow::Result<()> { -// todo!() -// // let location = file.location(); -// // let source = location.source(); -// // let output_path = output_folder.join(location.unique_name()); -// // file.options().process(source, &output_path) -// } - -// impl Process for FileOptions { -// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { -// if output_path.exists() { -// return Ok(()); -// } -// match self { -// Self::Other { .. } => { -// let bytes = source.read_to_bytes()?; -// std::fs::write(output_path, bytes).with_context(|| { -// format!( -// "Failed to write file to output location: {}", -// output_path.display() -// ) -// })?; -// } -// Self::Css(options) => { -// options.process(source, output_path)?; -// } -// Self::Js(options) => { -// options.process(source, output_path)?; -// } -// Self::Json(options) => { -// options.process(source, output_path)?; -// } -// Self::Image(options) => { -// options.process(source, output_path)?; -// } -// _ => todo!(), -// } - -// Ok(()) -// } -// } - -// impl Process for ImageOptions { -// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { -// let mut image = image::ImageReader::new(std::io::Cursor::new(&*source.read_to_bytes()?)) -// .with_guessed_format()? -// .decode()?; - -// if let Some(size) = self.size() { -// image = image.resize_exact(size.0, size.1, image::imageops::FilterType::Lanczos3); -// } - -// match self.ty() { -// ImageType::Png => { -// compress_png(image, output_path); -// } -// ImageType::Jpg => { -// compress_jpg(image, output_path)?; -// } -// ImageType::Avif => { -// if let Err(error) = image.save(output_path) { -// tracing::error!("Failed to save avif image: {} with path {}. You must have the avif feature enabled to use avif assets", error, output_path.display()); -// } -// } -// ImageType::Webp => { -// if let Err(err) = image.save(output_path) { -// tracing::error!("Failed to save webp image: {}. You must have the avif feature enabled to use webp assets", err); -// } -// } -// } - -// Ok(()) -// } -// } - -// fn compress_jpg(image: DynamicImage, output_location: &Path) -> anyhow::Result<()> { -// let mut comp = mozjpeg::Compress::new(mozjpeg::ColorSpace::JCS_EXT_RGBX); -// let width = image.width() as usize; -// let height = image.height() as usize; - -// comp.set_size(width, height); -// let mut comp = comp.start_compress(Vec::new())?; // any io::Write will work - -// comp.write_scanlines(image.to_rgba8().as_bytes())?; - -// let jpeg_bytes = comp.finish()?; - -// let file = std::fs::File::create(output_location)?; -// let w = &mut BufWriter::new(file); -// w.write_all(&jpeg_bytes)?; -// Ok(()) -// } - -// fn compress_png(image: DynamicImage, output_location: &Path) { -// // Image loading/saving is outside scope of this library -// let width = image.width() as usize; -// let height = image.height() as usize; -// let bitmap: Vec<_> = image -// .into_rgba8() -// .pixels() -// .map(|px| imagequant::RGBA::new(px[0], px[1], px[2], px[3])) -// .collect(); - -// // Configure the library -// let mut liq = imagequant::new(); -// liq.set_speed(5).unwrap(); -// liq.set_quality(0, 99).unwrap(); - -// // Describe the bitmap -// let mut img = liq.new_image(&bitmap[..], width, height, 0.0).unwrap(); - -// // The magic happens in quantize() -// let mut res = match liq.quantize(&mut img) { -// Ok(res) => res, -// Err(err) => panic!("Quantization failed, because: {err:?}"), -// }; - -// let (palette, pixels) = res.remapped(&mut img).unwrap(); - -// let file = std::fs::File::create(output_location).unwrap(); -// let w = &mut BufWriter::new(file); - -// let mut encoder = png::Encoder::new(w, width as u32, height as u32); -// encoder.set_color(png::ColorType::Rgba); -// let mut flattened_palette = Vec::new(); -// let mut alpha_palette = Vec::new(); -// for px in palette { -// flattened_palette.push(px.r); -// flattened_palette.push(px.g); -// flattened_palette.push(px.b); -// alpha_palette.push(px.a); -// } -// encoder.set_palette(flattened_palette); -// encoder.set_trns(alpha_palette); -// encoder.set_depth(png::BitDepth::Eight); -// encoder.set_color(png::ColorType::Indexed); -// encoder.set_compression(png::Compression::Best); -// let mut writer = encoder.write_header().unwrap(); -// writer.write_image_data(&pixels).unwrap(); -// writer.finish().unwrap(); -// } - -// impl Process for CssOptions { -// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { -// let css = source.read_to_string()?; - -// let css = if self.minify() { minify_css(&css) } else { css }; - -// std::fs::write(output_path, css).with_context(|| { -// format!( -// "Failed to write css to output location: {}", -// output_path.display() -// ) -// })?; - -// Ok(()) -// } -// } - -// pub(crate) fn minify_css(css: &str) -> String { -// let mut stylesheet = StyleSheet::parse(css, ParserOptions::default()).unwrap(); -// stylesheet.minify(MinifyOptions::default()).unwrap(); -// let printer = PrinterOptions { -// minify: true, -// ..Default::default() -// }; -// let res = stylesheet.to_css(printer).unwrap(); -// res.code -// } - -// pub(crate) fn minify_js(source: &ResourceAsset) -> anyhow::Result { -// todo!("disabled swc due to semver issues") -// // let cm = Arc::::default(); - -// // let js = source.read_to_string()?; -// // let c = swc::Compiler::new(cm.clone()); -// // let output = GLOBALS -// // .set(&Default::default(), || { -// // try_with_handler(cm.clone(), Default::default(), |handler| { -// // // let filename = Lrc::new(match source { -// // // manganis_common::ResourceAsset::Local(path) => { -// // // FileName::Real(path.canonicalized.clone()) -// // // } -// // // manganis_common::ResourceAsset::Remote(url) => FileName::Url(url.clone()), -// // // }); -// // let filename = todo!(); -// // let fm = cm.new_source_file(filename, js.to_string()); - -// // c.minify( -// // fm, -// // handler, -// // &JsMinifyOptions { -// // compress: BoolOrDataConfig::from_bool(true), -// // mangle: BoolOrDataConfig::from_bool(true), -// // ..Default::default() -// // }, -// // ) -// // .context("failed to minify javascript") -// // }) -// // }) -// // .map(|output| output.code); - -// // match output { -// // Ok(output) => Ok(output), -// // Err(err) => { -// // tracing::error!("Failed to minify javascript: {}", err); -// // Ok(js) -// // } -// // } -// } - -// impl Process for JsOptions { -// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { -// let js = if self.minify() { -// minify_js(source)? -// } else { -// source.read_to_string()? -// }; - -// std::fs::write(output_path, js).with_context(|| { -// format!( -// "Failed to write js to output location: {}", -// output_path.display() -// ) -// })?; - -// Ok(()) -// } -// } - -// pub(crate) fn minify_json(source: &str) -> anyhow::Result { -// // First try to parse the json -// let json: serde_json::Value = serde_json::from_str(source)?; -// // Then print it in a minified format -// let json = serde_json::to_string(&json)?; -// Ok(json) -// } - -// impl Process for JsonOptions { -// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { -// let source = source.read_to_string()?; -// let json = match minify_json(&source) { -// Ok(json) => json, -// Err(err) => { -// tracing::error!("Failed to minify json: {}", err); -// source -// } -// }; - -// std::fs::write(output_path, json).with_context(|| { -// format!( -// "Failed to write json to output location: {}", -// output_path.display() -// ) -// })?; - -// Ok(()) -// } -// } - -// /// Process a folder, optimizing and copying all assets into the output folder -// pub fn process_folder(folder: &FolderAsset, output_folder: &Path) -> anyhow::Result<()> { -// // Push the unique name of the folder to the output folder -// let output_folder = output_folder.join(folder.unique_name()); - -// if output_folder.exists() { -// return Ok(()); -// } - -// // .location() -// // // .source() -// // .as_path() -// let folder = folder.path(); - -// // Optimize and copy all assets in the folder in parallel -// process_folder_inner(folder, &output_folder) -// } - -// fn process_folder_inner(folder: &Path, output_folder: &Path) -> anyhow::Result<()> { -// // Create the folder -// std::fs::create_dir_all(output_folder)?; - -// // Then optimize children -// let files: Vec<_> = std::fs::read_dir(folder) -// .into_iter() -// .flatten() -// .flatten() -// .collect(); - -// files.par_iter().try_for_each(|file| { -// let file = file.path(); -// let metadata = file.metadata()?; -// let output_path = output_folder.join(file.strip_prefix(folder)?); -// if metadata.is_dir() { -// process_folder_inner(&file, &output_path) -// } else { -// process_file_minimal(&file, &output_path) -// } -// })?; - -// Ok(()) -// } - -// /// Optimize a file without changing any of its contents significantly (e.g. by changing the extension) -// fn process_file_minimal(input_path: &Path, output_path: &Path) -> anyhow::Result<()> { -// todo!() -// // let options = -// // FileOptions::default_for_extension(input_path.extension().and_then(|e| e.to_str())); -// // let source = input_path.to_path_buf(); -// // options.process(&source, output_path)?; -// // Ok(()) -// } - -// use image::{DynamicImage, EncodableLayout}; -// use lightningcss::stylesheet::{MinifyOptions, ParserOptions, PrinterOptions, StyleSheet}; -// use manganis_common::{ -// CssOptions, FileOptions, ImageOptions, ImageType, JsOptions, JsonOptions, ResourceAsset, -// }; - -// use swc::{config::JsMinifyOptions, try_with_handler, BoolOrDataConfig}; -// use swc_common::{sync::Lrc, FileName}; -// use swc_common::{SourceMap, GLOBALS}; - -// pub trait Process { -// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()>; -// } - -// /// Process a specific file asset -// pub fn process_file(file: &ResourceAsset, output_folder: &Path) -> anyhow::Result<()> { -// todo!() -// // let location = file.location(); -// // let source = location.source(); -// // let output_path = output_folder.join(location.unique_name()); -// // file.options().process(source, &output_path) -// } - -// impl Process for FileOptions { -// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { -// if output_path.exists() { -// return Ok(()); -// } -// match self { -// Self::Other { .. } => { -// let bytes = source.read_to_bytes()?; -// std::fs::write(output_path, bytes).with_context(|| { -// format!( -// "Failed to write file to output location: {}", -// output_path.display() -// ) -// })?; -// } -// Self::Css(options) => { -// options.process(source, output_path)?; -// } -// Self::Js(options) => { -// options.process(source, output_path)?; -// } -// Self::Json(options) => { -// options.process(source, output_path)?; -// } -// Self::Image(options) => { -// options.process(source, output_path)?; -// } -// _ => todo!(), -// } - -// Ok(()) -// } -// } - -// impl Process for ImageOptions { -// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { -// let mut image = image::ImageReader::new(std::io::Cursor::new(&*source.read_to_bytes()?)) -// .with_guessed_format()? -// .decode()?; - -// if let Some(size) = self.size() { -// image = image.resize_exact(size.0, size.1, image::imageops::FilterType::Lanczos3); -// } - -// match self.ty() { -// ImageType::Png => { -// compress_png(image, output_path); -// } -// ImageType::Jpg => { -// compress_jpg(image, output_path)?; -// } -// ImageType::Avif => { -// if let Err(error) = image.save(output_path) { -// tracing::error!("Failed to save avif image: {} with path {}. You must have the avif feature enabled to use avif assets", error, output_path.display()); -// } -// } -// ImageType::Webp => { -// if let Err(err) = image.save(output_path) { -// tracing::error!("Failed to save webp image: {}. You must have the avif feature enabled to use webp assets", err); -// } -// } -// } - -// Ok(()) -// } -// } - -// fn compress_jpg(image: DynamicImage, output_location: &Path) -> anyhow::Result<()> { -// let mut comp = mozjpeg::Compress::new(mozjpeg::ColorSpace::JCS_EXT_RGBX); -// let width = image.width() as usize; -// let height = image.height() as usize; - -// comp.set_size(width, height); -// let mut comp = comp.start_compress(Vec::new())?; // any io::Write will work - -// comp.write_scanlines(image.to_rgba8().as_bytes())?; - -// let jpeg_bytes = comp.finish()?; - -// let file = std::fs::File::create(output_location)?; -// let w = &mut BufWriter::new(file); -// w.write_all(&jpeg_bytes)?; -// Ok(()) -// } - -// fn compress_png(image: DynamicImage, output_location: &Path) { -// // Image loading/saving is outside scope of this library -// let width = image.width() as usize; -// let height = image.height() as usize; -// let bitmap: Vec<_> = image -// .into_rgba8() -// .pixels() -// .map(|px| imagequant::RGBA::new(px[0], px[1], px[2], px[3])) -// .collect(); - -// // Configure the library -// let mut liq = imagequant::new(); -// liq.set_speed(5).unwrap(); -// liq.set_quality(0, 99).unwrap(); - -// // Describe the bitmap -// let mut img = liq.new_image(&bitmap[..], width, height, 0.0).unwrap(); - -// // The magic happens in quantize() -// let mut res = match liq.quantize(&mut img) { -// Ok(res) => res, -// Err(err) => panic!("Quantization failed, because: {err:?}"), -// }; - -// let (palette, pixels) = res.remapped(&mut img).unwrap(); - -// let file = std::fs::File::create(output_location).unwrap(); -// let w = &mut BufWriter::new(file); - -// let mut encoder = png::Encoder::new(w, width as u32, height as u32); -// encoder.set_color(png::ColorType::Rgba); -// let mut flattened_palette = Vec::new(); -// let mut alpha_palette = Vec::new(); -// for px in palette { -// flattened_palette.push(px.r); -// flattened_palette.push(px.g); -// flattened_palette.push(px.b); -// alpha_palette.push(px.a); -// } -// encoder.set_palette(flattened_palette); -// encoder.set_trns(alpha_palette); -// encoder.set_depth(png::BitDepth::Eight); -// encoder.set_color(png::ColorType::Indexed); -// encoder.set_compression(png::Compression::Best); -// let mut writer = encoder.write_header().unwrap(); -// writer.write_image_data(&pixels).unwrap(); -// writer.finish().unwrap(); -// } - -// impl Process for CssOptions { -// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { -// let css = source.read_to_string()?; - -// let css = if self.minify() { minify_css(&css) } else { css }; - -// std::fs::write(output_path, css).with_context(|| { -// format!( -// "Failed to write css to output location: {}", -// output_path.display() -// ) -// })?; - -// Ok(()) -// } -// } - -// pub(crate) fn minify_css(css: &str) -> String { -// let mut stylesheet = StyleSheet::parse(css, ParserOptions::default()).unwrap(); -// stylesheet.minify(MinifyOptions::default()).unwrap(); -// let printer = PrinterOptions { -// minify: true, -// ..Default::default() -// }; -// let res = stylesheet.to_css(printer).unwrap(); -// res.code -// } - -// pub(crate) fn minify_js(source: &ResourceAsset) -> anyhow::Result { -// todo!("disabled swc due to semver issues") -// // let cm = Arc::::default(); - -// // let js = source.read_to_string()?; -// // let c = swc::Compiler::new(cm.clone()); -// // let output = GLOBALS -// // .set(&Default::default(), || { -// // try_with_handler(cm.clone(), Default::default(), |handler| { -// // // let filename = Lrc::new(match source { -// // // manganis_common::ResourceAsset::Local(path) => { -// // // FileName::Real(path.canonicalized.clone()) -// // // } -// // // manganis_common::ResourceAsset::Remote(url) => FileName::Url(url.clone()), -// // // }); -// // let filename = todo!(); -// // let fm = cm.new_source_file(filename, js.to_string()); - -// // c.minify( -// // fm, -// // handler, -// // &JsMinifyOptions { -// // compress: BoolOrDataConfig::from_bool(true), -// // mangle: BoolOrDataConfig::from_bool(true), -// // ..Default::default() -// // }, -// // ) -// // .context("failed to minify javascript") -// // }) -// // }) -// // .map(|output| output.code); - -// // match output { -// // Ok(output) => Ok(output), -// // Err(err) => { -// // tracing::error!("Failed to minify javascript: {}", err); -// // Ok(js) -// // } -// // } -// } - -// impl Process for JsOptions { -// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { -// let js = if self.minify() { -// minify_js(source)? -// } else { -// source.read_to_string()? -// }; - -// std::fs::write(output_path, js).with_context(|| { -// format!( -// "Failed to write js to output location: {}", -// output_path.display() -// ) -// })?; - -// Ok(()) -// } -// } - -// pub(crate) fn minify_json(source: &str) -> anyhow::Result { -// // First try to parse the json -// let json: serde_json::Value = serde_json::from_str(source)?; -// // Then print it in a minified format -// let json = serde_json::to_string(&json)?; -// Ok(json) -// } - -// impl Process for JsonOptions { -// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { -// let source = source.read_to_string()?; -// let json = match minify_json(&source) { -// Ok(json) => json, -// Err(err) => { -// tracing::error!("Failed to minify json: {}", err); -// source -// } -// }; - -// std::fs::write(output_path, json).with_context(|| { -// format!( -// "Failed to write json to output location: {}", -// output_path.display() -// ) -// })?; - -// Ok(()) -// } -// } - -// /// Returns the HTML that should be injected into the head of the page -// pub fn head(&self) -> String { -// let mut head = String::new(); -// for asset in &self.assets { -// if let crate::AssetType::Resource(file) = asset { -// match file.options() { -// crate::FileOptions::Css(css_options) => { -// if css_options.preload() { -// if let Ok(asset_path) = file.served_location() { -// head.push_str(&format!( -// "\n" -// )) -// } -// } -// } -// crate::FileOptions::Image(image_options) => { -// if image_options.preload() { -// if let Ok(asset_path) = file.served_location() { -// head.push_str(&format!( -// "\n" -// )) -// } -// } -// } -// crate::FileOptions::Js(js_options) => { -// if js_options.preload() { -// if let Ok(asset_path) = file.served_location() { -// head.push_str(&format!( -// "\n" -// )) -// } -// } -// } -// _ => {} -// } -// } -// } -// head -// } - -// /// An extension trait CLI support for the asset manifest -// pub trait AssetManifestExt { -// /// Load a manifest from a list of Manganis JSON strings. -// /// -// /// The asset descriptions are stored inside a manifest file that is produced when the linker is intercepted. -// fn load(json: Vec) -> Self; -// /// Load a manifest from the assets propogated through object files. -// /// -// /// The asset descriptions are stored inside a manifest file that is produced when the linker is intercepted. -// fn load_from_objects(object_paths: Vec) -> Self; -// /// Optimize and copy all assets in the manifest to a folder -// fn copy_static_assets_to(&self, location: impl Into) -> anyhow::Result<()>; -// /// Collect all tailwind classes and generate string with the output css -// fn collect_tailwind_css( -// &self, -// include_preflight: bool, -// warnings: &mut Vec, -// ) -> String; -// } - -// impl AssetManifestExt for AssetManifest { -// fn load(json: Vec) -> Self { -// let mut all_assets = Vec::new(); - -// // Collect all assets for each manganis string found. -// for item in json { -// let mut assets = deserialize_assets(item.as_str()); -// all_assets.append(&mut assets); -// } - -// // If we don't see any manganis assets used in the binary, just return an empty manifest -// if all_assets.is_empty() { -// return Self::default(); -// }; - -// Self::new(all_assets) -// } - -// fn load_from_objects(object_files: Vec) -> Self { -// let json = get_json_from_object_files(object_files); -// Self::load(json) -// } - -// fn copy_static_assets_to(&self, location: impl Into) -> anyhow::Result<()> { -// let location = location.into(); -// match std::fs::create_dir_all(&location) { -// Ok(_) => {} -// Err(err) => { -// tracing::error!("Failed to create directory for static assets: {}", err); -// return Err(err.into()); -// } -// } - -// self.assets().iter().try_for_each(|asset| { -// match asset { -// AssetType::Resource(file_asset) => { -// tracing::info!("Optimizing and bundling {:?}", file_asset); -// tracing::trace!("Copying asset from {:?} to {:?}", file_asset, location); -// match process_file(file_asset, &location) { -// Ok(_) => {} -// Err(err) => { -// tracing::error!("Failed to copy static asset: {}", err); -// return Err(err); -// } -// } - -// // tracing::info!("Copying folder asset {}", folder_asset); -// // match process_folder(folder_asset, &location) { -// // Ok(_) => {} -// // Err(err) => { -// // tracing::error!("Failed to copy static asset: {}", err); -// // return Err(err); -// // } -// // } -// } - -// _ => {} -// } -// Ok::<(), anyhow::Error>(()) -// }) -// } - -// // fn collect_tailwind_css( -// // self: &AssetManifest, -// // include_preflight: bool, -// // warnings: &mut Vec, -// // ) -> String { -// // let mut all_classes = String::new(); - -// // for asset in self.assets() { -// // if let AssetType::Tailwind(classes) = asset { -// // all_classes.push_str(classes.classes()); -// // all_classes.push(' '); -// // } -// // } - -// // let source = railwind::Source::String(all_classes, railwind::CollectionOptions::String); - -// // let css = railwind::parse_to_string(source, include_preflight, warnings); - -// // crate::file::minify_css(&css) -// // } -// } - -// The temp file name for passing manganis json from linker to current exec. -// pub const MG_JSON_OUT: &str = "mg-out"; - -// /// Create a head file that contains all of the imports for assets that the user project uses -// pub fn create_assets_head(build: &BuildRequest, manifest: &AssetManifest) -> Result<()> { -// let out_dir = build.target_out_dir(); -// std::fs::create_dir_all(&out_dir)?; -// let mut file = File::create(out_dir.join("__assets_head.html"))?; -// file.write_all(manifest.head().as_bytes())?; -// Ok(()) -// } - -// use crate::file::Process; - -// /// Process a folder, optimizing and copying all assets into the output folder -// pub fn process_folder(folder: &FolderAsset, output_folder: &Path) -> anyhow::Result<()> { -// // Push the unique name of the folder to the output folder -// let output_folder = output_folder.join(folder.unique_name()); - -// if output_folder.exists() { -// return Ok(()); -// } - -// // .location() -// // // .source() -// // .as_path() -// let folder = folder.path(); - -// // Optimize and copy all assets in the folder in parallel -// process_folder_inner(folder, &output_folder) -// } - -// fn process_folder_inner(folder: &Path, output_folder: &Path) -> anyhow::Result<()> { -// // Create the folder -// std::fs::create_dir_all(output_folder)?; - -// // Then optimize children -// let files: Vec<_> = std::fs::read_dir(folder) -// .into_iter() -// .flatten() -// .flatten() -// .collect(); - -// files.par_iter().try_for_each(|file| { -// let file = file.path(); -// let metadata = file.metadata()?; -// let output_path = output_folder.join(file.strip_prefix(folder)?); -// if metadata.is_dir() { -// process_folder_inner(&file, &output_path) -// } else { -// process_file_minimal(&file, &output_path) -// } -// })?; - -// Ok(()) -// } - -// /// Optimize a file without changing any of its contents significantly (e.g. by changing the extension) -// fn process_file_minimal(input_path: &Path, output_path: &Path) -> anyhow::Result<()> { -// todo!() -// // let options = -// // FileOptions::default_for_extension(input_path.extension().and_then(|e| e.to_str())); -// // let source = input_path.to_path_buf(); -// // options.process(&source, output_path)?; -// // Ok(()) -// } diff --git a/packages/cli/src/assets/manifest.rs b/packages/cli/src/assets/manifest.rs deleted file mode 100644 index 52bd479278..0000000000 --- a/packages/cli/src/assets/manifest.rs +++ /dev/null @@ -1,205 +0,0 @@ -use manganis_core::{LinkSection, ResourceAsset}; -use object::{read::archive::ArchiveFile, File as ObjectFile, Object, ObjectSection}; -use std::{collections::HashMap, path::PathBuf}; - -use crate::link::InterceptedArgs; - -/// A manifest of all assets collected from dependencies -/// -/// This will be filled in primarly by incremental compilation artifacts. -#[derive(Debug, PartialEq, Default, Clone)] -pub struct AssetManifest { - /// Map of asset pathbuf to its - pub(crate) assets: HashMap, -} - -impl AssetManifest { - /// Creates a new asset manifest - pub fn new() -> Self { - Self { - ..Default::default() - } - } - - /// Fill this manifest from the intercepted rustc args used to link the app together - pub fn add_from_linker_intercept(&mut self, args: InterceptedArgs) { - // Attempt to load the arg as a command file, otherwise just use the args themselves - // This is because windows will pass in `@linkerargs.txt` as a source of linker args - if let Some(command) = args.args.iter().find(|arg| arg.starts_with('@')).cloned() { - self.add_from_command_file(args, &command); - } else { - self.add_from_linker_args(args); - } - } - - /// Fill this manifest from the contents of a linker command file. - /// - /// Rustc will pass a file as link args to linkers on windows instead of args directly. - /// - /// We actually need to read that file and then pull out the args directly. - pub fn add_from_command_file(&mut self, args: InterceptedArgs, arg: &str) { - let path = arg.trim().trim_start_matches('@'); - let file_binary = std::fs::read(path).unwrap(); - - // This may be a utf-16le file. Let's try utf-8 first. - let content = match String::from_utf8(file_binary.clone()) { - Ok(s) => s, - Err(_) => { - // Convert Vec to Vec to convert into a String - let binary_u16le: Vec = file_binary - .chunks_exact(2) - .map(|a| u16::from_le_bytes([a[0], a[1]])) - .collect(); - - String::from_utf16_lossy(&binary_u16le) - } - }; - - // Gather linker args - let mut linker_args = Vec::new(); - let lines = content.lines(); - for line in lines { - // Remove quotes from the line - windows link args files are quoted - let line_parsed = line.to_string(); - let line_parsed = line_parsed.trim_end_matches('"').to_string(); - let line_parsed = line_parsed.trim_start_matches('"').to_string(); - linker_args.push(line_parsed); - } - - self.add_from_linker_args(InterceptedArgs { - args: linker_args, - ..args - }); - } - - pub fn add_from_linker_args(&mut self, args: InterceptedArgs) { - // Parse through linker args for `.o` or `.rlib` files. - for item in args.args { - if item.ends_with(".o") || item.ends_with(".rlib") { - self.add_from_object_path(args.work_dir.join(PathBuf::from(item))); - } - } - } - - /// Fill this manifest with a file object/rlib files, typically extracted from the linker intercepted - pub fn add_from_object_path(&mut self, path: PathBuf) { - let Some(ext) = path.extension() else { - return; - }; - - let Some(ext) = ext.to_str() else { - return; - }; - - let data = std::fs::read(path.clone()).expect("Failed to read asset optimization file"); - - match ext { - // Parse an unarchived object file - "o" => { - let object = object::File::parse(&*data).unwrap(); - self.add_from_object_file(&object); - } - - // Parse an rlib as a collection of objects - "rlib" => { - let archive = object::read::archive::ArchiveFile::parse(&*data).unwrap(); - self.add_from_archive_file(&archive, &data); - } - _ => {} - } - } - - /// Fill this manifest from an rlib / ar file that contains many object files and their entryies - pub fn add_from_archive_file(&mut self, archive: &ArchiveFile, data: &[u8]) { - // Look through each archive member for object files. - // Read the archive member's binary data (we know it's an object file) - // And parse it with the normal `object::File::parse` to find the manganis string. - for member in archive.members() { - let member = member.unwrap(); - let name = String::from_utf8_lossy(member.name()).to_string(); - - // Check if the archive member is an object file and parse it. - if name.ends_with(".o") { - let data = member.data(&*data).unwrap(); - let object = object::File::parse(data).unwrap(); - self.add_from_object_file(&object); - } - } - } - - /// Fill this manifest with whatever tables might come from the object file - pub fn add_from_object_file(&mut self, obj: &ObjectFile) -> Option<()> { - for section in obj.sections() { - let Ok(section_name) = section.name() else { - continue; - }; - - // Check if the link section matches the asset section for one of the platforms we support. This may not be the current platform if the user is cross compiling - let matches = LinkSection::ALL - .iter() - .any(|x| x.link_section == section_name); - - if !matches { - continue; - } - - let bytes = section.uncompressed_data().ok()?; - - let as_str = std::str::from_utf8(&bytes) - .ok()? - .chars() - .filter(|c| !c.is_control()) - .collect::(); - - let stream = serde_json::Deserializer::from_str(&as_str).into_iter::(); - - for as_resource in stream { - let as_resource = as_resource.unwrap(); - - // Some platforms (e.g. macOS) start the manganis section with a null byte, we need to filter that out before we deserialize the JSON - self.assets - .insert(as_resource.absolute.clone(), as_resource); - } - } - - None - } -} - -#[derive(Clone)] -pub struct OptimizeOptions { - pub precompress: bool, - pub enabled: bool, -} - -impl AssetManifest { - /// Copy the assest from this manifest to a target folder - /// - /// If `optimize` is enabled, then we will run the optimizer for this asset. - /// - /// The output file is guaranteed to be the destination + the ResourceAsset bundle name - /// - /// Will not actually copy the asset if the source asset hasn't changed? - pub fn copy_asset_to( - &self, - destination: PathBuf, - target_asset: PathBuf, - optimize: &OptimizeOptions, - ) { - let src = self.assets.get(&target_asset).unwrap(); - - let local = src.absolute.clone(); - - if !local.exists() { - panic!("Specified asset does not exist while trying to copy {target_asset:?} to {destination:?}") - } - - // If there's no optimizaton while copying this asset, we simply std::fs::copy and call it a day - if !optimize.enabled { - std::fs::copy(local, destination.join(&src.bundled)).expect("Failed to copy asset"); - return; - } - - // Otherwise, let's attempt to optimize the the asset we're copying - } -} diff --git a/packages/cli/src/builder/assets.rs b/packages/cli/src/builder/assets.rs index 8a716a36aa..1e6025daa4 100644 --- a/packages/cli/src/builder/assets.rs +++ b/packages/cli/src/builder/assets.rs @@ -1,7 +1,6 @@ -use super::BuildRequest; -use super::TargetPlatform; +use super::Platform; +use super::{BuildRequest, BuildResult}; use crate::builder::{progress::UpdateStage, MessageSource}; -use crate::config::Platform; use crate::Result; use crate::{ assets::OptimizeOptions, @@ -37,15 +36,15 @@ impl BuildRequest { /// This will execute `dx` with an env var set to force `dx` to operate as a linker, and then /// traverse the .o and .rlib files rustc passes that new `dx` instance, collecting the link /// tables marked by manganis and parsing them as a ResourceAsset. - pub async fn collect_assets(&mut self, cargo_args: Vec) -> anyhow::Result<()> { + pub async fn collect_assets(&self) -> anyhow::Result { // If this is the server build, the client build already copied any assets we need - if self.target_platform == TargetPlatform::Server { - return Ok(()); + if self.platform() == Platform::Server { + return Ok(AssetManifest::default()); } // If assets are skipped, we don't need to collect them - if self.build_arguments.skip_assets { - return Ok(()); + if self.build.skip_assets { + return Ok(AssetManifest::default()); } // Create a temp file to put the output of the args @@ -62,7 +61,7 @@ impl BuildRequest { // This might not be a "stable" way of keeping artifacts around, but it's in stable rustc tokio::process::Command::new("cargo") .arg("rustc") - .args(cargo_args) + .args(self.build_arguments()) .arg("--offline") /* don't use the network, should already be resolved */ .arg("--") .arg(format!("-Clinker={}", current_exe().unwrap().display())) /* pass ourselves in */ @@ -81,69 +80,67 @@ impl BuildRequest { let args = serde_json::from_str::(&args).expect("Failed to parse linker output"); - self.assets.add_from_linker_intercept(args); - - Ok(()) + Ok(AssetManifest::new_from_linker_intercept(args)) } - pub fn copy_assets_dir(&self) -> anyhow::Result<()> { - tracing::info!("Copying public assets to the output directory..."); - - let static_asset_output_dir = self.target_out_dir(); - std::fs::create_dir_all(&static_asset_output_dir) - .context("Failed to create static asset output directory")?; - - // todo: join the entire asset dir here - let asset_dir = self.krate.asset_dir(); - let assets = self.assets.assets.keys().collect::>(); - - let assets_finished = AtomicUsize::new(0); - let asset_count = assets.len(); - - let options = OptimizeOptions { - enabled: false, - precompress: self.targeting_web() - && self - .krate - .should_pre_compress_web_assets(self.build_arguments.release), - }; - - assets - .par_iter() - .enumerate() - .try_for_each(|(_idx, asset)| { - // Update the progress - _ = self.progress.unbounded_send(UpdateBuildProgress { - stage: Stage::OptimizingAssets, - update: UpdateStage::AddMessage(BuildMessage { - level: Level::INFO, - message: MessageType::Text(format!( - "Optimized static asset {}", - asset.display() - )), - source: MessageSource::Build, - }), - platform: self.target_platform, - }); - - // Copy the asset into the bundle directory - self.assets.copy_asset_to( - static_asset_output_dir.clone(), - asset.to_path_buf(), - &options, - ); - - let finished = assets_finished.fetch_add(1, std::sync::atomic::Ordering::SeqCst); - - _ = self.progress.unbounded_send(UpdateBuildProgress { - stage: Stage::OptimizingAssets, - update: UpdateStage::SetProgress(finished as f64 / asset_count as f64), - platform: self.target_platform, - }); - - Ok(()) as anyhow::Result<()> - })?; - - Ok(()) - } + // pub fn copy_assets_dir(&self) -> anyhow::Result<()> { + // tracing::info!("Copying public assets to the output directory..."); + + // let static_asset_output_dir = self.target_out_dir(); + // std::fs::create_dir_all(&static_asset_output_dir) + // .context("Failed to create static asset output directory")?; + + // // todo: join the entire asset dir here + // let asset_dir = self.krate.asset_dir(); + // let assets = self.assets.assets.keys().collect::>(); + + // let assets_finished = AtomicUsize::new(0); + // let asset_count = assets.len(); + + // let options = OptimizeOptions { + // enabled: false, + // precompress: self.targeting_web() + // && self + // .krate + // .should_pre_compress_web_assets(self.build_arguments.release), + // }; + + // assets + // .par_iter() + // .enumerate() + // .try_for_each(|(_idx, asset)| { + // // Update the progress + // _ = self.progress.unbounded_send(UpdateBuildProgress { + // stage: Stage::OptimizingAssets, + // update: UpdateStage::AddMessage(BuildMessage { + // level: Level::INFO, + // message: MessageType::Text(format!( + // "Optimized static asset {}", + // asset.display() + // )), + // source: MessageSource::Build, + // }), + // platform: self.target_platform, + // }); + + // // Copy the asset into the bundle directory + // self.assets.copy_asset_to( + // static_asset_output_dir.clone(), + // asset.to_path_buf(), + // &options, + // ); + + // let finished = assets_finished.fetch_add(1, std::sync::atomic::Ordering::SeqCst); + + // _ = self.progress.unbounded_send(UpdateBuildProgress { + // stage: Stage::OptimizingAssets, + // update: UpdateStage::SetProgress(finished as f64 / asset_count as f64), + // platform: self.target_platform, + // }); + + // Ok(()) as anyhow::Result<()> + // })?; + + // Ok(()) + // } } diff --git a/packages/cli/src/builder/builder.rs b/packages/cli/src/builder/builder.rs new file mode 100644 index 0000000000..a422cdba0c --- /dev/null +++ b/packages/cli/src/builder/builder.rs @@ -0,0 +1,119 @@ +//! The primary interface for building Dioxus apps in parallel + +use crate::builder::BuildRequest; +use crate::builder::{BuildResult, Platform}; +use crate::dioxus_crate::DioxusCrate; +use crate::Result; +use crate::{build::BuildArgs, builder::UpdateBuildProgress}; +use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender}; +use futures_util::StreamExt; +use tokio::task::JoinSet; + +/// A handle to ongoing builds and then the spawned tasks themselves +pub struct Builder { + /// The application we are building + pub krate: DioxusCrate, + + /// Ongoing builds + pub building: JoinSet<(Platform, Result)>, + + /// Messages from the build engine will be sent to this channel + pub channel: ( + UnboundedSender, + UnboundedReceiver, + ), +} + +pub enum BuildUpdate { + Progress(UpdateBuildProgress), + + BuildReady { + target: Platform, + result: BuildResult, + }, + + BuildFailed { + target: Platform, + err: crate::Error, + }, +} + +impl Builder { + /// Create a new builder that can accept multiple simultaneous builds + pub fn new(krate: &DioxusCrate) -> Self { + Self { + channel: futures_channel::mpsc::unbounded(), + krate: krate.clone(), + building: Default::default(), + } + } + + /// Create a new builder and immediately start a build + pub fn start(krate: &DioxusCrate, args: BuildArgs) -> Result { + let mut builder = Self::new(krate); + builder.build(args)?; + Ok(builder) + } + + /// Start a new build - killing the current one if it exists + pub fn build(&mut self, args: BuildArgs) -> Result<()> { + self.abort_all(); + + let mut requests = vec![ + // At least one request for the target app + BuildRequest::new(self.krate.clone(), args.clone(), self.channel.0.clone()), + ]; + + // And then the fullstack app if we're building a fullstack app + if args.fullstack { + super::profiles::initialize_profiles(&self.krate)?; + let server = BuildRequest::new_server(&self.krate, args.clone(), self.tx()); + requests.push(server); + } + + // Queue the builds on the joinset + for build_request in requests { + let platform = build_request.platform(); + self.building.spawn(async move { + // Run the build, but in a protected spawn, ensuring we can't produce panics and thus, joinerrors + let res = tokio::spawn(build_request.build()) + .await + .unwrap_or_else(|err| { + Err(crate::Error::Unique(format!( + "Panic while building project: {err:?}" + ))) + }); + + (platform, res) + }); + } + + Ok(()) + } + + /// Wait for any new updates to the builder - either it completed or gave us a message etc + /// + /// Also listen for any input from the app's handle + pub async fn wait(&mut self) -> BuildUpdate { + tokio::select! { + Some(update) = self.channel.1.next() => BuildUpdate::Progress(update), + Some(Ok((target, result))) = self.building.join_next() => { + match result { + Ok(result) => BuildUpdate::BuildReady { target, result }, + Err(err) => BuildUpdate::BuildFailed { err, target }, + } + } + } + } + + /// Shutdown the current build process + /// + /// todo: might want to use a cancellation token here to allow cleaner shutdowns + pub fn abort_all(&mut self) { + self.building.abort_all(); + } + + fn tx(&self) -> UnboundedSender { + self.channel.0.clone() + } +} diff --git a/packages/cli/src/builder/bundle.rs b/packages/cli/src/builder/bundle.rs index b730386deb..8b13789179 100644 --- a/packages/cli/src/builder/bundle.rs +++ b/packages/cli/src/builder/bundle.rs @@ -1,13 +1 @@ -/// The processed bundle infomrmation -#[derive(Clone)] -pub enum AppBundle { - MacOS, - Ios, - Fullstack, - Spa, - Msi, - Wix, - Deb, - Rpm, - AppImage, -} + diff --git a/packages/cli/src/builder/cargo.rs b/packages/cli/src/builder/cargo.rs index 99d3bea7ba..786a95d946 100644 --- a/packages/cli/src/builder/cargo.rs +++ b/packages/cli/src/builder/cargo.rs @@ -1,34 +1,134 @@ -use super::BuildRequest; -use super::TargetPlatform; -use crate::builder::progress::*; -use crate::config::Platform; +use super::{BuildRequest, BuildResult}; use crate::Result; +use crate::{assets::AssetManifest, builder::progress::*}; +use crate::{builder::Platform, bundler::AppBundle}; use anyhow::Context; use std::fs::create_dir_all; use std::path::PathBuf; use tokio::process::Command; impl BuildRequest { + pub async fn build(self) -> Result { + tracing::info!("🚅 Running build [Desktop] command..."); + + // Install any tooling that might be required for this build. + self.verify_tooling().await?; + + // Run the build command with a pretty loader, returning the executable output location + let executable = self.build_cargo().await?; + + // Extract out the asset manifest from the executable using our linker tricks + let assets = self.collect_assets().await?; + + // Assemble a bundle from everything + let bundle = self.bundle_app(executable, &assets).await?; + + // And then construct a final BuildResult which we can then modify while the app is running + BuildResult::new(self, assets, bundle) + .await + .map_err(Into::into) + } + + pub async fn verify_tooling(&self) -> Result<()> { + // If this is a web, build make sure we have the web build tooling set up + if self.targeting_web() { + self.install_web_build_tooling().await?; + } + + Ok(()) + } + + pub(crate) async fn bundle_app( + &self, + executable: PathBuf, + assets: &AssetManifest, + ) -> Result { + let mut bundle = AppBundle::new(self.platform()); + + bundle.copy_assets(assets); + + // _ = self.progress.unbounded_send(UpdateBuildProgress { + // stage: Stage::OptimizingAssets, + // update: UpdateStage::Start, + // platform: self.target_platform, + // }); + + // self.collect_assets().await?; + + // let file_name = self.krate.executable_name(); + + // // Move the final output executable into the dist folder + // let out_dir = self.target_out_dir(); + // if !out_dir.is_dir() { + // create_dir_all(&out_dir)?; + // } + + // let mut output_path = out_dir.join(file_name); + + // // todo: this should not be platform cfged but rather be a target config + // // we dont always want to set the .exe extension... + // if self.targeting_web() { + // output_path.set_extension("wasm"); + // } else if cfg!(windows) { + // output_path.set_extension("exe"); + // } + + // // if let Some(res_path) = &cargo_build_result.output_location { + // // std::fs::copy(res_path, &output_path)?; + // // } + + // // // Make sure we set the exeutable + // // self.executable = Some(output_path.canonicalize()?); + + // // // And then copy over the asset dir into the bundle + // // // todo: this will eventually become a full bundle step + // // self.copy_assets_dir()?; + + // // If this is a web build, run web post processing steps + // if self.targeting_web() { + // self.post_process_web_build().await?; + // } + + todo!() + } + + /// Get the output directory for a specific built target + pub fn target_out_dir(&self) -> PathBuf { + let out_dir = self.krate.out_dir(); + + todo!() + + // if let Some(Platform::Fullstack) = self.build_arguments.platform { + // match self.platform { + // Platform::Web => out_dir.join("public"), + // Platform::Desktop => out_dir.join("desktop"), + // _ => out_dir, + // } + // } else { + // out_dir + // } + } + /// Create a list of arguments for cargo builds pub(crate) fn build_arguments(&self) -> Vec { let mut cargo_args = Vec::new(); - if self.build_arguments.release { + if self.build.release { cargo_args.push("--release".to_string()); } - if self.build_arguments.verbose { + if self.build.verbose { cargo_args.push("--verbose".to_string()); } else { cargo_args.push("--quiet".to_string()); } - if let Some(custom_profile) = &self.build_arguments.profile { + if let Some(custom_profile) = &self.build.profile { cargo_args.push("--profile".to_string()); cargo_args.push(custom_profile.to_string()); } - if !self.build_arguments.target_args.features.is_empty() { - let features_str = self.build_arguments.target_args.features.join(" "); + if !self.build.target_args.features.is_empty() { + let features_str = self.build.target_args.features.join(" "); cargo_args.push("--features".to_string()); cargo_args.push(features_str); } @@ -36,18 +136,18 @@ impl BuildRequest { if let Some(target) = self .targeting_web() .then_some("wasm32-unknown-unknown") - .or(self.build_arguments.target_args.target.as_deref()) + .or(self.build.target_args.target.as_deref()) { cargo_args.push("--target".to_string()); cargo_args.push(target.to_string()); } - if let Some(ref platform) = self.build_arguments.target_args.package { + if let Some(ref platform) = self.build.target_args.package { cargo_args.push(String::from("-p")); cargo_args.push(platform.clone()); } - cargo_args.append(&mut self.build_arguments.cargo_args.clone()); + cargo_args.append(&mut self.build.cargo_args.clone()); match self.krate.executable_type() { krates::cm::TargetKind::Bin => { @@ -61,125 +161,9 @@ impl BuildRequest { } _ => {} }; + cargo_args.push(self.krate.executable_name().to_string()); cargo_args } - - /// Create a build command for cargo - fn prepare_build_command(&self) -> Result<(Command, Vec)> { - let mut cmd = Command::new("cargo"); - cmd.arg("rustc"); - if let Some(target_dir) = &self.target_dir { - cmd.env("CARGO_TARGET_DIR", target_dir); - } - cmd.current_dir(self.krate.crate_dir()) - .arg("--message-format") - .arg("json-diagnostic-rendered-ansi"); - - let cargo_args = self.build_arguments(); - cmd.args(&cargo_args); - - cmd.arg("--").args(self.rust_flags.clone()); - - Ok((cmd, cargo_args)) - } - - pub(crate) async fn build(&mut self) -> Result<()> { - tracing::info!("🚅 Running build [Desktop] command..."); - - // If this is a web, build make sure we have the web build tooling set up - if self.targeting_web() { - self.install_web_build_tooling().await?; - } - - // Create the build command - let (cmd, cargo_args) = self.prepare_build_command()?; - - // We want to provide helpful data - maybe we can do this earlier? - let crate_count = self.get_unit_count_estimate().await; - - // Run the build command with a pretty loader - let cargo_result = self.build_cargo(crate_count, cmd).await?; - - // Post process the build result - self.post_process_build(cargo_args, &cargo_result) - .await - .context("Failed to post process build")?; - - tracing::info!("🚩 Build completed: [{}]", self.krate.out_dir().display()); - - _ = self.progress.unbounded_send(UpdateBuildProgress { - platform: self.target_platform, - stage: Stage::Finished, - update: UpdateStage::Start, - }); - - Ok(()) - } - - async fn post_process_build( - &mut self, - cargo_args: Vec, - cargo_build_result: &CargoBuildResult, - ) -> Result<()> { - _ = self.progress.unbounded_send(UpdateBuildProgress { - stage: Stage::OptimizingAssets, - update: UpdateStage::Start, - platform: self.target_platform, - }); - - self.collect_assets(cargo_args).await?; - - let file_name = self.krate.executable_name(); - - // Move the final output executable into the dist folder - let out_dir = self.target_out_dir(); - if !out_dir.is_dir() { - create_dir_all(&out_dir)?; - } - - let mut output_path = out_dir.join(file_name); - - // todo: this should not be platform cfged but rather be a target config - // we dont always want to set the .exe extension... - if self.targeting_web() { - output_path.set_extension("wasm"); - } else if cfg!(windows) { - output_path.set_extension("exe"); - } - - if let Some(res_path) = &cargo_build_result.output_location { - std::fs::copy(res_path, &output_path)?; - } - - // Make sure we set the exeutable - self.executable = Some(output_path.canonicalize()?); - - // And then copy over the asset dir into the bundle - // todo: this will eventually become a full bundle step - self.copy_assets_dir()?; - - // If this is a web build, run web post processing steps - if self.targeting_web() { - self.post_process_web_build().await?; - } - - Ok(()) - } - - /// Get the output directory for a specific built target - pub fn target_out_dir(&self) -> PathBuf { - let out_dir = self.krate.out_dir(); - - if let Some(Platform::Fullstack) = self.build_arguments.platform { - match self.target_platform { - TargetPlatform::Web => out_dir.join("public"), - TargetPlatform::Desktop => out_dir.join("desktop"), - _ => out_dir, - } - } else { - out_dir - } - } } diff --git a/packages/cli/src/builder/fullstack.rs b/packages/cli/src/builder/fullstack.rs deleted file mode 100644 index b700205643..0000000000 --- a/packages/cli/src/builder/fullstack.rs +++ /dev/null @@ -1,165 +0,0 @@ -use crate::builder::Build; -use crate::builder::BuildRequest; -use crate::dioxus_crate::DioxusCrate; -use futures_channel::mpsc::UnboundedSender; -use std::io::Write; -use toml_edit::Item; - -use super::{BuildReason, TargetPlatform, UpdateBuildProgress}; - -static CLIENT_PROFILE: &str = "dioxus-client"; -static SERVER_PROFILE: &str = "dioxus-server"; - -impl BuildRequest { - pub(crate) fn new_single( - reason: BuildReason, - krate: DioxusCrate, - arguments: Build, - progress: UnboundedSender, - platform: TargetPlatform, - ) -> Self { - Self { - progress, - reason, - krate, - target_platform: platform, - build_arguments: arguments, - target_dir: Default::default(), - rust_flags: Default::default(), - executable: Default::default(), - assets: Default::default(), - child: None, - } - } - - pub(crate) fn new_fullstack( - config: DioxusCrate, - build_arguments: Build, - serve: BuildReason, - progress: UnboundedSender, - ) -> Result, crate::Error> { - initialize_profiles(&config)?; - - Ok(vec![ - Self::new_client(serve, &config, build_arguments.clone(), progress.clone()), - Self::new_server(serve, &config, build_arguments, progress), - ]) - } - - fn new_with_target_directory_rust_flags_and_features( - serve: BuildReason, - config: &DioxusCrate, - build: &Build, - feature: Option, - target_platform: TargetPlatform, - progress: UnboundedSender, - ) -> Self { - let config = config.clone(); - let mut build = build.clone(); - - // Add the server feature to the features we pass to the build - if let Some(feature) = feature { - build.target_args.features.push(feature); - } - - // Add the server flags to the build arguments - Self { - reason: serve, - build_arguments: build.clone(), - krate: config, - rust_flags: Default::default(), - target_dir: None, - target_platform, - executable: None, - assets: Default::default(), - progress, - child: None, - } - } - - fn new_server( - serve: BuildReason, - config: &DioxusCrate, - mut build: Build, - progress: UnboundedSender, - ) -> Self { - if build.profile.is_none() { - build.profile = Some(CLIENT_PROFILE.to_string()); - } - let client_feature = build.auto_detect_server_feature(config); - Self::new_with_target_directory_rust_flags_and_features( - serve, - config, - &build, - build.target_args.server_feature.clone().or(client_feature), - TargetPlatform::Server, - progress, - ) - } - - fn new_client( - serve: BuildReason, - config: &DioxusCrate, - mut build: Build, - progress: UnboundedSender, - ) -> Self { - if build.profile.is_none() { - build.profile = Some(SERVER_PROFILE.to_string()); - } - let (client_feature, client_platform) = build.auto_detect_client_platform(config); - Self::new_with_target_directory_rust_flags_and_features( - serve, - config, - &build, - build.target_args.client_feature.clone().or(client_feature), - client_platform, - progress, - ) - } -} - -// The `opt-level=2` increases build times, but can noticeably decrease time -// between saving changes and being able to interact with an app. The "overall" -// time difference (between having and not having the optimization) can be -// almost imperceptible (~1 s) but also can be very noticeable (~6 s) — depends -// on setup (hardware, OS, browser, idle load). -// Find or create the client and server profiles in the .cargo/config.toml file -fn initialize_profiles(config: &DioxusCrate) -> crate::Result<()> { - let config_path = config.workspace_dir().join(".cargo/config.toml"); - let mut config = match std::fs::read_to_string(&config_path) { - Ok(config) => config.parse::().map_err(|e| { - crate::Error::Other(anyhow::anyhow!("Failed to parse .cargo/config.toml: {}", e)) - })?, - Err(_) => Default::default(), - }; - - if let Item::Table(table) = config - .as_table_mut() - .entry("profile") - .or_insert(Item::Table(Default::default())) - { - if let toml_edit::Entry::Vacant(entry) = table.entry(CLIENT_PROFILE) { - let mut client = toml_edit::Table::new(); - client.insert("inherits", Item::Value("dev".into())); - client.insert("opt-level", Item::Value(2.into())); - entry.insert(Item::Table(client)); - } - - if let toml_edit::Entry::Vacant(entry) = table.entry(SERVER_PROFILE) { - let mut server = toml_edit::Table::new(); - server.insert("inherits", Item::Value("dev".into())); - server.insert("opt-level", Item::Value(2.into())); - entry.insert(Item::Table(server)); - } - } - - // Write the config back to the file - if let Some(parent) = config_path.parent() { - std::fs::create_dir_all(parent)?; - } - let file = std::fs::File::create(config_path)?; - let mut buf_writer = std::io::BufWriter::new(file); - write!(buf_writer, "{}", config)?; - - Ok(()) -} diff --git a/packages/cli/src/builder/mod.rs b/packages/cli/src/builder/mod.rs index 9a28b09bd8..b3efde8ed0 100644 --- a/packages/cli/src/builder/mod.rs +++ b/packages/cli/src/builder/mod.rs @@ -1,138 +1,28 @@ -use crate::build::Build; -use crate::config::Platform; -use crate::Result; -use crate::{assets::AssetManifest, dioxus_crate::DioxusCrate}; -use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender}; -use futures_util::StreamExt; -pub use platform::TargetPlatform; -use std::path::PathBuf; +/// The primary entrypoint for our build + optimize + bundle engine +/// +/// Handles multiple ongoing tasks and allows you to queue up builds from interactive and non-interactive contexts +/// +/// Uses a request -> response architecture that allows you to monitor the progress with an optional message +/// receiver. +mod builder; +mod request; +mod result; mod assets; mod bundle; mod cargo; -mod fullstack; mod handle; mod platform; mod prepare_html; +mod profiles; mod progress; mod web; -pub use progress::{ - BuildMessage, MessageSource, MessageType, Stage, UpdateBuildProgress, UpdateStage, -}; - -/// An app that's built, bundled, processed, and a handle to its running app, if it exists -/// -/// As the build progresses, we'll fill in fields like assets, executable, entitlements, etc -/// -/// If the app needs to be bundled, we'll add the bundle info here too -pub struct BuildRequest { - /// Whether the build is for serving the application - pub reason: BuildReason, - - /// The configuration for the crate we are building - pub krate: DioxusCrate, - - /// The target platform for the build - pub target_platform: TargetPlatform, - - /// The arguments for the build - pub build_arguments: Build, - - /// The rustc flags to pass to the build - pub rust_flags: Vec, - - /// The target directory for the build - pub target_dir: Option, - - /// The output executable location - pub executable: Option, - - /// The assets manifest - starts empty and will be populated as we go - pub assets: AssetManifest, - - /// The child process of this running app that has yet to be spawned. - /// - /// We might need to finangle this into something else - pub child: Option, - - /// Status channel to send our progress updates to - pub progress: UnboundedSender, -} - -/// The reason for the build - this will determine how we prep the output -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub enum BuildReason { - Serve, - Build, - Bundle, -} - -impl BuildRequest { - pub fn create( - serve: BuildReason, - dioxus_crate: &DioxusCrate, - build_arguments: impl Into, - progress: UnboundedSender, - ) -> crate::Result> { - let build_arguments: Build = build_arguments.into(); - - let single_platform = |platform| { - let req = Self::new_single( - serve, - dioxus_crate.clone(), - build_arguments.clone(), - progress.clone(), - platform, - ); - - Ok(vec![req]) - }; - - match build_arguments.platform() { - Platform::Liveview => single_platform(TargetPlatform::Liveview), - Platform::Web => single_platform(TargetPlatform::Web), - Platform::Desktop => single_platform(TargetPlatform::Desktop), - Platform::Mobile => single_platform(TargetPlatform::Mobile), - Platform::Fullstack => { - Self::new_fullstack(dioxus_crate.clone(), build_arguments, serve, progress) - } - } - } - - pub(crate) async fn build_all_parallel( - build_requests: Vec, - mut rx: UnboundedReceiver, - ) -> Result> { - let multi_platform_build = build_requests.len() > 1; - // let mut set = tokio::task::JoinSet::new(); - - // for build_request in build_requests { - // set.spawn(async move { build_request.build().await }); - // } - - // // Watch the build progress as it comes in - // while let Some(update) = rx.next().await { - // if multi_platform_build { - // let platform = update.platform; - // print!("{platform} build: "); - // update.to_std_out(); - // } else { - // update.to_std_out(); - // } - // } - - todo!() - - // let mut all_results = Vec::new(); - - // while let Some(result) = set.join_next().await { - // all_results.push( - // result - // .map_err(|_| crate::Error::Unique("Failed to build project".to_owned()))??, - // ); - // } +use crate::Result; +use crate::{assets::AssetManifest, dioxus_crate::DioxusCrate}; - // Ok(all_results) - } -} +use crate::build::BuildArgs; +pub use builder::*; +pub use platform::*; +pub use request::*; +pub use result::*; diff --git a/packages/cli/src/builder/platform.rs b/packages/cli/src/builder/platform.rs index e9bfd4a3f3..ce4f2d5167 100644 --- a/packages/cli/src/builder/platform.rs +++ b/packages/cli/src/builder/platform.rs @@ -1,48 +1,112 @@ +use serde::{Deserialize, Serialize}; +use std::fmt::Display; use std::str::FromStr; -/// The target platform for the build -/// This is very similar to the Platform enum, but we need to be able to differentiate between the -/// server and web targets for the fullstack platform -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum TargetPlatform { +#[derive( + Copy, + Clone, + Hash, + PartialEq, + Eq, + PartialOrd, + Ord, + Serialize, + Deserialize, + Debug, + Default, + clap::ValueEnum, +)] +#[non_exhaustive] +pub enum Platform { + /// Targeting the web platform using WASM + #[clap(name = "web")] + #[serde(rename = "web")] + #[default] Web, + + /// Targeting the desktop platform using Tao/Wry-based webview + /// + /// Will only build for your native architecture - to do cross builds you need to use a VM. + /// Read more about cross-builds on the Dioxus Website. + #[clap(name = "desktop")] + #[serde(rename = "desktop")] Desktop, - Mobile, + + /// Targeting the ios platform + /// + /// Can't work properly if you're not building from an Apple device. + #[clap(name = "ios")] + #[serde(rename = "ios")] + Ios, + + /// Targetting the server platform using Axum and Dioxus-Fullstack + /// + /// This is implicitly passed if `fullstack` is enabled as a feature. Using this variant simply + /// means you're only building the server variant without the `.wasm` to serve. + #[clap(name = "server")] + #[serde(rename = "server")] Server, + + /// Targeting the android platform + #[clap(name = "android")] + #[serde(rename = "android")] + Android, + + /// Targeting the static generation platform using SSR and Dioxus-Fullstack + #[clap(name = "liveview")] + #[serde(rename = "liveview")] Liveview, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum TargetArch { - Linux, - Mac, - Windows, - Ios, - Android, +/// An error that occurs when a platform is not recognized +pub struct UnknownPlatformError; + +impl std::fmt::Display for UnknownPlatformError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Unknown platform") + } } -impl FromStr for TargetPlatform { - type Err = (); +impl FromStr for Platform { + type Err = UnknownPlatformError; fn from_str(s: &str) -> Result { match s { "web" => Ok(Self::Web), "desktop" => Ok(Self::Desktop), - "axum" | "server" => Ok(Self::Server), "liveview" => Ok(Self::Liveview), - _ => Err(()), + _ => Err(UnknownPlatformError), } } } -impl std::fmt::Display for TargetPlatform { +impl Display for Platform { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let feature = self.feature_name(); + f.write_str(feature) + } +} + +impl Platform { + /// All platforms the dioxus CLI supports + pub const ALL: &'static [Self] = &[ + Platform::Web, + Platform::Desktop, + Platform::Ios, + Platform::Android, + Platform::Liveview, + Platform::Server, + ]; + + /// Get the feature name for the platform in the dioxus crate + pub fn feature_name(&self) -> &str { match self { - TargetPlatform::Web => write!(f, "web"), - TargetPlatform::Desktop => write!(f, "desktop"), - TargetPlatform::Server => write!(f, "server"), - TargetPlatform::Liveview => write!(f, "liveview"), - TargetPlatform::Mobile => write!(f, "ios"), + Platform::Web => "web", + Platform::Desktop => "desktop", + Platform::Liveview => "liveview", + Platform::Ios => "ios", + Platform::Android => "android", + Platform::Server => "server", } } } diff --git a/packages/cli/src/builder/prepare_html.rs b/packages/cli/src/builder/prepare_html.rs index 95d9119c7d..6b4b1d6c18 100644 --- a/packages/cli/src/builder/prepare_html.rs +++ b/packages/cli/src/builder/prepare_html.rs @@ -1,6 +1,6 @@ //! Build the HTML file to load a web application. The index.html file may be created from scratch or modified from the `index.html` file in the crate root. -use super::{BuildReason, BuildRequest, UpdateBuildProgress}; +use super::{BuildRequest, UpdateBuildProgress}; use crate::builder::progress::MessageSource; use crate::builder::Stage; use crate::Result; @@ -13,7 +13,7 @@ const DEFAULT_HTML: &str = include_str!("../../assets/index.html"); const TOAST_HTML: &str = include_str!("../../assets/toast.html"); impl BuildRequest { - pub(crate) fn prepare_html(&mut self) -> Result { + pub(crate) fn prepare_html(&self) -> Result { let mut html = html_or_default(&self.krate.crate_dir()); // Inject any resources from the config into the html @@ -33,11 +33,12 @@ impl BuildRequest { } fn is_dev_build(&self) -> bool { - self.reason == BuildReason::Serve && !self.build_arguments.release + todo!() + // self.reason == BuildReason::Serve && !self.build_arguments.release } // Inject any resources from the config into the html - fn inject_resources(&mut self, html: &mut String) -> Result<()> { + fn inject_resources(&self, html: &mut String) -> Result<()> { // Collect all resources into a list of styles and scripts let resources = &self.krate.dioxus_config.web.resource; let mut style_list = resources.style.clone().unwrap_or_default(); @@ -86,9 +87,9 @@ impl BuildRequest { } /// Inject loading scripts if they are not already present - fn inject_loading_scripts(&mut self, html: &mut String) { + fn inject_loading_scripts(&self, html: &mut String) { // If it looks like we are already loading wasm or the current build opted out of injecting loading scripts, don't inject anything - if !self.build_arguments.inject_loading_scripts || html.contains("__wbindgen_start") { + if !self.build.inject_loading_scripts || html.contains("__wbindgen_start") { return; } @@ -135,7 +136,7 @@ impl BuildRequest { *html = html.replace("{app_name}", app_name); } - fn send_resource_deprecation_warning(&mut self, paths: Vec, variant: ResourceType) { + fn send_resource_deprecation_warning(&self, paths: Vec, variant: ResourceType) { const RESOURCE_DEPRECATION_MESSAGE: &str = r#"The `web.resource` config has been deprecated in favor of head components and will be removed in a future release. Instead of including assets in the config, you can include assets with the `asset!` macro and add them to the head with `document::Link` and `Script` components."#; let replacement_components = paths @@ -177,7 +178,7 @@ impl BuildRequest { ); _ = self.progress.unbounded_send(UpdateBuildProgress { - platform: self.target_platform, + platform: self.platform(), stage: Stage::OptimizingWasm, update: super::UpdateStage::AddMessage(super::BuildMessage { level: Level::WARN, diff --git a/packages/cli/src/builder/profiles.rs b/packages/cli/src/builder/profiles.rs new file mode 100644 index 0000000000..a4068505dc --- /dev/null +++ b/packages/cli/src/builder/profiles.rs @@ -0,0 +1,57 @@ +use crate::builder::BuildArgs; +use crate::builder::BuildRequest; +use crate::dioxus_crate::DioxusCrate; +use futures_channel::mpsc::UnboundedSender; +use std::io::Write; +use toml_edit::Item; + +use super::{Platform, UpdateBuildProgress}; + +pub static CLIENT_PROFILE: &str = "dioxus-client"; +pub static SERVER_PROFILE: &str = "dioxus-server"; + +// The `opt-level=2` increases build times, but can noticeably decrease time +// between saving changes and being able to interact with an app. The "overall" +// time difference (between having and not having the optimization) can be +// almost imperceptible (~1 s) but also can be very noticeable (~6 s) — depends +// on setup (hardware, OS, browser, idle load). +// Find or create the client and server profiles in the .cargo/config.toml file +pub fn initialize_profiles(krate: &DioxusCrate) -> crate::Result<()> { + let config_path = krate.workspace_dir().join(".cargo/config.toml"); + let mut config = match std::fs::read_to_string(&config_path) { + Ok(config) => config.parse::().map_err(|e| { + crate::Error::Other(anyhow::anyhow!("Failed to parse .cargo/config.toml: {}", e)) + })?, + Err(_) => Default::default(), + }; + + if let Item::Table(table) = config + .as_table_mut() + .entry("profile") + .or_insert(Item::Table(Default::default())) + { + if let toml_edit::Entry::Vacant(entry) = table.entry(CLIENT_PROFILE) { + let mut client = toml_edit::Table::new(); + client.insert("inherits", Item::Value("dev".into())); + client.insert("opt-level", Item::Value(2.into())); + entry.insert(Item::Table(client)); + } + + if let toml_edit::Entry::Vacant(entry) = table.entry(SERVER_PROFILE) { + let mut server = toml_edit::Table::new(); + server.insert("inherits", Item::Value("dev".into())); + server.insert("opt-level", Item::Value(2.into())); + entry.insert(Item::Table(server)); + } + } + + // Write the config back to the file + if let Some(parent) = config_path.parent() { + std::fs::create_dir_all(parent)?; + } + let file = std::fs::File::create(config_path)?; + let mut buf_writer = std::io::BufWriter::new(file); + write!(buf_writer, "{}", config)?; + + Ok(()) +} diff --git a/packages/cli/src/builder/progress.rs b/packages/cli/src/builder/progress.rs index 6c6e6fd786..1a912f9adb 100644 --- a/packages/cli/src/builder/progress.rs +++ b/packages/cli/src/builder/progress.rs @@ -1,4 +1,5 @@ //! Report progress about the build to the user. We use channels to report progress back to the CLI. +use super::{BuildRequest, Platform}; use anyhow::Context; use cargo_metadata::{diagnostic::Diagnostic, Message}; use serde::Deserialize; @@ -6,11 +7,9 @@ use std::fmt::Display; use std::ops::Deref; use std::path::PathBuf; use std::process::Stdio; -use tokio::io::AsyncBufReadExt; +use tokio::{io::AsyncBufReadExt, process::Command}; use tracing::Level; -use super::{BuildRequest, TargetPlatform}; - #[derive(Default, Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Copy)] pub enum Stage { #[default] @@ -47,7 +46,7 @@ impl std::fmt::Display for Stage { pub struct UpdateBuildProgress { pub stage: Stage, pub update: UpdateStage, - pub platform: TargetPlatform, + pub platform: Platform, } impl UpdateBuildProgress { @@ -141,22 +140,35 @@ impl From for BuildMessage { } } -pub(crate) struct CargoBuildResult { - pub(crate) output_location: Option, -} - impl BuildRequest { - pub(crate) async fn build_cargo( - &self, - crate_count: usize, - mut cmd: tokio::process::Command, - ) -> anyhow::Result { + /// Run `cargo`, returning the location of the final exectuable + /// + /// todo: add some stats here, like timing reports, crate-graph optimizations, etc + pub async fn build_cargo(&self) -> anyhow::Result { + // Extract the unit count of the crate graph so build_cargo has more accurate data + let crate_count = self.get_unit_count_estimate().await; + _ = self.progress.unbounded_send(UpdateBuildProgress { stage: Stage::Compiling, update: UpdateStage::Start, - platform: self.target_platform, + platform: self.platform(), }); + let mut cmd = Command::new("cargo"); + + cmd.arg("rustc") + .envs( + self.custom_target_dir + .as_ref() + .map(|dir| ("CARGO_TARGET_DIR", dir)), + ) + .current_dir(self.krate.crate_dir()) + .arg("--message-format") + .arg("json-diagnostic-rendered-ansi") + .args(&self.build_arguments()) + .arg("--") + .args(self.rust_flags.clone()); + let mut child = cmd .stdout(Stdio::piped()) .stderr(Stdio::piped()) @@ -196,7 +208,7 @@ impl BuildRequest { _ = self.progress.unbounded_send(UpdateBuildProgress { stage: Stage::Compiling, update: UpdateStage::AddMessage(message.clone().into()), - platform: self.target_platform, + platform: self.platform(), }); const WARNING_LEVELS: &[cargo_metadata::diagnostic::DiagnosticLevel] = &[ cargo_metadata::diagnostic::DiagnosticLevel::Help, @@ -227,7 +239,7 @@ impl BuildRequest { } else { let build_progress = units_compiled as f64 / crate_count as f64; _ = self.progress.unbounded_send(UpdateBuildProgress { - platform: self.target_platform, + platform: self.platform(), stage: Stage::Compiling, update: UpdateStage::SetProgress((build_progress).clamp(0.0, 1.00)), }); @@ -243,7 +255,7 @@ impl BuildRequest { } Message::TextLine(line) => { _ = self.progress.unbounded_send(UpdateBuildProgress { - platform: self.target_platform, + platform: self.platform(), stage: Stage::Compiling, update: UpdateStage::AddMessage(BuildMessage { level: Level::DEBUG, @@ -258,7 +270,7 @@ impl BuildRequest { } } - Ok(CargoBuildResult { output_location }) + output_location.context("Build did not return an executable") } /// Try to get the unit graph for the crate. This is a nightly only feature which may not be available with the current version of rustc the user has installed. @@ -308,3 +320,15 @@ impl BuildRequest { }) } } + +impl BuildRequest { + pub fn status_build_finished(&self) { + tracing::info!("🚩 Build completed: [{}]", self.krate.out_dir().display()); + + _ = self.progress.unbounded_send(UpdateBuildProgress { + platform: self.platform(), + stage: Stage::Finished, + update: UpdateStage::Start, + }); + } +} diff --git a/packages/cli/src/builder/request.rs b/packages/cli/src/builder/request.rs new file mode 100644 index 0000000000..5d991cc828 --- /dev/null +++ b/packages/cli/src/builder/request.rs @@ -0,0 +1,171 @@ +use super::profiles::*; +use super::BuildResult; +use crate::build::BuildArgs; +use crate::builder::Platform; +use crate::Result; +use crate::{assets::AssetManifest, dioxus_crate::DioxusCrate}; +use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender}; +use futures_util::StreamExt; +use std::path::PathBuf; + +pub use super::progress::{ + BuildMessage, MessageSource, MessageType, Stage, UpdateBuildProgress, UpdateStage, +}; + +/// An app that's built, bundled, processed, and a handle to its running app, if it exists +/// +/// As the build progresses, we'll fill in fields like assets, executable, entitlements, etc +/// +/// If the app needs to be bundled, we'll add the bundle info here too +pub struct BuildRequest { + /// The configuration for the crate we are building + pub krate: DioxusCrate, + + /// The arguments for the build + pub build: BuildArgs, + + /// The rustc flags to pass to the build + pub rust_flags: Vec, + + /// The target directory for the build + pub custom_target_dir: Option, + + /// Status channel to send our progress updates to + pub progress: UnboundedSender, +} + +impl BuildRequest { + pub fn new( + krate: DioxusCrate, + build_arguments: impl Into, + progress: UnboundedSender, + ) -> Self { + let build: BuildArgs = build_arguments.into(); + + Self { + progress, + build, + krate, + custom_target_dir: Default::default(), + rust_flags: Default::default(), + } + } + + fn new_with_target_directory_rust_flags_and_features( + krate: &DioxusCrate, + build: &BuildArgs, + feature: Option, + target_platform: Platform, + progress: UnboundedSender, + ) -> Self { + let config = krate.clone(); + let mut build = build.clone(); + + // Add the server feature to the features we pass to the build + if let Some(feature) = feature { + build.target_args.features.push(feature); + } + + // Add the server flags to the build arguments + Self { + build: build.clone(), + krate: config, + rust_flags: Default::default(), + custom_target_dir: None, + progress, + } + } + + pub fn new_server( + krate: &DioxusCrate, + mut build: BuildArgs, + progress: UnboundedSender, + ) -> Self { + if build.profile.is_none() { + build.profile = Some(CLIENT_PROFILE.to_string()); + } + let client_feature = build.auto_detect_server_feature(krate); + Self::new_with_target_directory_rust_flags_and_features( + krate, + &build, + build.target_args.server_feature.clone().or(client_feature), + Platform::Server, + progress, + ) + } + + pub fn new_client( + krate: &DioxusCrate, + mut build: BuildArgs, + progress: UnboundedSender, + ) -> Self { + if build.profile.is_none() { + build.profile = Some(SERVER_PROFILE.to_string()); + } + let (client_feature, client_platform) = build.auto_detect_client_platform(krate); + Self::new_with_target_directory_rust_flags_and_features( + krate, + &build, + build.target_args.client_feature.clone().or(client_feature), + client_platform, + progress, + ) + } + + pub(crate) async fn build_all_parallel( + build_requests: Vec, + mut rx: UnboundedReceiver, + ) -> Result> { + let multi_platform_build = build_requests.len() > 1; + // let mut set = tokio::task::JoinSet::new(); + + // for build_request in build_requests { + // set.spawn(async move { build_request.build().await }); + // } + + // // Watch the build progress as it comes in + // while let Some(update) = rx.next().await { + // if multi_platform_build { + // let platform = update.platform; + // print!("{platform} build: "); + // update.to_std_out(); + // } else { + // update.to_std_out(); + // } + // } + + todo!() + + // let mut all_results = Vec::new(); + + // while let Some(result) = set.join_next().await { + // all_results.push( + // result + // .map_err(|_| crate::Error::Unique("Failed to build project".to_owned()))??, + // ); + // } + + // Ok(all_results) + } + + pub(crate) fn new_single( + krate: DioxusCrate, + arguments: BuildArgs, + progress: UnboundedSender, + ) -> Self { + Self { + progress, + krate, + build: arguments, + custom_target_dir: Default::default(), + rust_flags: Default::default(), + } + } + + /// Get the platform for this build + pub fn platform(&self) -> Platform { + self.build + .platform + .unwrap_or_else(|| self.krate.dioxus_config.application.default_platform) + } +} diff --git a/packages/cli/src/builder/result.rs b/packages/cli/src/builder/result.rs new file mode 100644 index 0000000000..7e7f7d598f --- /dev/null +++ b/packages/cli/src/builder/result.rs @@ -0,0 +1,40 @@ +use crate::bundler::AppBundle; +use anyhow::Context; +use std::path::PathBuf; + +use super::*; + +pub struct BuildResult { + /// Initial request that built this result + pub request: BuildRequest, + + /// The output bundle + pub bundle: AppBundle, + + /// The assets manifest + pub assets: AssetManifest, + + /// The child process of this running app that has yet to be spawned. + /// + /// We might need to finangle this into something else + pub child: Option, +} + +impl BuildResult { + pub async fn new( + request: BuildRequest, + assets: AssetManifest, + bundle: AppBundle, + ) -> anyhow::Result { + let mut res = Self { + request, + assets, + bundle, + child: None, + }; + + Ok(res) + } + + pub fn hotreload_asset(&mut self) {} +} diff --git a/packages/cli/src/builder/web.rs b/packages/cli/src/builder/web.rs index c213f9a979..4bcb1b1db0 100644 --- a/packages/cli/src/builder/web.rs +++ b/packages/cli/src/builder/web.rs @@ -1,20 +1,63 @@ -use super::{BuildRequest, TargetPlatform}; +use super::{BuildRequest, Platform}; use crate::assets::pre_compress_folder; use crate::builder::progress::Stage; use crate::builder::progress::UpdateBuildProgress; use crate::builder::progress::UpdateStage; use crate::error::{Error, Result}; -use std::path::Path; +use std::path::{Path, PathBuf}; use tokio::process::Command; use wasm_bindgen_cli_support::Bindgen; impl BuildRequest { + /// Post process the WASM build artifacts + pub(crate) async fn post_process_web_build(&self, executable: PathBuf) -> Result<()> { + _ = self.progress.unbounded_send(UpdateBuildProgress { + stage: Stage::OptimizingWasm, + update: UpdateStage::Start, + platform: self.platform(), + }); + + // Find the wasm file + let output_location = executable.clone(); + let input_path = output_location.with_extension("wasm"); + + // Create the directory where the bindgen output will be placed + let bindgen_outdir = self.target_out_dir().join("assets").join("dioxus"); + + // Run wasm-bindgen + self.run_wasm_bindgen(&input_path, &bindgen_outdir).await?; + + // Only run wasm-opt if the feature is enabled + // Wasm-opt has an expensive build script that makes it annoying to keep enabled for iterative dev + // We put it behind the "wasm-opt" feature flag so that it can be disabled when iterating on the cli + #[cfg(feature = "wasm-opt")] + self.run_wasm_opt(&bindgen_outdir)?; + + // If pre-compressing is enabled, we can pre_compress the wasm-bindgen output + let pre_compress = self + .krate + .should_pre_compress_web_assets(self.build.release); + + tokio::task::spawn_blocking(move || pre_compress_folder(&bindgen_outdir, pre_compress)) + .await + .unwrap()?; + + // Create the index.html file + // Note that we do this last since the webserver will attempt to serve the index.html file + // If we do this too early, the wasm won't be ready but the index.html will be served, leading + // to test failures and broken pages. + let html = self.prepare_html()?; + let html_path = self.target_out_dir().join("index.html"); + std::fs::write(html_path, html)?; + + Ok(()) + } + async fn run_wasm_bindgen(&self, input_path: &Path, bindgen_outdir: &Path) -> Result<()> { tracing::info!("Running wasm-bindgen"); let input_path = input_path.to_path_buf(); let bindgen_outdir = bindgen_outdir.to_path_buf(); - let keep_debug = - self.krate.dioxus_config.web.wasm_opt.debug || (!self.build_arguments.release); + let keep_debug = self.krate.dioxus_config.web.wasm_opt.debug || (!self.build.release); let name = self.krate.dioxus_config.application.name.clone(); let run_wasm_bindgen = move || { @@ -52,50 +95,6 @@ impl BuildRequest { Ok(()) } - /// Post process the WASM build artifacts - pub(crate) async fn post_process_web_build(&mut self) -> Result<()> { - _ = self.progress.unbounded_send(UpdateBuildProgress { - stage: Stage::OptimizingWasm, - update: UpdateStage::Start, - platform: self.target_platform, - }); - - // Find the wasm file - let output_location = self.executable.clone().unwrap(); - let input_path = output_location.with_extension("wasm"); - - // Create the directory where the bindgen output will be placed - let bindgen_outdir = self.target_out_dir().join("assets").join("dioxus"); - - // Run wasm-bindgen - self.run_wasm_bindgen(&input_path, &bindgen_outdir).await?; - - // Only run wasm-opt if the feature is enabled - // Wasm-opt has an expensive build script that makes it annoying to keep enabled for iterative dev - // We put it behind the "wasm-opt" feature flag so that it can be disabled when iterating on the cli - #[cfg(feature = "wasm-opt")] - self.run_wasm_opt(&bindgen_outdir)?; - - // If pre-compressing is enabled, we can pre_compress the wasm-bindgen output - let pre_compress = self - .krate - .should_pre_compress_web_assets(self.build_arguments.release); - - tokio::task::spawn_blocking(move || pre_compress_folder(&bindgen_outdir, pre_compress)) - .await - .unwrap()?; - - // Create the index.html file - // Note that we do this last since the webserver will attempt to serve the index.html file - // If we do this too early, the wasm won't be ready but the index.html will be served, leading - // to test failures and broken pages. - let html = self.prepare_html()?; - let html_path = self.target_out_dir().join("index.html"); - std::fs::write(html_path, html)?; - - Ok(()) - } - /// Check if the wasm32-unknown-unknown target is installed and try to install it if not pub(crate) async fn install_web_build_tooling(&self) -> Result<()> { // If the user has rustup, we can check if the wasm32-unknown-unknown target is installed @@ -107,7 +106,7 @@ impl BuildRequest { _ = self.progress.unbounded_send(UpdateBuildProgress { stage: Stage::InstallingWasmTooling, update: UpdateStage::Start, - platform: self.target_platform, + platform: self.platform(), }); tracing::info!("wasm32-unknown-unknown target not detected, installing.."); let _ = Command::new("rustup") @@ -195,6 +194,6 @@ impl BuildRequest { /// Check if the build is targeting the web platform pub fn targeting_web(&self) -> bool { - self.target_platform == TargetPlatform::Web + self.platform() == Platform::Web } } diff --git a/packages/cli/src/bundler.rs b/packages/cli/src/bundler.rs new file mode 100644 index 0000000000..0263d7ac11 --- /dev/null +++ b/packages/cli/src/bundler.rs @@ -0,0 +1,8 @@ +mod android; +mod ios; +mod mac; +mod web; +mod win; + +mod app; +pub use app::*; diff --git a/packages/cli/src/bundler/android.rs b/packages/cli/src/bundler/android.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/cli/src/bundler/app.rs b/packages/cli/src/bundler/app.rs new file mode 100644 index 0000000000..f4f1e9077f --- /dev/null +++ b/packages/cli/src/bundler/app.rs @@ -0,0 +1,43 @@ +use std::path::PathBuf; + +use crate::{assets::AssetManifest, builder::Platform}; + +pub struct AppBundle {} + +impl AppBundle { + pub fn new(platform: Platform) -> Self { + todo!() + } + + pub fn set_main_executable(&mut self) {} + + /// Copy the assets out of the manifest and into the target location + pub async fn copy_assets(&mut self, manifest: &AssetManifest) { + todo!() + } + + pub fn finish(self) -> Self { + todo!() + } + + pub fn open(&self) {} + + /// Get the path to the executable + pub fn path(&self) -> PathBuf { + todo!() + } +} + +/// The processed bundle infomrmation +#[derive(Clone)] +pub enum BundlePlatform { + MacOS, + Ios, + Fullstack, + Spa, + Msi, + Wix, + Deb, + Rpm, + AppImage, +} diff --git a/packages/cli/src/bundler/deb.rs b/packages/cli/src/bundler/deb.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/cli/src/bundler/ios.rs b/packages/cli/src/bundler/ios.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/cli/src/bundler/mac.rs b/packages/cli/src/bundler/mac.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/cli/src/bundler/web.rs b/packages/cli/src/bundler/web.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/cli/src/bundler/win.rs b/packages/cli/src/bundler/win.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/cli/src/cli/build.rs b/packages/cli/src/cli/build.rs index 1f81308e98..40891cee71 100644 --- a/packages/cli/src/cli/build.rs +++ b/packages/cli/src/cli/build.rs @@ -1,18 +1,15 @@ -use std::str::FromStr; - -use crate::{builder::TargetPlatform, config::Platform}; -use crate::{ - builder::{BuildReason, BuildRequest}, - dioxus_crate::DioxusCrate, -}; -use anyhow::Context; - use super::*; +use crate::builder::Platform; +use crate::dioxus_crate::DioxusCrate; +use anyhow::Context; +use std::str::FromStr; /// Build the Rust Dioxus app and all of its assets. +/// +/// Produces a final output bundle designed to be run on the target platform. #[derive(Clone, Debug, Default, Deserialize, Parser)] #[clap(name = "build")] -pub struct Build { +pub struct BuildArgs { /// Build in release mode [default: false] #[clap(long, short)] #[serde(default)] @@ -41,6 +38,16 @@ pub struct Build { #[clap(long, value_enum)] pub platform: Option, + /// Build the fullstack variant of this app, using that as the fileserver and backend + /// + /// This defaults to `false` but will be overriden to true if the `fullstack` feature is enabled. + #[clap(long)] + pub fullstack: bool, + + /// Run the ssg config of the app and generate the files + #[clap(long)] + pub ssg: bool, + /// Skip collecting assets from dependencies [default: false] #[clap(long)] #[serde(default)] @@ -90,17 +97,29 @@ pub struct TargetArgs { #[clap(long)] pub server_feature: Option, + /// The architecture to build for [default: "native"] + /// + /// Can either be `arm | arm64 | x86 | x86_64 | native` + #[clap(long)] + pub arch: Option, + /// Rustc platform triple #[clap(long)] pub target: Option, } -impl Build { +impl BuildArgs { + /// Update the arguments of the CLI by inspecting the DioxusCrate itself and learning about how + /// the user has configured their app. + /// + /// IE if they've specified "fullstack" as a feature on `dioxus`, then we want to build the + /// fullstack variant even if they omitted the `--fullstack` flag. pub fn resolve(&mut self, dioxus_crate: &mut DioxusCrate) -> Result<()> { // Inherit the platform from the defaults let platform = self .platform .unwrap_or_else(|| self.auto_detect_platform(dioxus_crate)); + self.platform = Some(platform); // Add any features required to turn on the platform we are building for @@ -113,11 +132,18 @@ impl Build { pub async fn build(&mut self, dioxus_crate: &mut DioxusCrate) -> Result<()> { self.resolve(dioxus_crate)?; - let (tx, rx) = futures_channel::mpsc::unbounded(); - let build_requests = - BuildRequest::create(BuildReason::Build, dioxus_crate, self.clone(), tx)?; - BuildRequest::build_all_parallel(build_requests, rx).await?; + let mut builder = crate::builder::Builder::new(dioxus_crate); + + // if self.fullstack { + // builder.build_fullstack(dioxus_crate)?; + // } + + // let (tx, rx) = futures_channel::mpsc::unbounded(); + + // let build_requests = BuildRequest::create(dioxus_crate, self.clone(), tx)?; + // BuildRequest::build_all_parallel(build_requests, rx).await?; + Ok(()) } @@ -131,19 +157,17 @@ impl Build { pub(crate) fn auto_detect_client_platform( &self, resolved: &DioxusCrate, - ) -> (Option, TargetPlatform) { + ) -> (Option, Platform) { self.find_dioxus_feature(resolved, |platform| { - matches!(platform, TargetPlatform::Web | TargetPlatform::Desktop) + matches!(platform, Platform::Web | Platform::Desktop) }) - .unwrap_or_else(|| (Some("web".to_string()), TargetPlatform::Web)) + .unwrap_or_else(|| (Some("web".to_string()), Platform::Web)) } pub(crate) fn auto_detect_server_feature(&self, resolved: &DioxusCrate) -> Option { - self.find_dioxus_feature(resolved, |platform| { - matches!(platform, TargetPlatform::Server) - }) - .map(|(feature, _)| feature) - .unwrap_or_else(|| Some("server".to_string())) + self.find_dioxus_feature(resolved, |platform| matches!(platform, Platform::Server)) + .map(|(feature, _)| feature) + .unwrap_or_else(|| Some("server".to_string())) } fn auto_detect_platform(&self, resolved: &DioxusCrate) -> Platform { diff --git a/packages/cli/src/cli/bundle.rs b/packages/cli/src/cli/bundle.rs index 87e3864436..86066d6613 100644 --- a/packages/cli/src/cli/bundle.rs +++ b/packages/cli/src/cli/bundle.rs @@ -1,5 +1,5 @@ use crate::DioxusCrate; -use crate::{build::Build, bundle_utils::make_tauri_bundler_settings}; +use crate::{build::BuildArgs, bundle_utils::make_tauri_bundler_settings}; use anyhow::Context; use std::env::current_dir; use std::fs::create_dir_all; @@ -18,11 +18,11 @@ pub struct Bundle { /// The arguments for the dioxus build #[clap(flatten)] - pub build_arguments: Build, + pub build_arguments: BuildArgs, } impl Deref for Bundle { - type Target = Build; + type Target = BuildArgs; fn deref(&self) -> &Self::Target { &self.build_arguments diff --git a/packages/cli/src/cli/mod.rs b/packages/cli/src/cli/mod.rs index 1616c8c560..978f0464b3 100644 --- a/packages/cli/src/cli/mod.rs +++ b/packages/cli/src/cli/mod.rs @@ -50,13 +50,13 @@ pub struct Cli { #[derive(Parser)] pub enum Commands { /// Build the Dioxus project and all of its assets. - Build(build::Build), + Build(build::BuildArgs), /// Translate a source file into Dioxus code. Translate(translate::Translate), /// Build, watch & serve the Dioxus project and all of its assets. - Serve(serve::Serve), + Serve(serve::ServeArgs), /// Create a new project for Dioxus. New(create::Create), diff --git a/packages/cli/src/cli/serve.rs b/packages/cli/src/cli/serve.rs index 4710f6032a..b87e44b6d4 100644 --- a/packages/cli/src/cli/serve.rs +++ b/packages/cli/src/cli/serve.rs @@ -2,14 +2,16 @@ use crate::config::AddressArguments; use crate::settings; use crate::DioxusCrate; use anyhow::Context; -use build::Build; +use build::BuildArgs; use std::ops::Deref; use super::*; -/// Arguments for the serve command -#[derive(Clone, Debug, Parser, Default)] -pub struct ServeArguments { +/// Run the WASM project on dev-server +#[derive(Clone, Debug, Default, Parser)] +#[command(group = clap::ArgGroup::new("release-incompatible").multiple(true).conflicts_with("release"))] +#[clap(name = "serve")] +pub struct ServeArgs { /// The arguments for the address the server will run on #[clap(flatten)] pub address: AddressArguments, @@ -38,54 +40,42 @@ pub struct ServeArguments { /// Sets the interval in seconds that the CLI will poll for file changes on WSL. #[clap(long, default_missing_value = "2")] pub wsl_file_poll_interval: Option, -} - -/// Run the WASM project on dev-server -#[derive(Clone, Debug, Default, Parser)] -#[command(group = clap::ArgGroup::new("release-incompatible").multiple(true).conflicts_with("release"))] -#[clap(name = "serve")] -pub struct Serve { - /// Arguments for the serve command - #[clap(flatten)] - pub(crate) server_arguments: ServeArguments, /// Arguments for the dioxus build #[clap(flatten)] - pub(crate) build_arguments: Build, + pub(crate) build_arguments: BuildArgs, /// Run the server in interactive mode #[arg(long, default_missing_value="true", num_args=0..=1, short = 'i')] pub interactive: Option, } -impl Serve { +impl ServeArgs { /// Resolve the serve arguments from the arguments or the config fn resolve(&mut self, crate_config: &mut DioxusCrate) -> Result<()> { // Set config settings. let settings = settings::CliSettings::load(); // Enable hot reload. - if self.server_arguments.hot_reload.is_none() { - self.server_arguments.hot_reload = Some(settings.always_hot_reload.unwrap_or(true)); + if self.hot_reload.is_none() { + self.hot_reload = Some(settings.always_hot_reload.unwrap_or(true)); } // Open browser. - if self.server_arguments.open.is_none() { - self.server_arguments.open = Some(settings.always_open_browser.unwrap_or_default()); + if self.open.is_none() { + self.open = Some(settings.always_open_browser.unwrap_or_default()); } // Set WSL file poll interval. - if self.server_arguments.wsl_file_poll_interval.is_none() { - self.server_arguments.wsl_file_poll_interval = - Some(settings.wsl_file_poll_interval.unwrap_or(2)); + if self.wsl_file_poll_interval.is_none() { + self.wsl_file_poll_interval = Some(settings.wsl_file_poll_interval.unwrap_or(2)); } // Set always-on-top for desktop. - if self.server_arguments.always_on_top.is_none() { - self.server_arguments.always_on_top = Some(settings.always_on_top.unwrap_or(true)) + if self.always_on_top.is_none() { + self.always_on_top = Some(settings.always_on_top.unwrap_or(true)) } - crate_config.dioxus_config.desktop.always_on_top = - self.server_arguments.always_on_top.unwrap_or(true); + crate_config.dioxus_config.desktop.always_on_top = self.always_on_top.unwrap_or(true); // Resolve the build arguments self.build_arguments.resolve(crate_config)?; @@ -114,12 +104,12 @@ impl Serve { } pub fn should_hotreload(&self) -> bool { - self.server_arguments.hot_reload.unwrap_or(true) + self.hot_reload.unwrap_or(true) } } -impl Deref for Serve { - type Target = Build; +impl Deref for ServeArgs { + type Target = BuildArgs; fn deref(&self) -> &Self::Target { &self.build_arguments diff --git a/packages/cli/src/config/app.rs b/packages/cli/src/config/app.rs index bcb04dcf49..9ba16c7d69 100644 --- a/packages/cli/src/config/app.rs +++ b/packages/cli/src/config/app.rs @@ -1,4 +1,4 @@ -use super::Platform; +use crate::builder::Platform; use serde::{Deserialize, Serialize}; use std::path::PathBuf; diff --git a/packages/cli/src/config/platform.rs b/packages/cli/src/config/platform.rs index d809ea3eb4..e69de29bb2 100644 --- a/packages/cli/src/config/platform.rs +++ b/packages/cli/src/config/platform.rs @@ -1,91 +0,0 @@ -use serde::{Deserialize, Serialize}; -use std::fmt::Display; -use std::str::FromStr; - -#[derive( - Copy, - Clone, - Hash, - PartialEq, - Eq, - PartialOrd, - Ord, - Serialize, - Deserialize, - Debug, - Default, - clap::ValueEnum, -)] -#[non_exhaustive] -pub enum Platform { - /// Targeting the web platform using WASM - #[clap(name = "web")] - #[serde(rename = "web")] - #[default] - Web, - - /// Targeting the desktop platform using Tao/Wry-based webview - #[clap(name = "desktop")] - #[serde(rename = "desktop")] - Desktop, - - #[clap(name = "mobile")] - #[serde(rename = "mobile")] - Mobile, - - /// Targeting the server platform using Axum and Dioxus-Fullstack - #[clap(name = "fullstack")] - #[serde(rename = "fullstack")] - Fullstack, - - /// Targeting the static generation platform using SSR and Dioxus-Fullstack - #[clap(name = "liveview")] - #[serde(rename = "liveview")] - Liveview, -} - -/// An error that occurs when a platform is not recognized -pub struct UnknownPlatformError; - -impl std::fmt::Display for UnknownPlatformError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Unknown platform") - } -} - -impl FromStr for Platform { - type Err = UnknownPlatformError; - - fn from_str(s: &str) -> Result { - match s { - "web" => Ok(Self::Web), - "desktop" => Ok(Self::Desktop), - "fullstack" => Ok(Self::Fullstack), - "liveview" => Ok(Self::Liveview), - _ => Err(UnknownPlatformError), - } - } -} - -impl Display for Platform { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let feature = self.feature_name(); - f.write_str(feature) - } -} - -impl Platform { - /// All platforms the dioxus CLI supports - pub const ALL: &'static [Self] = &[Platform::Web, Platform::Desktop, Platform::Fullstack]; - - /// Get the feature name for the platform in the dioxus crate - pub fn feature_name(&self) -> &str { - match self { - Platform::Web => "web", - Platform::Desktop => "desktop", - Platform::Fullstack => "fullstack", - Platform::Liveview => "liveview", - Platform::Mobile => "mobile", - } - } -} diff --git a/packages/cli/src/dioxus_crate.rs b/packages/cli/src/dioxus_crate.rs index 6a1d713984..fbd3c9b5da 100644 --- a/packages/cli/src/dioxus_crate.rs +++ b/packages/cli/src/dioxus_crate.rs @@ -1,7 +1,5 @@ -use crate::{ - build::TargetArgs, - config::{DioxusConfig, Platform}, -}; +use crate::builder::Platform; +use crate::{build::TargetArgs, config::DioxusConfig}; use krates::cm::Target; use krates::{cm::TargetKind, Cmd, Krates, NodeId}; use serde::{Deserialize, Serialize}; @@ -13,125 +11,6 @@ use std::{ use crate::metadata::CargoError; -/// Load the dioxus config from a path -fn load_dioxus_config( - krates: &Krates, - package: NodeId, -) -> Result, CrateConfigError> { - fn acquire_dioxus_toml(dir: &std::path::Path) -> Option { - ["Dioxus.toml", "dioxus.toml"] - .into_iter() - .map(|file| dir.join(file)) - .find(|path| path.is_file()) - } - - // Walk up from the cargo.toml to the root of the workspace looking for Dioxus.toml - let mut current_dir = krates[package] - .manifest_path - .parent() - .unwrap() - .as_std_path() - .to_path_buf() - .canonicalize()?; - - let workspace_path = krates - .workspace_root() - .as_std_path() - .to_path_buf() - .canonicalize()?; - - let mut dioxus_conf_file = None; - while current_dir.starts_with(&workspace_path) { - // Try to find Dioxus.toml in the current directory - if let Some(new_config) = acquire_dioxus_toml(¤t_dir) { - dioxus_conf_file = Some(new_config.as_path().to_path_buf()); - break; - } - // If we can't find it, go up a directory - current_dir = current_dir - .parent() - .ok_or(CrateConfigError::CurrentPackageNotFound)? - .to_path_buf(); - } - - let Some(dioxus_conf_file) = dioxus_conf_file else { - return Ok(None); - }; - - let cfg = toml::from_str::(&std::fs::read_to_string(&dioxus_conf_file)?) - .map_err(|err| { - CrateConfigError::LoadDioxusConfig(LoadDioxusConfigError { - location: dioxus_conf_file.display().to_string(), - error: err.to_string(), - }) - }) - .map(Some); - match cfg { - Ok(Some(mut cfg)) => { - let name = cfg.application.name.clone(); - if cfg.bundle.identifier.is_none() { - cfg.bundle.identifier = Some(format!("io.github.{name}")); - } - if cfg.bundle.publisher.is_none() { - cfg.bundle.publisher = Some(name); - } - - Ok(Some(cfg)) - } - cfg => cfg, - } -} - -// Find the main package in the workspace -fn find_main_package(package: Option, krates: &Krates) -> Result { - let kid = match package { - Some(package) => { - let mut workspace_members = krates.workspace_members(); - workspace_members - .find_map(|node| { - if let krates::Node::Krate { id, krate, .. } = node { - if krate.name == package { - return Some(id); - } - } - None - }) - .ok_or_else(|| CrateConfigError::PackageNotFound(package.clone()))? - } - None => { - // Otherwise find the package that is the closest parent of the current directory - let current_dir = std::env::current_dir()?; - let current_dir = current_dir.as_path(); - // Go through each member and find the path that is a parent of the current directory - let mut closest_parent = None; - for member in krates.workspace_members() { - if let krates::Node::Krate { id, krate, .. } = member { - let member_path = krate.manifest_path.parent().unwrap(); - if let Ok(path) = current_dir.strip_prefix(member_path.as_std_path()) { - let len = path.components().count(); - match closest_parent { - Some((_, closest_parent_len)) => { - if len < closest_parent_len { - closest_parent = Some((id, len)); - } - } - None => { - closest_parent = Some((id, len)); - } - } - } - } - } - closest_parent - .map(|(id, _)| id) - .ok_or(CrateConfigError::CurrentPackageNotFound)? - } - }; - - let package = krates.nid_for_kid(kid).unwrap(); - Ok(package) -} - // Contains information about the crate we are currently in and the dioxus config for that crate #[derive(Clone)] pub struct DioxusCrate { @@ -356,3 +235,122 @@ impl Display for CrateConfigError { } impl std::error::Error for CrateConfigError {} + +/// Load the dioxus config from a path +fn load_dioxus_config( + krates: &Krates, + package: NodeId, +) -> Result, CrateConfigError> { + fn acquire_dioxus_toml(dir: &std::path::Path) -> Option { + ["Dioxus.toml", "dioxus.toml"] + .into_iter() + .map(|file| dir.join(file)) + .find(|path| path.is_file()) + } + + // Walk up from the cargo.toml to the root of the workspace looking for Dioxus.toml + let mut current_dir = krates[package] + .manifest_path + .parent() + .unwrap() + .as_std_path() + .to_path_buf() + .canonicalize()?; + + let workspace_path = krates + .workspace_root() + .as_std_path() + .to_path_buf() + .canonicalize()?; + + let mut dioxus_conf_file = None; + while current_dir.starts_with(&workspace_path) { + // Try to find Dioxus.toml in the current directory + if let Some(new_config) = acquire_dioxus_toml(¤t_dir) { + dioxus_conf_file = Some(new_config.as_path().to_path_buf()); + break; + } + // If we can't find it, go up a directory + current_dir = current_dir + .parent() + .ok_or(CrateConfigError::CurrentPackageNotFound)? + .to_path_buf(); + } + + let Some(dioxus_conf_file) = dioxus_conf_file else { + return Ok(None); + }; + + let cfg = toml::from_str::(&std::fs::read_to_string(&dioxus_conf_file)?) + .map_err(|err| { + CrateConfigError::LoadDioxusConfig(LoadDioxusConfigError { + location: dioxus_conf_file.display().to_string(), + error: err.to_string(), + }) + }) + .map(Some); + match cfg { + Ok(Some(mut cfg)) => { + let name = cfg.application.name.clone(); + if cfg.bundle.identifier.is_none() { + cfg.bundle.identifier = Some(format!("io.github.{name}")); + } + if cfg.bundle.publisher.is_none() { + cfg.bundle.publisher = Some(name); + } + + Ok(Some(cfg)) + } + cfg => cfg, + } +} + +// Find the main package in the workspace +fn find_main_package(package: Option, krates: &Krates) -> Result { + let kid = match package { + Some(package) => { + let mut workspace_members = krates.workspace_members(); + workspace_members + .find_map(|node| { + if let krates::Node::Krate { id, krate, .. } = node { + if krate.name == package { + return Some(id); + } + } + None + }) + .ok_or_else(|| CrateConfigError::PackageNotFound(package.clone()))? + } + None => { + // Otherwise find the package that is the closest parent of the current directory + let current_dir = std::env::current_dir()?; + let current_dir = current_dir.as_path(); + // Go through each member and find the path that is a parent of the current directory + let mut closest_parent = None; + for member in krates.workspace_members() { + if let krates::Node::Krate { id, krate, .. } = member { + let member_path = krate.manifest_path.parent().unwrap(); + if let Ok(path) = current_dir.strip_prefix(member_path.as_std_path()) { + let len = path.components().count(); + match closest_parent { + Some((_, closest_parent_len)) => { + if len < closest_parent_len { + closest_parent = Some((id, len)); + } + } + None => { + closest_parent = Some((id, len)); + } + } + } + } + } + closest_parent + .map(|(id, _)| id) + .ok_or(CrateConfigError::CurrentPackageNotFound)? + } + }; + + let package = krates.nid_for_kid(kid).unwrap(); + Ok(package) +} diff --git a/packages/cli/src/main.rs b/packages/cli/src/main.rs index e501371c0a..2d8b43d9a2 100644 --- a/packages/cli/src/main.rs +++ b/packages/cli/src/main.rs @@ -5,6 +5,7 @@ pub mod assets; pub mod builder; pub mod bundle_utils; +pub mod bundler; pub mod cli; pub mod config; pub mod dioxus_crate; diff --git a/packages/cli/src/serve/builder.rs b/packages/cli/src/serve/builder.rs index 5a44aa4d44..8b13789179 100644 --- a/packages/cli/src/serve/builder.rs +++ b/packages/cli/src/serve/builder.rs @@ -1,163 +1 @@ -use crate::builder::TargetPlatform; -use crate::builder::UpdateBuildProgress; -use crate::builder::{BuildReason, BuildRequest}; -use crate::dioxus_crate::DioxusCrate; -use crate::serve::next_or_pending; -use crate::serve::Serve; -use crate::Result; -use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender}; -use futures_util::future::OptionFuture; -use futures_util::StreamExt; -use std::{collections::HashMap, process::Stdio}; -use tokio::{ - process::{Child, Command}, - task::{JoinHandle, JoinSet}, -}; -use tokio_util::task::TaskTracker; -use super::update::ServeUpdate; - -/// A handle to ongoing builds and then the spawned tasks themselves -pub struct Builder { - /// Ongoing apps running in place - /// - /// They might be actively being being, running, or have exited. - /// - /// When a new full rebuild occurs, we will keep these requests here - pub running: HashMap, - - tx: UnboundedSender, - rx: UnboundedReceiver, - - /// The application we are building - config: DioxusCrate, - - /// The arguments for the build - serve: Serve, -} - -impl Builder { - /// Create a new builder and immediately start a build - pub fn start(serve: &Serve, config: &DioxusCrate) -> Result { - let (tx, rx) = futures_channel::mpsc::unbounded(); - - let mut builder = Self { - tx, - rx, - building: Default::default(), - config: config.clone(), - serve: serve.clone(), - running: Default::default(), - }; - - builder.build()?; - - Ok(builder) - } - - /// Start a new build - killing the current one if it exists - pub fn build(&mut self) -> Result<()> { - self.shutdown(); - - let build_requests = BuildRequest::create( - BuildReason::Serve, - &self.config, - self.serve.build_arguments.clone(), - self.tx.clone(), - )?; - - for build_request in build_requests { - // Queue the build - let platform = build_request.target_platform.clone(); - self.building.spawn(async move { - // Run the build, but in a protected spawn, ensuring we can't produce panics and thus, joinerrors - let res = tokio::spawn(build_request.build()) - .await - .unwrap_or_else(|err| { - Err(crate::Error::Unique(format!( - "Panic while building project: {err:?}" - ))) - }); - - (platform, build_request) - }); - } - - Ok(()) - } - - /// Wait for any new updates to the builder - either it completed or gave us a message etc - /// - /// Also listen for any input from the app's handle - pub async fn wait(&mut self) -> ServeUpdate { - // Exits and stdout/stderr - let processes = self.running.iter_mut().filter_map(|(target, request)| { - let Some(child) = request.child else { - return None; - }; - - Some(Box::pin(async move { - // - (*target, child.wait().await) - })) - }); - - // Wait for the next build result - tokio::select! { - Some(update) = self.rx.next() => { - ServeUpdate::Progress { update } - } - - Some(Ok((target, build_result))) = self.building.join_next() => { - match build_result { - Ok(build_result) => ServeUpdate::BuildReady { target, request: build_result }, - Err(err) => ServeUpdate::BuildFailed { err, target }, - } - } - - ((target, exit_status), _, _) = futures_util::future::select_all(processes) => { - ServeUpdate::ProcessExited { status: exit_status, target_platform: target } - } - } - } - - /// Shutdown the current build process - pub(crate) fn shutdown(&mut self) { - for (_target, app) in self.running.drain() { - let Some(mut child) = app.child else { - continue; - }; - - // Gracefully shtudown the desktop app - // It might have a receiver to do some cleanup stuff - if let Some(pid) = child.id() { - // on unix, we can send a signal to the process to shut down - #[cfg(unix)] - { - _ = Command::new("kill") - .args(["-s", "TERM", &pid.to_string()]) - .stderr(Stdio::null()) - .stdout(Stdio::null()) - .spawn(); - } - - // on windows, use the `taskkill` command - #[cfg(windows)] - { - _ = Command::new("taskkill") - .args(["/F", "/PID", &pid.to_string()]) - .stderr(Stdio::null()) - .stdout(Stdio::null()) - .spawn(); - } - } - - // Todo: add a timeout here to kill the process if it doesn't shut down within a reasonable time - _ = child.start_kill(); - } - - if let Some(tasks) = self.building.take() { - tasks.abort(); - } - } -} diff --git a/packages/cli/src/serve/mod.rs b/packages/cli/src/serve/mod.rs index 5d65ca19bf..673725beab 100644 --- a/packages/cli/src/serve/mod.rs +++ b/packages/cli/src/serve/mod.rs @@ -1,21 +1,21 @@ -use crate::builder::{Stage, TargetPlatform, UpdateBuildProgress, UpdateStage}; -use crate::cli::serve::Serve; +use crate::builder::{BuildUpdate, Builder, Platform, Stage, UpdateBuildProgress, UpdateStage}; +use crate::cli::serve::ServeArgs; use crate::dioxus_crate::DioxusCrate; use crate::Result; -mod builder; mod detect; mod hot_reloading_file_map; mod logs_tab; mod output; mod proxy; +mod runner; mod server; mod update; mod util; mod watcher; -use builder::*; use output::*; +use runner::*; use server::*; use update::*; use util::*; @@ -49,63 +49,59 @@ use watcher::*; /// - Handle logs from the build engine separately? /// - I want us to be able to detect a `server_fn` in the project and then upgrade from a static server /// to a dynamic one on the fly. -pub async fn serve_all(serve: Serve, krate: DioxusCrate) -> Result<()> { +pub async fn serve_all(args: ServeArgs, krate: DioxusCrate) -> Result<()> { // Start each component of the devserver. // Start the screen first, since it'll start to swallow the tracing logs from the rest of the the cli - let mut screen = Output::start(&serve).expect("Failed to open terminal logger"); + let mut screen = Output::start(&args).expect("Failed to open terminal logger"); // Note that starting the builder will queue up a build immediately - let mut builder = Builder::start(&serve, &krate)?; + let mut builder = Builder::start(&krate, args.build_arguments.clone())?; // The watcher and devserver are started after but don't really matter in order - let mut server = DevServer::start(&serve, &krate); - let mut watcher = Watcher::start(&serve, &krate); + let mut devserver = DevServer::start(&args, &krate); + let mut watcher = Watcher::start(&args, &krate); + let mut runner = AppRunner::start(&args, &krate); loop { // Make sure we don't hog the CPU: these loop { select! {} } blocks can starve the executor if we're not careful tokio::task::yield_now().await; // Draw the state of the server to the screen - screen.render(&serve, &krate, &builder, &server, &watcher); + screen.render(&args, &krate, &builder, &devserver, &watcher); // And then wait for any updates before redrawing let msg = tokio::select! { - msg = watcher.wait(), if serve.should_hotreload() => msg, - msg = server.wait() => msg, - msg = builder.wait() => msg, - msg = screen.wait() => match msg { - Ok(res) => res, - Err(_err) => break - } + msg = builder.wait() => ServeUpdate::BuildUpdate(msg), + msg = watcher.wait() => msg, + msg = devserver.wait() => msg, + msg = screen.wait() => msg, + msg = runner.wait() => msg, }; match msg { - ServeUpdate::FilesChanged {} => { - if !watcher.pending_changes() { + ServeUpdate::FilesChanged { files } => { + if files.is_empty() || args.should_hotreload() { continue; } - let changed_files = watcher.dequeue_changed_files(&krate); - // if change is hotreloadable, hotreload it // and then send that update to all connected clients - if let Some(hr) = watcher.attempt_hot_reload(&krate, changed_files) { + if let Some(hr) = watcher.attempt_hot_reload(&krate, files) { // Only send a hotreload message for templates and assets - otherwise we'll just get a full rebuild if hr.templates.is_empty() && hr.assets.is_empty() { continue; } - server.send_hotreload(hr).await; + devserver.send_hotreload(hr).await; } else { - // If the change is not binary patchable, rebuild the project // We're going to kick off a new build, interrupting the current build if it's ongoing - builder.build()?; + builder.build(args.build_arguments.clone())?; // Clear the hot reload changes watcher.clear_hot_reload_changes(); // Tell the server to show a loading page for any new requests - server.start_build().await; + devserver.start_build().await; } } @@ -113,21 +109,23 @@ pub async fn serve_all(serve: Serve, krate: DioxusCrate) -> Result<()> { // Waiting for updates here lets us tap into when clients are added/removed ServeUpdate::NewConnection => { if let Some(msg) = watcher.applied_hot_reload_changes() { - server.send_hotreload(msg).await; + devserver.send_hotreload(msg).await; } } - ServeUpdate::Message(msg) => { - screen.new_ws_message(TargetPlatform::Web, msg); + // Received a message from the devtools server - currently we only use this for + // logging, so we just forward it the tui + ServeUpdate::WsMessage(msg) => { + screen.new_ws_message(Platform::Web, msg); } // Wait for logs from the build engine // These will cause us to update the screen // We also can check the status of the builds here in case we have multiple ongoing builds - ServeUpdate::Progress { update } => { + ServeUpdate::BuildUpdate(BuildUpdate::Progress(update)) => { let update_clone = update.clone(); screen.new_build_logs(update.platform, update_clone); - server + devserver .update_build_status(screen.build_progress.progress(), update.stage.to_string()) .await; @@ -137,24 +135,24 @@ pub async fn serve_all(serve: Serve, krate: DioxusCrate) -> Result<()> { stage: Stage::Compiling, update: UpdateStage::Start, platform: _, - } => server.send_reload_start().await, + } => devserver.send_reload_start().await, // Send rebuild failed message. UpdateBuildProgress { stage: Stage::Finished, update: UpdateStage::Failed(_), platform: _, - } => server.send_reload_failed().await, + } => devserver.send_reload_failed().await, _ => {} } } - ServeUpdate::BuildFailed { err, target } => { - server.send_build_error(err).await; + ServeUpdate::BuildUpdate(BuildUpdate::BuildFailed { err, target }) => { + devserver.send_build_error(err).await; } - ServeUpdate::BuildReady { target } => { + ServeUpdate::BuildUpdate(BuildUpdate::BuildReady { target, result }) => { // if !results.is_empty() { // builder.children.clear(); // } @@ -183,6 +181,10 @@ pub async fn serve_all(serve: Serve, krate: DioxusCrate) -> Result<()> { // server.send_reload_command().await; } + ServeUpdate::StdoutReceived { target, msg } => {} + + ServeUpdate::StderrReceived { target, msg } => {} + // If the process exited *cleanly*, we can exit ServeUpdate::ProcessExited { status, @@ -207,20 +209,23 @@ pub async fn serve_all(serve: Serve, krate: DioxusCrate) -> Result<()> { // } } + // Handle TUI input and maybe even rebuild the app ServeUpdate::TuiInput { rebuild } => { - // Request a rebuild. if rebuild { - builder.build()?; - server.start_build().await + builder.build(args.build_arguments.clone())?; + devserver.start_build().await } } + + // A fatal error occured and we need to exit + cleanup + ServeUpdate::Fatal { err } => break, } } // Kill the clients first - _ = server.shutdown().await; + _ = devserver.shutdown().await; _ = screen.shutdown(); - _ = builder.shutdown(); + _ = builder.abort_all(); Ok(()) } diff --git a/packages/cli/src/serve/output.rs b/packages/cli/src/serve/output.rs index c479b93e2e..2de6baae09 100644 --- a/packages/cli/src/serve/output.rs +++ b/packages/cli/src/serve/output.rs @@ -1,11 +1,9 @@ -use crate::{ - builder::BuildRequest, - config::{AddressArguments, Platform}, -}; -use crate::{builder::UpdateStage, serve::Serve}; +use crate::config::AddressArguments; +use crate::{builder::UpdateStage, serve::ServeArgs}; use crate::{ builder::{ - BuildMessage, MessageSource, MessageType, Stage, TargetPlatform, UpdateBuildProgress, + BuildMessage, BuildRequest, MessageSource, MessageType, Platform, Stage, + UpdateBuildProgress, }, dioxus_crate::DioxusCrate, serve::next_or_pending, @@ -41,7 +39,7 @@ use super::{update::ServeUpdate, Builder, DevServer, Watcher}; #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum LogSource { Internal, - Target(TargetPlatform), + Target(Platform), } impl Display for LogSource { @@ -53,8 +51,8 @@ impl Display for LogSource { } } -impl From for LogSource { - fn from(platform: TargetPlatform) -> Self { +impl From for LogSource { + fn from(platform: Platform) -> Self { LogSource::Target(platform) } } @@ -62,7 +60,7 @@ impl From for LogSource { #[derive(Default)] pub struct BuildProgress { internal_logs: Vec, - build_logs: HashMap, + build_logs: HashMap, } impl BuildProgress { @@ -115,7 +113,7 @@ enum Tab { type TerminalBackend = Terminal>; impl Output { - pub fn start(cfg: &Serve) -> io::Result { + pub fn start(cfg: &ServeArgs) -> io::Result { // Start a tracing instance just for serving. // This ensures that any tracing we do while serving doesn't break the TUI itself, and instead is // redirected to the serve process. @@ -186,12 +184,12 @@ impl Output { num_lines_with_wrapping: 0, anim_start: Instant::now(), tab: Tab::BuildLog, - addr: cfg.server_arguments.address.clone(), + addr: cfg.address.clone(), }) } /// Add a message from stderr to the logs - fn push_stderr(&mut self, platform: TargetPlatform, stderr: String) { + fn push_stderr(&mut self, platform: Platform, stderr: String) { todo!() // self.set_tab(Tab::BuildLog); @@ -216,7 +214,7 @@ impl Output { } /// Add a message from stdout to the logs - fn push_stdout(&mut self, platform: TargetPlatform, stdout: String) { + fn push_stdout(&mut self, platform: Platform, stdout: String) { todo!() // self.running_apps // .get_mut(&platform) @@ -243,20 +241,21 @@ impl Output { /// Why is the ctrl_c handler here? /// /// Also tick animations every few ms - pub async fn wait(&mut self) -> io::Result { - fn ok_and_some(f: F) -> impl Future - where - F: Future, E>>, - { - next_or_pending(async move { f.await.ok().flatten() }) - } + pub async fn wait(&mut self) -> ServeUpdate { + todo!() + // fn ok_and_some(f: F) -> impl Future + // where + // F: Future, E>>, + // { + // next_or_pending(async move { f.await.ok().flatten() }) + // } - let user_input = async { - let events = self.events.as_mut()?; - events.next().await - }; + // let user_input = async { + // let events = self.events.as_mut()?; + // events.next().await + // }; - let user_input = ok_and_some(user_input.map(|e| e.transpose())); + // let user_input = ok_and_some(user_input.map(|e| e.transpose())); // let has_running_apps = !self.running_apps.is_empty(); // let next_stdout = self.running_apps.values_mut().map(|app| { @@ -314,7 +313,7 @@ impl Output { // } // } - Ok(ServeUpdate::TuiInput { rebuild: false }) + // Ok(ServeUpdate::TuiInput { rebuild: false }) } pub fn shutdown(&mut self) -> io::Result<()> { @@ -437,11 +436,7 @@ impl Output { Ok(false) } - pub fn new_ws_message( - &mut self, - platform: TargetPlatform, - message: axum::extract::ws::Message, - ) { + pub fn new_ws_message(&mut self, platform: Platform, message: axum::extract::ws::Message) { if let axum::extract::ws::Message::Text(text) = message { let msg = serde_json::from_str::(text.as_str()); match msg { @@ -513,7 +508,7 @@ impl Output { } } - pub fn new_build_logs(&mut self, platform: TargetPlatform, update: UpdateBuildProgress) { + pub fn new_build_logs(&mut self, platform: Platform, update: UpdateBuildProgress) { let snapped = self.is_snapped(LogSource::Target(platform)); // when the build is finished, switch to the console @@ -532,7 +527,7 @@ impl Output { } } - pub fn new_ready_app(&mut self, build_engine: &mut Builder, target: TargetPlatform) { + pub fn new_ready_app(&mut self, build_engine: &mut Builder, target: Platform) { todo!() // for result in results { // let out = build_engine @@ -573,9 +568,9 @@ impl Output { pub fn render( &mut self, - _opts: &Serve, - _config: &DioxusCrate, - _build_engine: &Builder, + _opts: &ServeArgs, + _krate: &DioxusCrate, + _builder: &Builder, server: &DevServer, _watcher: &Watcher, ) { diff --git a/packages/cli/src/serve/runner.rs b/packages/cli/src/serve/runner.rs new file mode 100644 index 0000000000..44c1c33b37 --- /dev/null +++ b/packages/cli/src/serve/runner.rs @@ -0,0 +1,43 @@ +use std::collections::HashMap; + +use crate::{ + builder::{BuildResult, BuildUpdate, Platform}, + cli::serve::ServeArgs, + DioxusCrate, +}; + +use super::ServeUpdate; + +pub struct AppRunner { + /// Ongoing apps running in place + /// + /// They might be actively being being, running, or have exited. + /// + /// When a new full rebuild occurs, we will keep these requests here + pub running: HashMap, +} + +impl AppRunner { + pub fn start(serve: &ServeArgs, config: &DioxusCrate) -> Self { + todo!() + } + + pub async fn wait(&mut self) -> ServeUpdate { + // // Exits and stdout/stderr + // let processes = self.running.iter_mut().filter_map(|(target, request)| { + // let Some(child) = request.child else { + // return None; + // }; + + // Some(Box::pin(async move { + // // + // (*target, child.wait().await) + // })) + // }); + + // ((target, exit_status), _, _) = futures_util::future::select_all(processes) => { + // BuildUpdate::ProcessExited { status: exit_status, target_platform: target } + // } + todo!() + } +} diff --git a/packages/cli/src/serve/server.rs b/packages/cli/src/serve/server.rs index b0c727d097..447995357f 100644 --- a/packages/cli/src/serve/server.rs +++ b/packages/cli/src/serve/server.rs @@ -1,12 +1,12 @@ -use crate::{builder::BuildRequest, dioxus_crate::DioxusCrate}; use crate::{ - builder::TargetPlatform, - serve::{next_or_pending, Serve}, + builder::Platform, + serve::{next_or_pending, ServeArgs}, }; use crate::{ - config::{Platform, WebHttpsConfig}, - serve::update::ServeUpdate, + builder::{BuildRequest, BuildResult}, + dioxus_crate::DioxusCrate, }; +use crate::{config::WebHttpsConfig, serve::update::ServeUpdate}; use crate::{Error, Result}; use axum::extract::{Request, State}; use axum::middleware::{self, Next}; @@ -85,7 +85,7 @@ impl SharedStatus { } pub struct DevServer { - pub serve: Serve, + pub serve: ServeArgs, pub hot_reload_sockets: Vec, pub build_status_sockets: Vec, pub ip: SocketAddr, @@ -102,7 +102,7 @@ pub struct DevServer { } impl DevServer { - pub fn start(serve: &Serve, cfg: &DioxusCrate) -> Self { + pub fn start(args: &ServeArgs, cfg: &DioxusCrate) -> Self { let (hot_reload_sockets_tx, hot_reload_sockets_rx) = futures_channel::mpsc::unbounded(); let (build_status_sockets_tx, build_status_sockets_rx) = futures_channel::mpsc::unbounded(); @@ -111,14 +111,11 @@ impl DevServer { build_message: "Starting the build...".to_string(), }); - let addr = serve.server_arguments.address.address(); - let start_browser = serve.server_arguments.open.unwrap_or_default(); + let addr = args.address.address(); + let start_browser = args.open.unwrap_or_default(); // If we're serving a fullstack app, we need to find a port to proxy to - let fullstack_port = if matches!( - serve.build_arguments.platform(), - Platform::Liveview | Platform::Fullstack - ) { + let fullstack_port = if matches!(args.build_arguments.platform(), Platform::Liveview) { get_available_port(addr.ip()) } else { None @@ -127,7 +124,7 @@ impl DevServer { let fullstack_address = fullstack_port.map(|port| SocketAddr::new(addr.ip(), port)); let router = Self::setup_router( - serve, + args, cfg, hot_reload_sockets_tx, build_status_sockets_tx, @@ -139,7 +136,7 @@ impl DevServer { // Actually just start the server, cloning in a few bits of config let web_config = cfg.dioxus_config.web.https.clone(); let base_path = cfg.dioxus_config.web.app.base_path.clone(); - let platform = serve.platform(); + let platform = args.platform(); let listener = std::net::TcpListener::bind(addr).expect("Failed to bind port"); _ = listener.set_nonblocking(true); @@ -175,7 +172,7 @@ impl DevServer { }); Self { - serve: serve.clone(), + serve: args.clone(), hot_reload_sockets: Default::default(), build_status_sockets: Default::default(), new_hot_reload_sockets: hot_reload_sockets_rx, @@ -186,7 +183,7 @@ impl DevServer { build_status, application_name: cfg.dioxus_config.application.name.clone(), - platform: serve.build_arguments.platform().to_string(), + platform: args.build_arguments.platform().to_string(), } } @@ -287,7 +284,7 @@ impl DevServer { } (idx, message) = next_new_message => { match message { - Some(Ok(message)) => return ServeUpdate::Message(message), + Some(Ok(message)) => return ServeUpdate::WsMessage(message), _ => { drop(new_message); _ = self.hot_reload_sockets.remove(idx); @@ -356,18 +353,19 @@ impl DevServer { } /// Open the executable if this is a native build - pub fn open(&self, build: &BuildRequest) -> std::io::Result> { - match build.target_platform { - TargetPlatform::Web => Ok(None), - TargetPlatform::Mobile => self.open_bundled_ios_app(build), - TargetPlatform::Desktop | TargetPlatform::Server | TargetPlatform::Liveview => { + pub fn open(&self, build: &BuildResult) -> std::io::Result> { + match build.request.platform() { + Platform::Web => Ok(None), + Platform::Ios => self.open_bundled_ios_app(build), + Platform::Android => todo!("Android not supported yet"), + Platform::Desktop | Platform::Server | Platform::Liveview => { self.open_unbundled_native_app(build) } } } - fn open_unbundled_native_app(&self, build: &BuildRequest) -> std::io::Result> { - if build.target_platform == TargetPlatform::Server { + fn open_unbundled_native_app(&self, build: &BuildResult) -> std::io::Result> { + if build.request.platform() == Platform::Server { tracing::trace!( "Proxying fullstack server from port {:?}", self.fullstack_address() @@ -379,46 +377,38 @@ impl DevServer { self.ip.to_string() ); - // // open the exe with some arguments/envvars/etc // we're going to try and configure this binary from the environment, if we can // // web can't be configured like this, so instead, we'll need to plumb a meta tag into the // index.html during dev - // - let res = Command::new( - build - .executable - .as_deref() - .expect("executable should be built if we're trying to open it") - .canonicalize()?, - ) - .env( - dioxus_runtime_config::FULLSTACK_ADDRESS_ENV, - self.fullstack_address() - .as_ref() - .map(|addr| addr.to_string()) - .unwrap_or_else(|| "127.0.0.1:8080".to_string()), - ) - .env( - dioxus_runtime_config::IOS_DEVSERVER_ADDR_ENV, - format!("ws://{}/_dioxus", self.ip.to_string()), - ) - .env( - dioxus_runtime_config::DEVSERVER_RAW_ADDR_ENV, - format!("ws://{}/_dioxus", self.ip.to_string()), - ) - .env("CARGO_MANIFEST_DIR", build.krate.crate_dir()) - .stderr(Stdio::piped()) - .stdout(Stdio::piped()) - .kill_on_drop(true) - .current_dir(build.krate.workspace_dir()) - .spawn()?; + let res = Command::new(build.bundle.path()) + .env( + dioxus_runtime_config::FULLSTACK_ADDRESS_ENV, + self.fullstack_address() + .as_ref() + .map(|addr| addr.to_string()) + .unwrap_or_else(|| "127.0.0.1:8080".to_string()), + ) + .env( + dioxus_runtime_config::IOS_DEVSERVER_ADDR_ENV, + format!("ws://{}/_dioxus", self.ip.to_string()), + ) + .env( + dioxus_runtime_config::DEVSERVER_RAW_ADDR_ENV, + format!("ws://{}/_dioxus", self.ip.to_string()), + ) + .env("CARGO_MANIFEST_DIR", build.request.krate.crate_dir()) + .stderr(Stdio::piped()) + .stdout(Stdio::piped()) + .kill_on_drop(true) + .current_dir(build.request.krate.workspace_dir()) + .spawn()?; Ok(Some(res)) } - fn open_bundled_ios_app(&self, build: &BuildRequest) -> std::io::Result> { + fn open_bundled_ios_app(&self, build: &BuildResult) -> std::io::Result> { // command = "xcrun" // args = [ // "simctl", @@ -470,18 +460,18 @@ impl DevServer { /// - Setting up the file serve service /// - Setting up the websocket endpoint for devtools fn setup_router( - serve: &Serve, - config: &DioxusCrate, + args: &ServeArgs, + krate: &DioxusCrate, hot_reload_sockets: UnboundedSender, build_status_sockets: UnboundedSender, fullstack_address: Option, build_status: SharedStatus, ) -> Result { let mut router = Router::new(); - let platform = serve.build_arguments.platform(); + let platform = args.build_arguments.platform(); // Setup proxy for the endpoint specified in the config - for proxy_config in config.dioxus_config.web.proxy.iter() { + for proxy_config in krate.dioxus_config.web.proxy.iter() { router = super::proxy::add_proxy(router, proxy_config)?; } @@ -491,7 +481,7 @@ impl DevServer { // Route file service to output the .wasm and assets if this is a web build let base_path = format!( "/{}", - config + krate .dioxus_config .web .app @@ -501,9 +491,9 @@ impl DevServer { .trim_matches('/') ); - router = router.nest_service(&base_path, build_serve_dir(serve, config)); + router = router.nest_service(&base_path, build_serve_dir(args, krate)); } - Platform::Liveview | Platform::Fullstack => { + Platform::Liveview => { // For fullstack and static generation, forward all requests to the server let address = fullstack_address.unwrap(); @@ -568,7 +558,7 @@ impl DevServer { } } -fn build_serve_dir(serve: &Serve, cfg: &DioxusCrate) -> axum::routing::MethodRouter { +fn build_serve_dir(args: &ServeArgs, cfg: &DioxusCrate) -> axum::routing::MethodRouter { static CORS_UNSAFE: (HeaderValue, HeaderValue) = ( HeaderValue::from_static("unsafe-none"), HeaderValue::from_static("unsafe-none"), @@ -579,7 +569,7 @@ fn build_serve_dir(serve: &Serve, cfg: &DioxusCrate) -> axum::routing::MethodRou HeaderValue::from_static("same-origin"), ); - let (coep, coop) = match serve.server_arguments.cross_origin_policy { + let (coep, coop) = match args.cross_origin_policy { true => CORS_REQUIRE.clone(), false => CORS_UNSAFE.clone(), }; diff --git a/packages/cli/src/serve/update.rs b/packages/cli/src/serve/update.rs index cd7d496972..7a2983deef 100644 --- a/packages/cli/src/serve/update.rs +++ b/packages/cli/src/serve/update.rs @@ -1,36 +1,49 @@ -use crate::builder::{BuildRequest, TargetPlatform, UpdateBuildProgress}; +use crate::builder::{BuildRequest, BuildResult, BuildUpdate, Platform, UpdateBuildProgress}; use axum::extract::ws::Message as WsMessage; -use std::process::ExitStatus; +use std::{path::PathBuf, process::ExitStatus}; /// One fat enum to rule them all.... /// /// Thanks to libraries like winit for the inspiration pub enum ServeUpdate { NewConnection, - Message(WsMessage), + WsMessage(WsMessage), - Progress { - update: UpdateBuildProgress, - }, + /// A build update from the build engine + BuildUpdate(BuildUpdate), - BuildReady { - target: TargetPlatform, - request: BuildRequest, + /// A running process has received a stdout. + /// May or may not be a complete line - do not treat it as a line. It will include a line if it is a complete line. + /// + /// We will poll lines and any content in a 50ms interval + StdoutReceived { + target: Platform, + msg: String, }, - BuildFailed { - target: TargetPlatform, - err: crate::Error, + /// A running process has received a stderr. + /// May or may not be a complete line - do not treat it as a line. It will include a line if it is a complete line. + /// + /// We will poll lines and any content in a 50ms interval + StderrReceived { + target: Platform, + msg: String, }, ProcessExited { - target_platform: TargetPlatform, + target_platform: Platform, status: Result, }, - FilesChanged {}, + FilesChanged { + files: Vec, + }, TuiInput { rebuild: bool, }, + + Fatal { + err: Box, + }, } diff --git a/packages/cli/src/serve/util.rs b/packages/cli/src/serve/util.rs index b103a0ed50..2c8c94c74b 100644 --- a/packages/cli/src/serve/util.rs +++ b/packages/cli/src/serve/util.rs @@ -1,5 +1,5 @@ -use crate::builder::{Stage, TargetPlatform, UpdateBuildProgress, UpdateStage}; -use crate::cli::serve::Serve; +use crate::builder::{Platform, Stage, UpdateBuildProgress, UpdateStage}; +use crate::cli::serve::ServeArgs; use crate::dioxus_crate::DioxusCrate; use crate::Result; use futures_util::FutureExt; diff --git a/packages/cli/src/serve/watcher.rs b/packages/cli/src/serve/watcher.rs index 55d96f1eda..f8c08f3c4b 100644 --- a/packages/cli/src/serve/watcher.rs +++ b/packages/cli/src/serve/watcher.rs @@ -1,7 +1,7 @@ use super::detect::is_wsl; use super::{hot_reloading_file_map::HotreloadError, update::ServeUpdate}; use crate::serve::hot_reloading_file_map::FileMap; -use crate::{cli::serve::Serve, dioxus_crate::DioxusCrate}; +use crate::{cli::serve::ServeArgs, dioxus_crate::DioxusCrate}; use dioxus_devtools_types::HotReloadMsg; use dioxus_html::HtmlCtx; use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender}; @@ -20,7 +20,7 @@ use std::{path::PathBuf, time::Duration}; /// directories. pub struct Watcher { rx: UnboundedReceiver, - queued_events: Vec, + krate: DioxusCrate, file_map: FileMap, ignore: Gitignore, applied_hot_reload_message: Option, @@ -30,24 +30,24 @@ pub struct Watcher { } impl Watcher { - pub fn start(serve: &Serve, config: &DioxusCrate) -> Self { + pub fn start(serve: &ServeArgs, krate: &DioxusCrate) -> Self { let (tx, rx) = futures_channel::mpsc::unbounded(); // Extend the watch path to include: // - the assets directory - this is so we can hotreload CSS and other assets by default // - the Cargo.toml file - this is so we can hotreload the project if the user changes dependencies // - the Dioxus.toml file - this is so we can hotreload the project if the user changes the Dioxus config - let mut allow_watch_path = config.dioxus_config.web.watcher.watch_path.clone(); - allow_watch_path.push(config.dioxus_config.application.asset_dir.clone()); + let mut allow_watch_path = krate.dioxus_config.web.watcher.watch_path.clone(); + allow_watch_path.push(krate.dioxus_config.application.asset_dir.clone()); allow_watch_path.push("Cargo.toml".to_string().into()); allow_watch_path.push("Dioxus.toml".to_string().into()); allow_watch_path.dedup(); - let crate_dir = config.crate_dir(); + let crate_dir = krate.crate_dir(); let mut builder = ignore::gitignore::GitignoreBuilder::new(&crate_dir); builder.add(crate_dir.join(".gitignore")); - let out_dir = config.out_dir(); + let out_dir = krate.out_dir(); let out_dir_str = out_dir.display().to_string(); let excluded_paths = vec![ @@ -85,9 +85,8 @@ impl Watcher { // Create the file watcher. let mut watcher: Box = match is_wsl { true => { - let poll_interval = Duration::from_secs( - serve.server_arguments.wsl_file_poll_interval.unwrap_or(2) as u64, - ); + let poll_interval = + Duration::from_secs(serve.wsl_file_poll_interval.unwrap_or(2) as u64); Box::new( notify::PollWatcher::new( @@ -105,7 +104,7 @@ impl Watcher { // Watch the specified paths // todo: make sure we don't double-watch paths if they're nested for sub_path in allow_watch_path { - let path = &config.crate_dir().join(sub_path); + let path = &krate.crate_dir().join(sub_path); // If the path is ignored, don't watch it if ignore.matched(path, path.is_dir()).is_ignore() { @@ -121,52 +120,38 @@ impl Watcher { // Probe the entire project looking for our rsx calls // Whenever we get an update from the file watcher, we'll try to hotreload against this file map - let file_map = FileMap::create_with_filter::(config.crate_dir(), |path| { + let file_map = FileMap::create_with_filter::(krate.crate_dir(), |path| { ignore.matched(path, path.is_dir()).is_ignore() }) .unwrap(); Self { _tx: tx, + krate: krate.clone(), rx, _watcher: watcher, file_map, ignore, - queued_events: Vec::new(), _last_update_time: chrono::Local::now().timestamp(), applied_hot_reload_message: None, } } - /// A cancel safe handle to the file watcher - /// - /// todo: this should be simpler logic? + /// Wait for changed files to be detected pub async fn wait(&mut self) -> ServeUpdate { - // Pull off any queued events in succession - while let Ok(Some(event)) = self.rx.try_next() { - self.queued_events.push(event); - } - - if !self.queued_events.is_empty() { - tokio::time::sleep(std::time::Duration::from_millis(100)).await; - tracing::info!("Waiting for file changes!"); - return ServeUpdate::FilesChanged {}; - } + // Wait for the next file to change + let mut changes: Vec<_> = self.rx.next().await.into_iter().collect(); - // If there are no queued events, wait for the next event - if let Some(event) = self.rx.next().await { - self.queued_events.push(event); + // Dequeue in bulk if we can, we might've received a lot of events in one go + while let Some(event) = self.rx.try_next().ok().flatten() { + changes.push(event); } - ServeUpdate::FilesChanged {} - } - - /// Deques changed files from the event queue, doing the proper intelligent filtering - pub fn dequeue_changed_files(&mut self, config: &DioxusCrate) -> Vec { + // Filter the changes let mut all_mods: Vec = vec![]; // Decompose the events into a list of all the files that have changed - for event in self.queued_events.drain(..) { + for event in changes.drain(..) { // We only care about certain events. if !is_allowed_notify_event(&event) { continue; @@ -177,18 +162,8 @@ impl Watcher { } } - let mut modified_files = vec![]; - - // For the non-rust files, we want to check if it's an asset file - // This would mean the asset lives somewhere under the /assets directory or is referenced by magnanis in the linker - // todo: mg integration here - let _asset_dir = config - .dioxus_config - .application - .asset_dir - .canonicalize() - .ok(); - + // Collect the files that have changed + let mut files = vec![]; for path in all_mods.iter() { if path.extension().is_none() { continue; @@ -217,19 +192,19 @@ impl Watcher { tracing::info!("Enqueuing hotreload update to file: {:?}", path); - modified_files.push(path.clone()); + files.push(path.clone()); } - modified_files + ServeUpdate::FilesChanged { files } } pub fn attempt_hot_reload( &mut self, - config: &DioxusCrate, + krate: &DioxusCrate, modified_files: Vec, ) -> Option { // If we have any changes to the rust files, we need to update the file map - let crate_dir = config.crate_dir(); + let crate_dir = krate.crate_dir(); let mut templates = vec![]; // Prepare the hotreload message we need to send @@ -318,12 +293,6 @@ impl Watcher { applied.assets = assets.into_iter().collect(); applied.unknown_files = unknown_files.into_iter().collect(); } - - /// Ensure the changes we've received from the queue are actually legit changes to either assets or - /// rust code. We don't care about changes otherwise, unless we get a signal elsewhere to do a full rebuild - pub fn pending_changes(&mut self) -> bool { - !self.queued_events.is_empty() - } } fn is_backup_file(path: PathBuf) -> bool { From d843884cc04aea80d5cbcc3d343659b75ad8f1ef Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 4 Sep 2024 16:01:44 -0700 Subject: [PATCH 061/139] bit more cleanup --- packages/cli/src/assets.rs | 12 +--- packages/cli/src/builder/assets.rs | 7 +- packages/cli/src/builder/builder.rs | 37 ++++++---- packages/cli/src/builder/handle.rs | 9 --- packages/cli/src/builder/mod.rs | 3 +- packages/cli/src/builder/prepare_html.rs | 11 +-- packages/cli/src/builder/profiles.rs | 5 -- packages/cli/src/builder/progress.rs | 4 ++ packages/cli/src/builder/request.rs | 88 ++---------------------- packages/cli/src/bundler/app.rs | 4 +- packages/cli/src/cli/build.rs | 14 ++-- packages/cli/src/cli/bundle.rs | 3 +- packages/cli/src/cli/serve.rs | 23 +++---- packages/cli/src/config.rs | 1 - packages/cli/src/serve/mod.rs | 4 ++ packages/cli/src/serve/output.rs | 18 ++--- packages/cli/src/serve/runner.rs | 18 ++++- 17 files changed, 92 insertions(+), 169 deletions(-) diff --git a/packages/cli/src/assets.rs b/packages/cli/src/assets.rs index 24611e5c52..3883529cae 100644 --- a/packages/cli/src/assets.rs +++ b/packages/cli/src/assets.rs @@ -31,16 +31,9 @@ use crate::link::InterceptedArgs; /// This will be filled in primarly by incremental compilation artifacts. #[derive(Debug, PartialEq, Default, Clone)] pub struct AssetManifest { - /// Map of asset pathbuf to its pub(crate) assets: HashMap, } -#[derive(Clone)] -pub struct OptimizeOptions { - pub precompress: bool, - pub enabled: bool, -} - impl AssetManifest { /// Creates a new asset manifest pub fn new() -> Self { @@ -210,7 +203,8 @@ impl AssetManifest { &self, destination: &Path, target_asset: &Path, - optimize: &OptimizeOptions, + optimize: bool, + pre_compress: bool, ) { let src = self.assets.get(target_asset).unwrap(); @@ -221,7 +215,7 @@ impl AssetManifest { } // If there's no optimizaton while copying this asset, we simply std::fs::copy and call it a day - if !optimize.enabled { + if !optimize { std::fs::copy(local, destination.join(&src.bundled)).expect("Failed to copy asset"); return; } diff --git a/packages/cli/src/builder/assets.rs b/packages/cli/src/builder/assets.rs index 1e6025daa4..3919a29e2e 100644 --- a/packages/cli/src/builder/assets.rs +++ b/packages/cli/src/builder/assets.rs @@ -1,11 +1,8 @@ use super::Platform; use super::{BuildRequest, BuildResult}; -use crate::builder::{progress::UpdateStage, MessageSource}; +use crate::builder::progress::UpdateBuildProgress; +use crate::builder::progress::UpdateStage; use crate::Result; -use crate::{ - assets::OptimizeOptions, - builder::{progress::UpdateBuildProgress, BuildMessage, MessageType}, -}; use crate::{ assets::{copy_dir_to, AssetManifest}, link::LINK_OUTPUT_ENV_VAR, diff --git a/packages/cli/src/builder/builder.rs b/packages/cli/src/builder/builder.rs index a422cdba0c..835688edf3 100644 --- a/packages/cli/src/builder/builder.rs +++ b/packages/cli/src/builder/builder.rs @@ -1,12 +1,9 @@ -//! The primary interface for building Dioxus apps in parallel - -use crate::builder::BuildRequest; -use crate::builder::{BuildResult, Platform}; +use crate::build::BuildArgs; +use crate::builder::*; use crate::dioxus_crate::DioxusCrate; use crate::Result; -use crate::{build::BuildArgs, builder::UpdateBuildProgress}; -use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender}; use futures_util::StreamExt; +use progress::{ProgressRx, ProgressTx, UpdateBuildProgress}; use tokio::task::JoinSet; /// A handle to ongoing builds and then the spawned tasks themselves @@ -18,10 +15,7 @@ pub struct Builder { pub building: JoinSet<(Platform, Result)>, /// Messages from the build engine will be sent to this channel - pub channel: ( - UnboundedSender, - UnboundedReceiver, - ), + pub channel: (ProgressTx, ProgressRx), } pub enum BuildUpdate { @@ -36,6 +30,9 @@ pub enum BuildUpdate { target: Platform, err: crate::Error, }, + + /// All builds have finished and there's nothing left to do + Finished, } impl Builder { @@ -91,16 +88,32 @@ impl Builder { Ok(()) } + /// Wait for the build to finish + pub async fn wait_for_finish(&mut self) { + loop { + let next = self.wait().await; + if let BuildUpdate::Finished = next { + return; + } + } + } + /// Wait for any new updates to the builder - either it completed or gave us a message etc /// /// Also listen for any input from the app's handle + /// + /// Returns immediately with `Finished` if there are no more builds to run - don't poll-loop this! pub async fn wait(&mut self) -> BuildUpdate { + if self.building.is_empty() { + return BuildUpdate::Finished; + } + tokio::select! { Some(update) = self.channel.1.next() => BuildUpdate::Progress(update), Some(Ok((target, result))) = self.building.join_next() => { match result { Ok(result) => BuildUpdate::BuildReady { target, result }, - Err(err) => BuildUpdate::BuildFailed { err, target }, + Err(err) => BuildUpdate::BuildFailed { target, err }, } } } @@ -113,7 +126,7 @@ impl Builder { self.building.abort_all(); } - fn tx(&self) -> UnboundedSender { + fn tx(&self) -> ProgressTx { self.channel.0.clone() } } diff --git a/packages/cli/src/builder/handle.rs b/packages/cli/src/builder/handle.rs index feed326836..8b13789179 100644 --- a/packages/cli/src/builder/handle.rs +++ b/packages/cli/src/builder/handle.rs @@ -1,10 +1 @@ -use tokio::process::Child; -/// A handle to a running app -pub struct AppHandle { - // child: Option, - // stdout: Lines>, - // stderr: Lines>, - // stdout_line: String, - // stderr_line: String, -} diff --git a/packages/cli/src/builder/mod.rs b/packages/cli/src/builder/mod.rs index b3efde8ed0..1cb715ba83 100644 --- a/packages/cli/src/builder/mod.rs +++ b/packages/cli/src/builder/mod.rs @@ -18,11 +18,12 @@ mod profiles; mod progress; mod web; +use crate::build::BuildArgs; use crate::Result; use crate::{assets::AssetManifest, dioxus_crate::DioxusCrate}; -use crate::build::BuildArgs; pub use builder::*; pub use platform::*; +pub use progress::*; pub use request::*; pub use result::*; diff --git a/packages/cli/src/builder/prepare_html.rs b/packages/cli/src/builder/prepare_html.rs index 6b4b1d6c18..99cdcfb942 100644 --- a/packages/cli/src/builder/prepare_html.rs +++ b/packages/cli/src/builder/prepare_html.rs @@ -1,8 +1,9 @@ //! Build the HTML file to load a web application. The index.html file may be created from scratch or modified from the `index.html` file in the crate root. -use super::{BuildRequest, UpdateBuildProgress}; -use crate::builder::progress::MessageSource; -use crate::builder::Stage; +use super::BuildRequest; +use crate::builder::progress::{ + BuildMessage, MessageSource, MessageType, Stage, UpdateBuildProgress, UpdateStage, +}; use crate::Result; use std::fmt::Write; @@ -180,9 +181,9 @@ impl BuildRequest { _ = self.progress.unbounded_send(UpdateBuildProgress { platform: self.platform(), stage: Stage::OptimizingWasm, - update: super::UpdateStage::AddMessage(super::BuildMessage { + update: UpdateStage::AddMessage(BuildMessage { level: Level::WARN, - message: super::MessageType::Text(message), + message: MessageType::Text(message), source: MessageSource::Build, }), }); diff --git a/packages/cli/src/builder/profiles.rs b/packages/cli/src/builder/profiles.rs index a4068505dc..23a6e40ab1 100644 --- a/packages/cli/src/builder/profiles.rs +++ b/packages/cli/src/builder/profiles.rs @@ -1,12 +1,7 @@ -use crate::builder::BuildArgs; -use crate::builder::BuildRequest; use crate::dioxus_crate::DioxusCrate; -use futures_channel::mpsc::UnboundedSender; use std::io::Write; use toml_edit::Item; -use super::{Platform, UpdateBuildProgress}; - pub static CLIENT_PROFILE: &str = "dioxus-client"; pub static SERVER_PROFILE: &str = "dioxus-server"; diff --git a/packages/cli/src/builder/progress.rs b/packages/cli/src/builder/progress.rs index 1a912f9adb..d90030399b 100644 --- a/packages/cli/src/builder/progress.rs +++ b/packages/cli/src/builder/progress.rs @@ -2,6 +2,7 @@ use super::{BuildRequest, Platform}; use anyhow::Context; use cargo_metadata::{diagnostic::Diagnostic, Message}; +use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender}; use serde::Deserialize; use std::fmt::Display; use std::ops::Deref; @@ -10,6 +11,9 @@ use std::process::Stdio; use tokio::{io::AsyncBufReadExt, process::Command}; use tracing::Level; +pub type ProgressTx = UnboundedSender; +pub type ProgressRx = UnboundedReceiver; + #[derive(Default, Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Copy)] pub enum Stage { #[default] diff --git a/packages/cli/src/builder/request.rs b/packages/cli/src/builder/request.rs index 5d991cc828..de89ba5697 100644 --- a/packages/cli/src/builder/request.rs +++ b/packages/cli/src/builder/request.rs @@ -1,17 +1,10 @@ use super::profiles::*; -use super::BuildResult; +use super::progress::ProgressTx; use crate::build::BuildArgs; use crate::builder::Platform; -use crate::Result; -use crate::{assets::AssetManifest, dioxus_crate::DioxusCrate}; -use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender}; -use futures_util::StreamExt; +use crate::dioxus_crate::DioxusCrate; use std::path::PathBuf; -pub use super::progress::{ - BuildMessage, MessageSource, MessageType, Stage, UpdateBuildProgress, UpdateStage, -}; - /// An app that's built, bundled, processed, and a handle to its running app, if it exists /// /// As the build progresses, we'll fill in fields like assets, executable, entitlements, etc @@ -31,17 +24,11 @@ pub struct BuildRequest { pub custom_target_dir: Option, /// Status channel to send our progress updates to - pub progress: UnboundedSender, + pub progress: ProgressTx, } impl BuildRequest { - pub fn new( - krate: DioxusCrate, - build_arguments: impl Into, - progress: UnboundedSender, - ) -> Self { - let build: BuildArgs = build_arguments.into(); - + pub fn new(krate: DioxusCrate, build: BuildArgs, progress: ProgressTx) -> Self { Self { progress, build, @@ -55,8 +42,7 @@ impl BuildRequest { krate: &DioxusCrate, build: &BuildArgs, feature: Option, - target_platform: Platform, - progress: UnboundedSender, + progress: ProgressTx, ) -> Self { let config = krate.clone(); let mut build = build.clone(); @@ -76,11 +62,7 @@ impl BuildRequest { } } - pub fn new_server( - krate: &DioxusCrate, - mut build: BuildArgs, - progress: UnboundedSender, - ) -> Self { + pub fn new_server(krate: &DioxusCrate, mut build: BuildArgs, progress: ProgressTx) -> Self { if build.profile.is_none() { build.profile = Some(CLIENT_PROFILE.to_string()); } @@ -89,16 +71,11 @@ impl BuildRequest { krate, &build, build.target_args.server_feature.clone().or(client_feature), - Platform::Server, progress, ) } - pub fn new_client( - krate: &DioxusCrate, - mut build: BuildArgs, - progress: UnboundedSender, - ) -> Self { + pub fn new_client(krate: &DioxusCrate, mut build: BuildArgs, progress: ProgressTx) -> Self { if build.profile.is_none() { build.profile = Some(SERVER_PROFILE.to_string()); } @@ -107,61 +84,10 @@ impl BuildRequest { krate, &build, build.target_args.client_feature.clone().or(client_feature), - client_platform, progress, ) } - pub(crate) async fn build_all_parallel( - build_requests: Vec, - mut rx: UnboundedReceiver, - ) -> Result> { - let multi_platform_build = build_requests.len() > 1; - // let mut set = tokio::task::JoinSet::new(); - - // for build_request in build_requests { - // set.spawn(async move { build_request.build().await }); - // } - - // // Watch the build progress as it comes in - // while let Some(update) = rx.next().await { - // if multi_platform_build { - // let platform = update.platform; - // print!("{platform} build: "); - // update.to_std_out(); - // } else { - // update.to_std_out(); - // } - // } - - todo!() - - // let mut all_results = Vec::new(); - - // while let Some(result) = set.join_next().await { - // all_results.push( - // result - // .map_err(|_| crate::Error::Unique("Failed to build project".to_owned()))??, - // ); - // } - - // Ok(all_results) - } - - pub(crate) fn new_single( - krate: DioxusCrate, - arguments: BuildArgs, - progress: UnboundedSender, - ) -> Self { - Self { - progress, - krate, - build: arguments, - custom_target_dir: Default::default(), - rust_flags: Default::default(), - } - } - /// Get the platform for this build pub fn platform(&self) -> Platform { self.build diff --git a/packages/cli/src/bundler/app.rs b/packages/cli/src/bundler/app.rs index f4f1e9077f..f26902010b 100644 --- a/packages/cli/src/bundler/app.rs +++ b/packages/cli/src/bundler/app.rs @@ -5,14 +5,14 @@ use crate::{assets::AssetManifest, builder::Platform}; pub struct AppBundle {} impl AppBundle { - pub fn new(platform: Platform) -> Self { + pub fn new(_platform: Platform) -> Self { todo!() } pub fn set_main_executable(&mut self) {} /// Copy the assets out of the manifest and into the target location - pub async fn copy_assets(&mut self, manifest: &AssetManifest) { + pub async fn copy_assets(&mut self, _manifest: &AssetManifest) { todo!() } diff --git a/packages/cli/src/cli/build.rs b/packages/cli/src/cli/build.rs index 40891cee71..8572996655 100644 --- a/packages/cli/src/cli/build.rs +++ b/packages/cli/src/cli/build.rs @@ -133,16 +133,10 @@ impl BuildArgs { pub async fn build(&mut self, dioxus_crate: &mut DioxusCrate) -> Result<()> { self.resolve(dioxus_crate)?; - let mut builder = crate::builder::Builder::new(dioxus_crate); - - // if self.fullstack { - // builder.build_fullstack(dioxus_crate)?; - // } - - // let (tx, rx) = futures_channel::mpsc::unbounded(); - - // let build_requests = BuildRequest::create(dioxus_crate, self.clone(), tx)?; - // BuildRequest::build_all_parallel(build_requests, rx).await?; + // todo: probably want to consume the logs from the builder here, instead of just waiting for it to finish + crate::builder::Builder::start(dioxus_crate, self.clone())? + .wait_for_finish() + .await; Ok(()) } diff --git a/packages/cli/src/cli/bundle.rs b/packages/cli/src/cli/bundle.rs index 86066d6613..a70e3931ec 100644 --- a/packages/cli/src/cli/bundle.rs +++ b/packages/cli/src/cli/bundle.rs @@ -3,7 +3,6 @@ use crate::{build::BuildArgs, bundle_utils::make_tauri_bundler_settings}; use anyhow::Context; use std::env::current_dir; use std::fs::create_dir_all; -use std::ops::Deref; use std::str::FromStr; use tauri_bundler::{PackageSettings, SettingsBuilder}; @@ -21,7 +20,7 @@ pub struct Bundle { pub build_arguments: BuildArgs, } -impl Deref for Bundle { +impl std::ops::Deref for Bundle { type Target = BuildArgs; fn deref(&self) -> &Self::Target { diff --git a/packages/cli/src/cli/serve.rs b/packages/cli/src/cli/serve.rs index b87e44b6d4..a2f801539c 100644 --- a/packages/cli/src/cli/serve.rs +++ b/packages/cli/src/cli/serve.rs @@ -3,7 +3,6 @@ use crate::settings; use crate::DioxusCrate; use anyhow::Context; use build::BuildArgs; -use std::ops::Deref; use super::*; @@ -51,6 +50,15 @@ pub struct ServeArgs { } impl ServeArgs { + pub async fn serve(mut self) -> Result<()> { + let mut krate = DioxusCrate::new(&self.build_arguments.target_args) + .context("Failed to load Dioxus workspace")?; + + self.resolve(&mut krate)?; + + crate::serve::serve_all(self, krate).await + } + /// Resolve the serve arguments from the arguments or the config fn resolve(&mut self, crate_config: &mut DioxusCrate) -> Result<()> { // Set config settings. @@ -75,6 +83,7 @@ impl ServeArgs { if self.always_on_top.is_none() { self.always_on_top = Some(settings.always_on_top.unwrap_or(true)) } + crate_config.dioxus_config.desktop.always_on_top = self.always_on_top.unwrap_or(true); // Resolve the build arguments @@ -93,22 +102,12 @@ impl ServeArgs { Ok(()) } - pub async fn serve(mut self) -> anyhow::Result<()> { - let mut dioxus_crate = DioxusCrate::new(&self.build_arguments.target_args) - .context("Failed to load Dioxus workspace")?; - - self.resolve(&mut dioxus_crate)?; - - crate::serve::serve_all(self, dioxus_crate).await?; - Ok(()) - } - pub fn should_hotreload(&self) -> bool { self.hot_reload.unwrap_or(true) } } -impl Deref for ServeArgs { +impl std::ops::Deref for ServeArgs { type Target = BuildArgs; fn deref(&self) -> &Self::Target { diff --git a/packages/cli/src/config.rs b/packages/cli/src/config.rs index daec03f3b4..c495b45e0d 100644 --- a/packages/cli/src/config.rs +++ b/packages/cli/src/config.rs @@ -13,6 +13,5 @@ pub use app::*; pub use bundle::*; pub use desktop::*; pub use dioxus_config::*; -pub use platform::*; pub use serve::*; pub use web::*; diff --git a/packages/cli/src/serve/mod.rs b/packages/cli/src/serve/mod.rs index 673725beab..1f000d9ed8 100644 --- a/packages/cli/src/serve/mod.rs +++ b/packages/cli/src/serve/mod.rs @@ -185,6 +185,10 @@ pub async fn serve_all(args: ServeArgs, krate: DioxusCrate) -> Result<()> { ServeUpdate::StderrReceived { target, msg } => {} + ServeUpdate::BuildUpdate(BuildUpdate::Finished) => { + // nothing - the builder just signals that there are no more pending builds + } + // If the process exited *cleanly*, we can exit ServeUpdate::ProcessExited { status, diff --git a/packages/cli/src/serve/output.rs b/packages/cli/src/serve/output.rs index 2de6baae09..45e7351287 100644 --- a/packages/cli/src/serve/output.rs +++ b/packages/cli/src/serve/output.rs @@ -1,14 +1,7 @@ +use crate::builder::*; use crate::config::AddressArguments; -use crate::{builder::UpdateStage, serve::ServeArgs}; -use crate::{ - builder::{ - BuildMessage, BuildRequest, MessageSource, MessageType, Platform, Stage, - UpdateBuildProgress, - }, - dioxus_crate::DioxusCrate, - serve::next_or_pending, - tracer::CLILogControl, -}; +use crate::serve::ServeArgs; +use crate::{builder::Platform, dioxus_crate::DioxusCrate, tracer::CLILogControl}; use core::panic; use crossterm::{ event::{Event, EventStream, KeyCode, KeyModifiers, MouseEventKind}, @@ -28,10 +21,7 @@ use std::{ sync::atomic::Ordering, time::{Duration, Instant}, }; -use tokio::{ - io::{AsyncBufReadExt, BufReader, Lines}, - process::{ChildStderr, ChildStdout}, -}; + use tracing::Level; use super::{update::ServeUpdate, Builder, DevServer, Watcher}; diff --git a/packages/cli/src/serve/runner.rs b/packages/cli/src/serve/runner.rs index 44c1c33b37..026c8bdd18 100644 --- a/packages/cli/src/serve/runner.rs +++ b/packages/cli/src/serve/runner.rs @@ -5,6 +5,10 @@ use crate::{ cli::serve::ServeArgs, DioxusCrate, }; +use tokio::{ + io::{AsyncBufReadExt, BufReader, Lines}, + process::{ChildStderr, ChildStdout}, +}; use super::ServeUpdate; @@ -14,7 +18,19 @@ pub struct AppRunner { /// They might be actively being being, running, or have exited. /// /// When a new full rebuild occurs, we will keep these requests here - pub running: HashMap, + pub running: HashMap, +} + +use tokio::process::Child; + +/// A handle to a running app +pub struct AppHandle { + pub app: BuildResult, + pub child: Option, + pub stdout: Lines>, + pub stderr: Lines>, + pub stdout_line: String, + pub stderr_line: String, } impl AppRunner { From 778b5ec3526368fd04cf65422af6442447937eee Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 4 Sep 2024 20:45:40 -0700 Subject: [PATCH 062/139] add http serve subcommand --- Cargo.lock | 1 + packages/cli/.gitignore | 1 + packages/cli/Cargo.toml | 1 + packages/cli/src/assets.rs | 40 ++-- packages/cli/src/builder/assets.rs | 111 +--------- packages/cli/src/builder/builder.rs | 5 +- packages/cli/src/builder/cargo.rs | 155 ++++++------- packages/cli/src/builder/platform.rs | 10 +- packages/cli/src/builder/prepare_html.rs | 7 +- packages/cli/src/builder/progress.rs | 29 ++- packages/cli/src/builder/request.rs | 20 +- packages/cli/src/builder/result.rs | 41 +--- packages/cli/src/builder/web.rs | 162 +++++--------- packages/cli/src/bundler.rs | 24 ++ packages/cli/src/bundler/app.rs | 267 ++++++++++++++++++++--- packages/cli/src/cli/build.rs | 44 ++-- packages/cli/src/cli/create.rs | 2 +- packages/cli/src/cli/httpserver.rs | 16 ++ packages/cli/src/cli/mod.rs | 6 + packages/cli/src/cli/serve.rs | 21 +- packages/cli/src/dioxus_crate.rs | 19 +- packages/cli/src/fastfs.rs | 4 + packages/cli/src/main.rs | 70 ++---- packages/cli/src/metadata.rs | 2 +- packages/cli/src/serve/detect.rs | 2 +- packages/cli/src/serve/mod.rs | 94 ++++---- packages/cli/src/serve/output.rs | 166 ++++---------- packages/cli/src/serve/proxy.rs | 2 +- packages/cli/src/serve/runner.rs | 162 +++++++++++++- packages/cli/src/serve/server.rs | 104 +-------- packages/cli/src/serve/tracer.rs | 108 +++++++++ packages/cli/src/serve/update.rs | 11 +- packages/cli/src/serve/util.rs | 2 +- packages/cli/src/serve/watcher.rs | 4 +- packages/cli/src/tracer.rs | 95 -------- packages/fullstack/Cargo.toml | 4 +- 36 files changed, 948 insertions(+), 864 deletions(-) create mode 100644 packages/cli/src/cli/httpserver.rs create mode 100644 packages/cli/src/fastfs.rs create mode 100644 packages/cli/src/serve/tracer.rs diff --git a/Cargo.lock b/Cargo.lock index daf9646b5c..c517ab3d73 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2391,6 +2391,7 @@ dependencies = [ "dioxus-core", "dioxus-core-types", "dioxus-devtools-types", + "dioxus-fullstack", "dioxus-html", "dioxus-rsx", "dioxus-runtime-config", diff --git a/packages/cli/.gitignore b/packages/cli/.gitignore index 6700b1332d..f61923446c 100644 --- a/packages/cli/.gitignore +++ b/packages/cli/.gitignore @@ -2,3 +2,4 @@ Cargo.lock .DS_Store .idea/ +ignore/ diff --git a/packages/cli/Cargo.toml b/packages/cli/Cargo.toml index 171a351908..3bcf1dc5c6 100644 --- a/packages/cli/Cargo.toml +++ b/packages/cli/Cargo.toml @@ -92,6 +92,7 @@ dioxus-core = { workspace = true, features = ["serialize"] } dioxus-core-types = { workspace = true } dioxus-devtools-types = { workspace = true } dioxus-runtime-config = { workspace = true } +dioxus-fullstack = { workspace = true } ignore = "0.4.22" env_logger = { workspace = true } diff --git a/packages/cli/src/assets.rs b/packages/cli/src/assets.rs index 3883529cae..70226de70c 100644 --- a/packages/cli/src/assets.rs +++ b/packages/cli/src/assets.rs @@ -31,21 +31,15 @@ use crate::link::InterceptedArgs; /// This will be filled in primarly by incremental compilation artifacts. #[derive(Debug, PartialEq, Default, Clone)] pub struct AssetManifest { - pub(crate) assets: HashMap, + pub assets: HashMap, } impl AssetManifest { - /// Creates a new asset manifest - pub fn new() -> Self { - Self { - ..Default::default() - } - } - + /// Create a new asset manifest pre-populated with the assets from the linker intercept pub fn new_from_linker_intercept(args: InterceptedArgs) -> Self { - let mut _self = Self::new(); - _self.add_from_linker_intercept(args); - _self + let mut manifest = Self::default(); + manifest.add_from_linker_intercept(args); + manifest } /// Fill this manifest from the intercepted rustc args used to link the app together @@ -227,11 +221,7 @@ impl AssetManifest { pub fn sync_with_folder(&self, destination: &Path) {} } -pub(crate) fn copy_dir_to( - src_dir: PathBuf, - dest_dir: PathBuf, - pre_compress: bool, -) -> std::io::Result<()> { +pub fn copy_dir_to(src_dir: PathBuf, dest_dir: PathBuf, pre_compress: bool) -> std::io::Result<()> { let entries = std::fs::read_dir(&src_dir)?; let mut children: Vec>> = Vec::new(); @@ -299,20 +289,22 @@ fn compressed_path(path: &Path) -> Option { } /// pre-compress a file with brotli -pub(crate) fn pre_compress_file(path: &Path) -> std::io::Result<()> { +pub fn pre_compress_file(path: &Path) -> std::io::Result<()> { let Some(compressed_path) = compressed_path(path) else { return Ok(()); }; + let file = std::fs::File::open(path)?; let mut stream = std::io::BufReader::new(file); let mut buffer = std::fs::File::create(compressed_path)?; let params = BrotliEncoderParams::default(); brotli::BrotliCompress(&mut stream, &mut buffer, ¶ms)?; + Ok(()) } /// pre-compress all files in a folder -pub(crate) fn pre_compress_folder(path: &Path, pre_compress: bool) -> std::io::Result<()> { +pub fn pre_compress_folder(path: &Path, pre_compress: bool) -> std::io::Result<()> { let walk_dir = WalkDir::new(path); for entry in walk_dir.into_iter().filter_map(|e| e.ok()) { let entry_path = entry.path(); @@ -499,7 +491,7 @@ pub(crate) fn pre_compress_folder(path: &Path, pre_compress: bool) -> std::io::R // } // } -// pub(crate) fn minify_css(css: &str) -> String { +// pub fn minify_css(css: &str) -> String { // let mut stylesheet = StyleSheet::parse(css, ParserOptions::default()).unwrap(); // stylesheet.minify(MinifyOptions::default()).unwrap(); // let printer = PrinterOptions { @@ -510,7 +502,7 @@ pub(crate) fn pre_compress_folder(path: &Path, pre_compress: bool) -> std::io::R // res.code // } -// pub(crate) fn minify_js(source: &ResourceAsset) -> anyhow::Result { +// pub fn minify_js(source: &ResourceAsset) -> anyhow::Result { // todo!("disabled swc due to semver issues") // // let cm = Arc::::default(); @@ -570,7 +562,7 @@ pub(crate) fn pre_compress_folder(path: &Path, pre_compress: bool) -> std::io::R // } // } -// pub(crate) fn minify_json(source: &str) -> anyhow::Result { +// pub fn minify_json(source: &str) -> anyhow::Result { // // First try to parse the json // let json: serde_json::Value = serde_json::from_str(source)?; // // Then print it in a minified format @@ -827,7 +819,7 @@ pub(crate) fn pre_compress_folder(path: &Path, pre_compress: bool) -> std::io::R // } // } -// pub(crate) fn minify_css(css: &str) -> String { +// pub fn minify_css(css: &str) -> String { // let mut stylesheet = StyleSheet::parse(css, ParserOptions::default()).unwrap(); // stylesheet.minify(MinifyOptions::default()).unwrap(); // let printer = PrinterOptions { @@ -838,7 +830,7 @@ pub(crate) fn pre_compress_folder(path: &Path, pre_compress: bool) -> std::io::R // res.code // } -// pub(crate) fn minify_js(source: &ResourceAsset) -> anyhow::Result { +// pub fn minify_js(source: &ResourceAsset) -> anyhow::Result { // todo!("disabled swc due to semver issues") // // let cm = Arc::::default(); @@ -898,7 +890,7 @@ pub(crate) fn pre_compress_folder(path: &Path, pre_compress: bool) -> std::io::R // } // } -// pub(crate) fn minify_json(source: &str) -> anyhow::Result { +// pub fn minify_json(source: &str) -> anyhow::Result { // // First try to parse the json // let json: serde_json::Value = serde_json::from_str(source)?; // // Then print it in a minified format diff --git a/packages/cli/src/builder/assets.rs b/packages/cli/src/builder/assets.rs index 3919a29e2e..5470e38910 100644 --- a/packages/cli/src/builder/assets.rs +++ b/packages/cli/src/builder/assets.rs @@ -1,5 +1,5 @@ +use super::BuildRequest; use super::Platform; -use super::{BuildRequest, BuildResult}; use crate::builder::progress::UpdateBuildProgress; use crate::builder::progress::UpdateStage; use crate::Result; @@ -28,116 +28,7 @@ use tokio::process::Command; use tracing::Level; impl BuildRequest { - /// Run the linker intercept and then fill in our AssetManifest from the incremental artifacts - /// - /// This will execute `dx` with an env var set to force `dx` to operate as a linker, and then - /// traverse the .o and .rlib files rustc passes that new `dx` instance, collecting the link - /// tables marked by manganis and parsing them as a ResourceAsset. - pub async fn collect_assets(&self) -> anyhow::Result { - // If this is the server build, the client build already copied any assets we need - if self.platform() == Platform::Server { - return Ok(AssetManifest::default()); - } - - // If assets are skipped, we don't need to collect them - if self.build.skip_assets { - return Ok(AssetManifest::default()); - } - - // Create a temp file to put the output of the args - // We need to do this since rustc won't actually print the link args to stdout, so we need to - // give `dx` a file to dump its env::args into - let tmp_file = tempfile::NamedTempFile::new()?; - - // Run `cargo rustc` again, but this time with a custom linker (dx) and an env var to force - // `dx` to act as a linker - // - // Pass in the tmp_file as the env var itself - // - // NOTE: that -Csave-temps=y is needed to prevent rustc from deleting the incremental cache... - // This might not be a "stable" way of keeping artifacts around, but it's in stable rustc - tokio::process::Command::new("cargo") - .arg("rustc") - .args(self.build_arguments()) - .arg("--offline") /* don't use the network, should already be resolved */ - .arg("--") - .arg(format!("-Clinker={}", current_exe().unwrap().display())) /* pass ourselves in */ - .env(LINK_OUTPUT_ENV_VAR, tmp_file.path()) /* but with the env var pointing to the temp file */ - .arg("-Csave-temps=y") /* don't delete the incremental cache */ - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .output() - .await?; - - // Read the contents of the temp file - let args = std::fs::read_to_string(tmp_file.path()).expect("Failed to read linker output"); - - // Parse them as a Vec which is just our informal format for link args in the cli - // Todo: this might be wrong-ish on windows? The format is weird - let args = - serde_json::from_str::(&args).expect("Failed to parse linker output"); - - Ok(AssetManifest::new_from_linker_intercept(args)) - } - // pub fn copy_assets_dir(&self) -> anyhow::Result<()> { - // tracing::info!("Copying public assets to the output directory..."); - - // let static_asset_output_dir = self.target_out_dir(); - // std::fs::create_dir_all(&static_asset_output_dir) - // .context("Failed to create static asset output directory")?; - - // // todo: join the entire asset dir here - // let asset_dir = self.krate.asset_dir(); - // let assets = self.assets.assets.keys().collect::>(); - - // let assets_finished = AtomicUsize::new(0); - // let asset_count = assets.len(); - - // let options = OptimizeOptions { - // enabled: false, - // precompress: self.targeting_web() - // && self - // .krate - // .should_pre_compress_web_assets(self.build_arguments.release), - // }; - - // assets - // .par_iter() - // .enumerate() - // .try_for_each(|(_idx, asset)| { - // // Update the progress - // _ = self.progress.unbounded_send(UpdateBuildProgress { - // stage: Stage::OptimizingAssets, - // update: UpdateStage::AddMessage(BuildMessage { - // level: Level::INFO, - // message: MessageType::Text(format!( - // "Optimized static asset {}", - // asset.display() - // )), - // source: MessageSource::Build, - // }), - // platform: self.target_platform, - // }); - - // // Copy the asset into the bundle directory - // self.assets.copy_asset_to( - // static_asset_output_dir.clone(), - // asset.to_path_buf(), - // &options, - // ); - - // let finished = assets_finished.fetch_add(1, std::sync::atomic::Ordering::SeqCst); - - // _ = self.progress.unbounded_send(UpdateBuildProgress { - // stage: Stage::OptimizingAssets, - // update: UpdateStage::SetProgress(finished as f64 / asset_count as f64), - // platform: self.target_platform, - // }); - - // Ok(()) as anyhow::Result<()> - // })?; - // Ok(()) // } } diff --git a/packages/cli/src/builder/builder.rs b/packages/cli/src/builder/builder.rs index 835688edf3..12610f1f47 100644 --- a/packages/cli/src/builder/builder.rs +++ b/packages/cli/src/builder/builder.rs @@ -1,6 +1,7 @@ use crate::build::BuildArgs; use crate::builder::*; use crate::dioxus_crate::DioxusCrate; +use crate::AppBundle; use crate::Result; use futures_util::StreamExt; use progress::{ProgressRx, ProgressTx, UpdateBuildProgress}; @@ -12,7 +13,7 @@ pub struct Builder { pub krate: DioxusCrate, /// Ongoing builds - pub building: JoinSet<(Platform, Result)>, + pub building: JoinSet<(Platform, Result)>, /// Messages from the build engine will be sent to this channel pub channel: (ProgressTx, ProgressRx), @@ -23,7 +24,7 @@ pub enum BuildUpdate { BuildReady { target: Platform, - result: BuildResult, + result: AppBundle, }, BuildFailed { diff --git a/packages/cli/src/builder/cargo.rs b/packages/cli/src/builder/cargo.rs index 786a95d946..8886d01038 100644 --- a/packages/cli/src/builder/cargo.rs +++ b/packages/cli/src/builder/cargo.rs @@ -1,14 +1,14 @@ -use super::{BuildRequest, BuildResult}; -use crate::Result; -use crate::{assets::AssetManifest, builder::progress::*}; -use crate::{builder::Platform, bundler::AppBundle}; +use super::{AppBundle, BuildRequest}; +use crate::builder::Platform; +use crate::{assets::AssetManifest, builder::progress::*, link::LINK_OUTPUT_ENV_VAR}; +use crate::{link::InterceptedArgs, Result}; use anyhow::Context; -use std::fs::create_dir_all; -use std::path::PathBuf; +use std::{env::current_exe, fs::create_dir_all}; +use std::{path::PathBuf, process::Stdio}; use tokio::process::Command; impl BuildRequest { - pub async fn build(self) -> Result { + pub async fn build(self) -> Result { tracing::info!("🚅 Running build [Desktop] command..."); // Install any tooling that might be required for this build. @@ -21,96 +21,83 @@ impl BuildRequest { let assets = self.collect_assets().await?; // Assemble a bundle from everything - let bundle = self.bundle_app(executable, &assets).await?; - - // And then construct a final BuildResult which we can then modify while the app is running - BuildResult::new(self, assets, bundle) - .await - .map_err(Into::into) + AppBundle::new(self, assets, executable).await } pub async fn verify_tooling(&self) -> Result<()> { - // If this is a web, build make sure we have the web build tooling set up - if self.targeting_web() { - self.install_web_build_tooling().await?; - } - - Ok(()) - } - - pub(crate) async fn bundle_app( - &self, - executable: PathBuf, - assets: &AssetManifest, - ) -> Result { - let mut bundle = AppBundle::new(self.platform()); - - bundle.copy_assets(assets); - - // _ = self.progress.unbounded_send(UpdateBuildProgress { - // stage: Stage::OptimizingAssets, - // update: UpdateStage::Start, - // platform: self.target_platform, - // }); - - // self.collect_assets().await?; + match self.platform() { + // If this is a web, build make sure we have the web build tooling set up + Platform::Web => self.install_web_build_tooling().await?, - // let file_name = self.krate.executable_name(); + // Make sure we have mobile tooling if need be + Platform::Ios => {} + Platform::Android => {} - // // Move the final output executable into the dist folder - // let out_dir = self.target_out_dir(); - // if !out_dir.is_dir() { - // create_dir_all(&out_dir)?; - // } + // Make sure we have the required deps for desktop. More important for linux + Platform::Desktop => {} - // let mut output_path = out_dir.join(file_name); - - // // todo: this should not be platform cfged but rather be a target config - // // we dont always want to set the .exe extension... - // if self.targeting_web() { - // output_path.set_extension("wasm"); - // } else if cfg!(windows) { - // output_path.set_extension("exe"); - // } - - // // if let Some(res_path) = &cargo_build_result.output_location { - // // std::fs::copy(res_path, &output_path)?; - // // } - - // // // Make sure we set the exeutable - // // self.executable = Some(output_path.canonicalize()?); + // Generally nothing for the server, pretty simple + Platform::Server => {} + Platform::Liveview => {} + } - // // // And then copy over the asset dir into the bundle - // // // todo: this will eventually become a full bundle step - // // self.copy_assets_dir()?; + Ok(()) + } - // // If this is a web build, run web post processing steps - // if self.targeting_web() { - // self.post_process_web_build().await?; - // } + /// Run the linker intercept and then fill in our AssetManifest from the incremental artifacts + /// + /// This will execute `dx` with an env var set to force `dx` to operate as a linker, and then + /// traverse the .o and .rlib files rustc passes that new `dx` instance, collecting the link + /// tables marked by manganis and parsing them as a ResourceAsset. + pub async fn collect_assets(&self) -> anyhow::Result { + // If this is the server build, the client build already copied any assets we need + if self.platform() == Platform::Server { + return Ok(AssetManifest::default()); + } - todo!() - } + // If assets are skipped, we don't need to collect them + if self.build.skip_assets { + return Ok(AssetManifest::default()); + } - /// Get the output directory for a specific built target - pub fn target_out_dir(&self) -> PathBuf { - let out_dir = self.krate.out_dir(); - - todo!() - - // if let Some(Platform::Fullstack) = self.build_arguments.platform { - // match self.platform { - // Platform::Web => out_dir.join("public"), - // Platform::Desktop => out_dir.join("desktop"), - // _ => out_dir, - // } - // } else { - // out_dir - // } + // Create a temp file to put the output of the args + // We need to do this since rustc won't actually print the link args to stdout, so we need to + // give `dx` a file to dump its env::args into + let tmp_file = tempfile::NamedTempFile::new()?; + + // Run `cargo rustc` again, but this time with a custom linker (dx) and an env var to force + // `dx` to act as a linker + // + // Pass in the tmp_file as the env var itself + // + // NOTE: that -Csave-temps=y is needed to prevent rustc from deleting the incremental cache... + // This might not be a "stable" way of keeping artifacts around, but it's in stable rustc + tokio::process::Command::new("cargo") + .arg("rustc") + .args(self.build_arguments()) + .arg("--offline") /* don't use the network, should already be resolved */ + .arg("--") + .arg(format!("-Clinker={}", current_exe().unwrap().display())) /* pass ourselves in */ + .env(LINK_OUTPUT_ENV_VAR, tmp_file.path()) /* but with the env var pointing to the temp file */ + .arg("-Csave-temps=y") /* don't delete the incremental cache */ + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .output() + .await?; + + // Read the contents of the temp file + let args = std::fs::read_to_string(tmp_file.path()).expect("Failed to read linker output"); + + // Parse them as a Vec which is just our informal format for link args in the cli + // Todo: this might be wrong-ish on windows? The format is weird + let args = + serde_json::from_str::(&args).expect("Failed to parse linker output"); + + Ok(AssetManifest::new_from_linker_intercept(args)) } /// Create a list of arguments for cargo builds - pub(crate) fn build_arguments(&self) -> Vec { + pub fn build_arguments(&self) -> Vec { let mut cargo_args = Vec::new(); if self.build.release { diff --git a/packages/cli/src/builder/platform.rs b/packages/cli/src/builder/platform.rs index ce4f2d5167..f3a60fab14 100644 --- a/packages/cli/src/builder/platform.rs +++ b/packages/cli/src/builder/platform.rs @@ -39,6 +39,11 @@ pub enum Platform { #[serde(rename = "ios")] Ios, + /// Targeting the android platform + #[clap(name = "android")] + #[serde(rename = "android")] + Android, + /// Targetting the server platform using Axum and Dioxus-Fullstack /// /// This is implicitly passed if `fullstack` is enabled as a feature. Using this variant simply @@ -47,11 +52,6 @@ pub enum Platform { #[serde(rename = "server")] Server, - /// Targeting the android platform - #[clap(name = "android")] - #[serde(rename = "android")] - Android, - /// Targeting the static generation platform using SSR and Dioxus-Fullstack #[clap(name = "liveview")] #[serde(rename = "liveview")] diff --git a/packages/cli/src/builder/prepare_html.rs b/packages/cli/src/builder/prepare_html.rs index 99cdcfb942..91681c4ad6 100644 --- a/packages/cli/src/builder/prepare_html.rs +++ b/packages/cli/src/builder/prepare_html.rs @@ -14,7 +14,7 @@ const DEFAULT_HTML: &str = include_str!("../../assets/index.html"); const TOAST_HTML: &str = include_str!("../../assets/toast.html"); impl BuildRequest { - pub(crate) fn prepare_html(&self) -> Result { + pub fn prepare_html(&self) -> Result { let mut html = html_or_default(&self.krate.crate_dir()); // Inject any resources from the config into the html @@ -34,8 +34,7 @@ impl BuildRequest { } fn is_dev_build(&self) -> bool { - todo!() - // self.reason == BuildReason::Serve && !self.build_arguments.release + !self.build.release } // Inject any resources from the config into the html @@ -149,7 +148,7 @@ impl BuildRequest { // If the path is absolute, make it relative to the current directory before we join it // The path is actually a web path which is relative to the root of the website let path = path.strip_prefix("/").unwrap_or(path); - let asset_dir_path = self.krate.asset_dir().join(path); + let asset_dir_path = self.krate.legacy_asset_dir().join(path); if let Ok(absolute_path) = asset_dir_path.canonicalize() { let absolute_crate_root = self.krate.crate_dir().canonicalize().unwrap(); PathBuf::from("./") diff --git a/packages/cli/src/builder/progress.rs b/packages/cli/src/builder/progress.rs index d90030399b..cbe0f3a2a7 100644 --- a/packages/cli/src/builder/progress.rs +++ b/packages/cli/src/builder/progress.rs @@ -4,10 +4,10 @@ use anyhow::Context; use cargo_metadata::{diagnostic::Diagnostic, Message}; use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender}; use serde::Deserialize; -use std::fmt::Display; use std::ops::Deref; use std::path::PathBuf; use std::process::Stdio; +use std::{fmt::Display, path::Path}; use tokio::{io::AsyncBufReadExt, process::Command}; use tracing::Level; @@ -309,7 +309,7 @@ impl BuildRequest { /// Get an estimate of the number of units in the crate. If nightly rustc is not available, this will return an estimate of the number of units in the crate based on cargo metadata. /// TODO: always use https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#unit-graph once it is stable - pub(crate) async fn get_unit_count_estimate(&self) -> usize { + pub async fn get_unit_count_estimate(&self) -> usize { // Try to get it from nightly self.get_unit_count().await.unwrap_or_else(|| { // Otherwise, use cargo metadata @@ -335,4 +335,29 @@ impl BuildRequest { update: UpdateStage::Start, }); } + + pub fn status_copying_asset(&self, cur: usize, total: usize, asset: &Path) { + // Update the progress + // _ = self.progress.unbounded_send(UpdateBuildProgress { + // stage: Stage::OptimizingAssets, + // update: UpdateStage::AddMessage(BuildMessage { + // level: Level::INFO, + // message: MessageType::Text(format!( + // "Optimized static asset {}", + // asset.display() + // )), + // source: MessageSource::Build, + // }), + // platform: self.target_platform, + // }); + } + + pub fn status_finished_asset(&self, idx: usize, total: usize, asset: &Path) { + // Update the progress + // _ = self.progress.unbounded_send(UpdateBuildProgress { + // stage: Stage::OptimizingAssets, + // update: UpdateStage::SetProgress(finished as f64 / asset_count as f64), + // platform: self.target_platform, + // }); + } } diff --git a/packages/cli/src/builder/request.rs b/packages/cli/src/builder/request.rs index de89ba5697..de7538863c 100644 --- a/packages/cli/src/builder/request.rs +++ b/packages/cli/src/builder/request.rs @@ -1,8 +1,8 @@ use super::profiles::*; use super::progress::ProgressTx; -use crate::build::BuildArgs; use crate::builder::Platform; use crate::dioxus_crate::DioxusCrate; +use crate::{build::BuildArgs, bundler::BundleFormat}; use std::path::PathBuf; /// An app that's built, bundled, processed, and a handle to its running app, if it exists @@ -94,4 +94,22 @@ impl BuildRequest { .platform .unwrap_or_else(|| self.krate.dioxus_config.application.default_platform) } + + /// The final output name of the app, primarly to be used when bundled + /// + /// Needs to be very disambiguated + /// Eg: my-app-web-macos-x86_64.app + /// {app_name}-{platform}-{arch} + /// + /// Does not include the extension + pub fn app_name(&self) -> String { + match self.platform() { + Platform::Web => "web".to_string(), + Platform::Desktop => todo!(), + Platform::Ios => todo!(), + Platform::Server => todo!(), + Platform::Android => todo!(), + Platform::Liveview => todo!(), + } + } } diff --git a/packages/cli/src/builder/result.rs b/packages/cli/src/builder/result.rs index 7e7f7d598f..42e4c0a758 100644 --- a/packages/cli/src/builder/result.rs +++ b/packages/cli/src/builder/result.rs @@ -1,40 +1 @@ -use crate::bundler::AppBundle; -use anyhow::Context; -use std::path::PathBuf; - -use super::*; - -pub struct BuildResult { - /// Initial request that built this result - pub request: BuildRequest, - - /// The output bundle - pub bundle: AppBundle, - - /// The assets manifest - pub assets: AssetManifest, - - /// The child process of this running app that has yet to be spawned. - /// - /// We might need to finangle this into something else - pub child: Option, -} - -impl BuildResult { - pub async fn new( - request: BuildRequest, - assets: AssetManifest, - bundle: AppBundle, - ) -> anyhow::Result { - let mut res = Self { - request, - assets, - bundle, - child: None, - }; - - Ok(res) - } - - pub fn hotreload_asset(&mut self) {} -} +pub use crate::bundler::AppBundle; diff --git a/packages/cli/src/builder/web.rs b/packages/cli/src/builder/web.rs index 4bcb1b1db0..c5f436ba38 100644 --- a/packages/cli/src/builder/web.rs +++ b/packages/cli/src/builder/web.rs @@ -4,67 +4,23 @@ use crate::builder::progress::Stage; use crate::builder::progress::UpdateBuildProgress; use crate::builder::progress::UpdateStage; use crate::error::{Error, Result}; +use anyhow::Context; use std::path::{Path, PathBuf}; use tokio::process::Command; use wasm_bindgen_cli_support::Bindgen; impl BuildRequest { - /// Post process the WASM build artifacts - pub(crate) async fn post_process_web_build(&self, executable: PathBuf) -> Result<()> { - _ = self.progress.unbounded_send(UpdateBuildProgress { - stage: Stage::OptimizingWasm, - update: UpdateStage::Start, - platform: self.platform(), - }); - - // Find the wasm file - let output_location = executable.clone(); - let input_path = output_location.with_extension("wasm"); - - // Create the directory where the bindgen output will be placed - let bindgen_outdir = self.target_out_dir().join("assets").join("dioxus"); - - // Run wasm-bindgen - self.run_wasm_bindgen(&input_path, &bindgen_outdir).await?; - - // Only run wasm-opt if the feature is enabled - // Wasm-opt has an expensive build script that makes it annoying to keep enabled for iterative dev - // We put it behind the "wasm-opt" feature flag so that it can be disabled when iterating on the cli - #[cfg(feature = "wasm-opt")] - self.run_wasm_opt(&bindgen_outdir)?; - - // If pre-compressing is enabled, we can pre_compress the wasm-bindgen output - let pre_compress = self - .krate - .should_pre_compress_web_assets(self.build.release); - - tokio::task::spawn_blocking(move || pre_compress_folder(&bindgen_outdir, pre_compress)) - .await - .unwrap()?; - - // Create the index.html file - // Note that we do this last since the webserver will attempt to serve the index.html file - // If we do this too early, the wasm won't be ready but the index.html will be served, leading - // to test failures and broken pages. - let html = self.prepare_html()?; - let html_path = self.target_out_dir().join("index.html"); - std::fs::write(html_path, html)?; - - Ok(()) - } - - async fn run_wasm_bindgen(&self, input_path: &Path, bindgen_outdir: &Path) -> Result<()> { + pub async fn run_wasm_bindgen(&self, input_path: &Path, bindgen_outdir: &Path) -> Result<()> { tracing::info!("Running wasm-bindgen"); + let input_path = input_path.to_path_buf(); let bindgen_outdir = bindgen_outdir.to_path_buf(); - let keep_debug = self.krate.dioxus_config.web.wasm_opt.debug || (!self.build.release); let name = self.krate.dioxus_config.application.name.clone(); + let keep_debug = self.krate.dioxus_config.web.wasm_opt.debug || (!self.build.release); - let run_wasm_bindgen = move || { - // [3] Bindgen the final binary for use easy linking - let mut bindgen_builder = Bindgen::new(); - - bindgen_builder + let start = std::time::Instant::now(); + tokio::task::spawn_blocking(move || { + Bindgen::new() .input_path(&input_path) .web(true) .unwrap() @@ -76,27 +32,65 @@ impl BuildRequest { .remove_producers_section(!keep_debug) .out_name(&name) .generate(&bindgen_outdir) - .unwrap(); - }; - - let start = std::time::Instant::now(); - let bindgen_result = tokio::task::spawn_blocking(run_wasm_bindgen.clone()).await; + }) + .await + .context("Wasm-bindgen crashed while optimizing the wasm binary")? + .context("Failed to generate wasm-bindgen bindings")?; tracing::info!("wasm-bindgen complete in {:?}", start.elapsed()); - // WASM bindgen requires the exact version of the bindgen schema to match the version the CLI was built with - // If we get an error, we can try to recover by pinning the user's wasm-bindgen version to the version we used - if let Err(err) = bindgen_result { - tracing::error!("Bindgen build failed: {:?}", err); - Self::update_wasm_bindgen_version().await?; - run_wasm_bindgen(); + Ok(()) + } + + #[allow(unused)] + pub fn run_wasm_opt(&self, bindgen_outdir: &std::path::PathBuf) -> Result<(), Error> { + if !self.build.release { + return Ok(()); + }; + + #[cfg(feature = "wasm-opt")] + { + use crate::config::WasmOptLevel; + + tracing::info!("Running optimization with wasm-opt..."); + + let mut options = match self.dioxus_crate.dioxus_config.web.wasm_opt.level { + WasmOptLevel::Z => { + wasm_opt::OptimizationOptions::new_optimize_for_size_aggressively() + } + WasmOptLevel::S => wasm_opt::OptimizationOptions::new_optimize_for_size(), + WasmOptLevel::Zero => wasm_opt::OptimizationOptions::new_opt_level_0(), + WasmOptLevel::One => wasm_opt::OptimizationOptions::new_opt_level_1(), + WasmOptLevel::Two => wasm_opt::OptimizationOptions::new_opt_level_2(), + WasmOptLevel::Three => wasm_opt::OptimizationOptions::new_opt_level_3(), + WasmOptLevel::Four => wasm_opt::OptimizationOptions::new_opt_level_4(), + }; + let wasm_file = bindgen_outdir.join(format!( + "{}_bg.wasm", + self.dioxus_crate.dioxus_config.application.name + )); + let old_size = wasm_file.metadata()?.len(); + options + // WASM bindgen relies on reference types + .enable_feature(wasm_opt::Feature::ReferenceTypes) + .debug_info(self.dioxus_crate.dioxus_config.web.wasm_opt.debug) + .run(&wasm_file, &wasm_file) + .map_err(|err| Error::Other(anyhow::anyhow!(err)))?; + + let new_size = wasm_file.metadata()?.len(); + tracing::info!( + "wasm-opt reduced WASM size from {} to {} ({:2}%)", + old_size, + new_size, + (new_size as f64 - old_size as f64) / old_size as f64 * 100.0 + ); } Ok(()) } /// Check if the wasm32-unknown-unknown target is installed and try to install it if not - pub(crate) async fn install_web_build_tooling(&self) -> Result<()> { + pub async fn install_web_build_tooling(&self) -> Result<()> { // If the user has rustup, we can check if the wasm32-unknown-unknown target is installed // Otherwise we can just assume it is installed - which is not great... // Eventually we can poke at the errors and let the user know they need to install the target @@ -120,7 +114,7 @@ impl BuildRequest { } // Attempt to automatically recover from a bindgen failure by updating the wasm-bindgen version - pub(crate) async fn update_wasm_bindgen_version() -> Result<()> { + pub async fn update_wasm_bindgen_version() -> Result<()> { let cli_bindgen_version = wasm_bindgen_shared::version(); tracing::info!("Attempting to recover from bindgen failure by setting the wasm-bindgen version to {cli_bindgen_version}..."); @@ -152,46 +146,6 @@ impl BuildRequest { Err(Error::BuildFailed(format!("WASM bindgen build failed!\nThis is probably due to the Bindgen version, dioxus-cli is using `{cli_bindgen_version}` which is not compatible with your crate.\nPlease reinstall the dioxus cli to fix this issue.\nYou can reinstall the dioxus cli by running `cargo install dioxus-cli --force` and then rebuild your project"))) } - #[cfg(feature = "wasm-opt")] - fn run_wasm_opt(&mut self, bindgen_outdir: &std::path::PathBuf) -> Result<(), Error> { - if !self.build_arguments.release { - return Ok(()); - }; - - use crate::config::WasmOptLevel; - - tracing::info!("Running optimization with wasm-opt..."); - let mut options = match self.dioxus_crate.dioxus_config.web.wasm_opt.level { - WasmOptLevel::Z => wasm_opt::OptimizationOptions::new_optimize_for_size_aggressively(), - WasmOptLevel::S => wasm_opt::OptimizationOptions::new_optimize_for_size(), - WasmOptLevel::Zero => wasm_opt::OptimizationOptions::new_opt_level_0(), - WasmOptLevel::One => wasm_opt::OptimizationOptions::new_opt_level_1(), - WasmOptLevel::Two => wasm_opt::OptimizationOptions::new_opt_level_2(), - WasmOptLevel::Three => wasm_opt::OptimizationOptions::new_opt_level_3(), - WasmOptLevel::Four => wasm_opt::OptimizationOptions::new_opt_level_4(), - }; - let wasm_file = bindgen_outdir.join(format!( - "{}_bg.wasm", - self.dioxus_crate.dioxus_config.application.name - )); - let old_size = wasm_file.metadata()?.len(); - options - // WASM bindgen relies on reference types - .enable_feature(wasm_opt::Feature::ReferenceTypes) - .debug_info(self.dioxus_crate.dioxus_config.web.wasm_opt.debug) - .run(&wasm_file, &wasm_file) - .map_err(|err| Error::Other(anyhow::anyhow!(err)))?; - let new_size = wasm_file.metadata()?.len(); - tracing::info!( - "wasm-opt reduced WASM size from {} to {} ({:2}%)", - old_size, - new_size, - (new_size as f64 - old_size as f64) / old_size as f64 * 100.0 - ); - - Ok(()) - } - /// Check if the build is targeting the web platform pub fn targeting_web(&self) -> bool { self.platform() == Platform::Web diff --git a/packages/cli/src/bundler.rs b/packages/cli/src/bundler.rs index 0263d7ac11..3b7f695256 100644 --- a/packages/cli/src/bundler.rs +++ b/packages/cli/src/bundler.rs @@ -6,3 +6,27 @@ mod win; mod app; pub use app::*; + +use serde::{Deserialize, Serialize}; + +#[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Debug)] +pub enum BundleFormat { + // Apple + Macos, + Ios, + + // wasm + Web, + + // Android + Android, + + // Linux + AppImage, + Deb, + Rpm, + + // Windows + Msi, + Wix, +} diff --git a/packages/cli/src/bundler/app.rs b/packages/cli/src/bundler/app.rs index f26902010b..8bbff991a4 100644 --- a/packages/cli/src/bundler/app.rs +++ b/packages/cli/src/bundler/app.rs @@ -1,43 +1,258 @@ use std::path::PathBuf; -use crate::{assets::AssetManifest, builder::Platform}; +use crate::link::InterceptedArgs; +use crate::Result; +use crate::{ + assets::{copy_dir_to, AssetManifest}, + link::LINK_OUTPUT_ENV_VAR, +}; +use anyhow::Context; +use core::str; +use futures_channel::mpsc::UnboundedSender; +use manganis_core::ResourceAsset; +use rayon::prelude::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator}; +use std::process::Stdio; +use std::{ + env::current_exe, + fs::{self, create_dir_all}, + io::Read, + sync::{atomic::AtomicUsize, Arc}, +}; +use std::{ + io::{BufWriter, Write}, + path::Path, +}; +use tokio::process::Command; +use tracing::Level; -pub struct AppBundle {} +use super::*; +use crate::DioxusCrate; +use crate::{ + build::BuildArgs, + builder::{BuildRequest, Platform}, + config::BundleConfig, +}; + +pub struct AppBundle { + pub build: BuildRequest, + pub workdir: PathBuf, + pub executable: PathBuf, + pub assets: AssetManifest, +} impl AppBundle { - pub fn new(_platform: Platform) -> Self { - todo!() + pub async fn new( + build: BuildRequest, + assets: AssetManifest, + executable: PathBuf, + ) -> Result { + let bundle = Self { + workdir: std::env::temp_dir(), + build, + executable, + assets, + }; + + bundle.write_main_executable().await?; + bundle.write_assets().await?; + bundle.write_metadata().await?; + bundle.optimize().await?; + + Ok(bundle) + } + + pub fn open(&self) {} + + /// Take the workdir and copy it to the output location, returning the path to final bundle + /// + /// Perform any finishing steps here: + /// - Signing the bundle + pub async fn finish(&self, destination: PathBuf) -> Result { + match self.build.platform() { + // Web is a simple fs copy of the workdir to the output location + Platform::Web => { + let output_location = destination.join(self.build.app_name()); + copy_dir_to(self.workdir.clone(), output_location.clone(), false)?; + Ok(output_location) + } + + // Create a .ipa, only from macOS + Platform::Ios => todo!(), + + // Create a .exe, from linux/mac/windows + Platform::Android => todo!(), + + // Create a final .app/.exe/etc depending on the host platform, not dependent on the host + Platform::Desktop => todo!(), + Platform::Server => todo!(), + Platform::Liveview => todo!(), + } + } + + /// Take the output of rustc and make it into the main exe of the bundle + /// + /// For wasm, we'll want to run `wasm-bindgen` to make it a wasm binary along with some other optimizations + /// Other platforms we might do some stripping or other optimizations + async fn write_main_executable(&self) -> Result<()> { + match self.build.platform() { + // Run wasm-bindgen on the wasm binary and set its output to be in the bundle folder + // Also run wasm-opt on the wasm binary, and sets the index.html since that's also the "executable". + // + // The wasm stuff will be in a folder called "wasm" in the workdir. + // + // Final output format: + // ``` + // dist/ + // web + // index.html + // wasm/ + // app.wasm + // glue.js + // snippets/ + // ... + // assets/ + // logo.png + // ``` + Platform::Web => { + // Run wasm-bindgen and drop its output into the assets folder under "dioxus" + self.build + .run_wasm_bindgen(&self.executable.with_extension("wasm"), &self.bindgen_dir()) + .await?; + + // Only run wasm-opt if the feature is enabled + // Wasm-opt has an expensive build script that makes it annoying to keep enabled for iterative dev + // We put it behind the "wasm-opt" feature flag so that it can be disabled when iterating on the cli + self.build.run_wasm_opt(&self.bindgen_dir())?; + + // Write the index.html file + std::fs::write(self.workdir.join("index.html"), self.build.prepare_html()?)?; + } + + Platform::Ios => {} + Platform::Desktop => {} + Platform::Server => {} + Platform::Liveview => {} + Platform::Android => todo!("android not yet supported!"), + } + + Ok(()) } - pub fn set_main_executable(&mut self) {} + fn bindgen_dir(&self) -> PathBuf { + self.workdir.join("wasm") + } /// Copy the assets out of the manifest and into the target location - pub async fn copy_assets(&mut self, _manifest: &AssetManifest) { - todo!() + /// + /// Should be the same on all platforms - just copy over the assets from the manifest into the output directory + async fn write_assets(&self) -> Result<()> { + let asset_dir = self.asset_dir(); + + let assets = self.all_source_assets(); + let asset_count = assets.len(); + let assets_finished = AtomicUsize::new(0); + + let optimize = false; + let pre_compress = false; + + // Parallel Copy over the assets and keep track of progress with an atomic counter + assets.par_iter().try_for_each(|asset| { + self.build.status_copying_asset( + assets_finished.fetch_add(0, std::sync::atomic::Ordering::SeqCst), + asset_count, + asset, + ); + + self.assets + .copy_asset_to(&asset_dir, asset, optimize, pre_compress); + + self.build.status_finished_asset( + assets_finished.fetch_add(1, std::sync::atomic::Ordering::SeqCst), + asset_count, + asset, + ); + + Ok(()) as anyhow::Result<()> + })?; + + Ok(()) } - pub fn finish(self) -> Self { - todo!() + pub fn all_source_assets(&self) -> Vec { + // Merge the legacy asset dir assets with the assets from the manifest + // Legacy assets need to retain their name in case they're referenced in the manifest + // todo: we should only copy over assets that appear in `img { src: "assets/logo.png" }` to + // properly deprecate the legacy asset dir + self.assets + .assets + .keys() + .cloned() + .chain(self.build.krate.legacy_asset_dir_files()) + .collect::>() } - pub fn open(&self) {} + async fn write_metadata(&self) -> Result<()> { + Ok(()) + } - /// Get the path to the executable - pub fn path(&self) -> PathBuf { - todo!() + pub fn asset_dir(&self) -> PathBuf { + let dir: PathBuf = match self.build.platform() { + Platform::Web => self.workdir.join("assets"), + Platform::Desktop => self.workdir.join("Resources"), + Platform::Ios => self.workdir.join("Resources"), + Platform::Android => self.workdir.join("assets"), + Platform::Server => self.workdir.join("assets"), + Platform::Liveview => self.workdir.join("assets"), + }; + + if !dir.exists() { + std::fs::create_dir_all(&dir).expect("Failed to create asset dir in temp dir"); + } + + dir } -} -/// The processed bundle infomrmation -#[derive(Clone)] -pub enum BundlePlatform { - MacOS, - Ios, - Fullstack, - Spa, - Msi, - Wix, - Deb, - Rpm, - AppImage, + /// Run the optimizers, obfuscators, minimizers, etc + pub async fn optimize(&self) -> Result<()> { + match self.build.platform() { + Platform::Web => { + // Compress the asset dir + // // If pre-compressing is enabled, we can pre_compress the wasm-bindgen output + // let pre_compress = self + // .krate + // .should_pre_compress_web_assets(self.build.release); + + // tokio::task::spawn_blocking(move || { + // pre_compress_folder(&bindgen_outdir, pre_compress) + // }) + // .await + // .unwrap()?; + } + Platform::Desktop => {} + Platform::Ios => {} + Platform::Android => {} + Platform::Server => {} + Platform::Liveview => {} + } + + Ok(()) + } + + /// The folder where the bundles will be built + /// + /// ``` + /// dist/ + /// app-windows-11-arch-x64.exe + /// app-macos-11-arch-x64.app + /// app-macos-11-installer.dmg + /// app-linux-11-arch-x64.AppImage + /// server.sh + /// web + /// index.html + /// assets + /// logo.png + /// ``` + fn bundle_root(&self) -> PathBuf { + todo!() + } } diff --git a/packages/cli/src/cli/build.rs b/packages/cli/src/cli/build.rs index 8572996655..fa581c6be6 100644 --- a/packages/cli/src/cli/build.rs +++ b/packages/cli/src/cli/build.rs @@ -1,5 +1,5 @@ use super::*; -use crate::builder::Platform; +use crate::builder::{Builder, Platform}; use crate::dioxus_crate::DioxusCrate; use anyhow::Context; use std::str::FromStr; @@ -109,6 +109,26 @@ pub struct TargetArgs { } impl BuildArgs { + pub async fn run(mut self) -> anyhow::Result<()> { + let mut dioxus_crate = + DioxusCrate::new(&self.target_args).context("Failed to load Dioxus workspace")?; + + self.build(&mut dioxus_crate).await?; + + Ok(()) + } + + pub async fn build(&mut self, dioxus_crate: &mut DioxusCrate) -> Result<()> { + self.resolve(dioxus_crate)?; + + // todo: probably want to consume the logs from the builder here, instead of just waiting for it to finish + Builder::start(dioxus_crate, self.clone())? + .wait_for_finish() + .await; + + Ok(()) + } + /// Update the arguments of the CLI by inspecting the DioxusCrate itself and learning about how /// the user has configured their app. /// @@ -130,25 +150,7 @@ impl BuildArgs { Ok(()) } - pub async fn build(&mut self, dioxus_crate: &mut DioxusCrate) -> Result<()> { - self.resolve(dioxus_crate)?; - - // todo: probably want to consume the logs from the builder here, instead of just waiting for it to finish - crate::builder::Builder::start(dioxus_crate, self.clone())? - .wait_for_finish() - .await; - - Ok(()) - } - - pub async fn run(&mut self) -> anyhow::Result<()> { - let mut dioxus_crate = - DioxusCrate::new(&self.target_args).context("Failed to load Dioxus workspace")?; - self.build(&mut dioxus_crate).await?; - Ok(()) - } - - pub(crate) fn auto_detect_client_platform( + pub fn auto_detect_client_platform( &self, resolved: &DioxusCrate, ) -> (Option, Platform) { @@ -158,7 +160,7 @@ impl BuildArgs { .unwrap_or_else(|| (Some("web".to_string()), Platform::Web)) } - pub(crate) fn auto_detect_server_feature(&self, resolved: &DioxusCrate) -> Option { + pub fn auto_detect_server_feature(&self, resolved: &DioxusCrate) -> Option { self.find_dioxus_feature(resolved, |platform| matches!(platform, Platform::Server)) .map(|(feature, _)| feature) .unwrap_or_else(|| Some("server".to_string())) diff --git a/packages/cli/src/cli/create.rs b/packages/cli/src/cli/create.rs index c859aa1ff2..57bcfcad36 100644 --- a/packages/cli/src/cli/create.rs +++ b/packages/cli/src/cli/create.rs @@ -3,7 +3,7 @@ use cargo_generate::{GenerateArgs, TemplatePath}; use cargo_metadata::Metadata; use std::path::Path; -pub(crate) static DEFAULT_TEMPLATE: &str = "gh:dioxuslabs/dioxus-template"; +pub static DEFAULT_TEMPLATE: &str = "gh:dioxuslabs/dioxus-template"; #[derive(Clone, Debug, Default, Deserialize, Parser)] #[clap(name = "new")] diff --git a/packages/cli/src/cli/httpserver.rs b/packages/cli/src/cli/httpserver.rs new file mode 100644 index 0000000000..4f7f94cfca --- /dev/null +++ b/packages/cli/src/cli/httpserver.rs @@ -0,0 +1,16 @@ +use std::process::exit; + +use dioxus_rsx::{BodyNode, CallBody, TemplateBody}; + +use super::*; + +/// Translate some source file into Dioxus code +#[derive(Clone, Debug, Parser)] +#[clap(name = "http-server")] +pub struct Httpserver {} + +impl Httpserver { + pub async fn serve(self) -> Result<()> { + todo!() + } +} diff --git a/packages/cli/src/cli/mod.rs b/packages/cli/src/cli/mod.rs index 978f0464b3..1d836b9aa2 100644 --- a/packages/cli/src/cli/mod.rs +++ b/packages/cli/src/cli/mod.rs @@ -5,6 +5,7 @@ pub mod check; pub mod clean; pub mod config; pub mod create; +pub mod httpserver; pub mod init; pub mod link; pub mod serve; @@ -79,6 +80,10 @@ pub enum Commands { #[clap(name = "check")] Check(check::Check), + /// Start a local http server, akin to a default fullstack app + #[clap(name = "http-server")] + HttpServer(httpserver::Httpserver), + /// Dioxus config file controls. #[clap(subcommand)] Config(config::Config), @@ -97,6 +102,7 @@ impl Display for Commands { Commands::Autoformat(_) => write!(f, "fmt"), Commands::Check(_) => write!(f, "check"), Commands::Bundle(_) => write!(f, "bundle"), + Commands::HttpServer(_) => write!(f, "http-server"), } } } diff --git a/packages/cli/src/cli/serve.rs b/packages/cli/src/cli/serve.rs index a2f801539c..bbab882571 100644 --- a/packages/cli/src/cli/serve.rs +++ b/packages/cli/src/cli/serve.rs @@ -1,10 +1,10 @@ +use super::*; use crate::config::AddressArguments; use crate::settings; use crate::DioxusCrate; use anyhow::Context; use build::BuildArgs; - -use super::*; +use crossterm::tty::IsTty; /// Run the WASM project on dev-server #[derive(Clone, Debug, Default, Parser)] @@ -40,16 +40,17 @@ pub struct ServeArgs { #[clap(long, default_missing_value = "2")] pub wsl_file_poll_interval: Option, - /// Arguments for the dioxus build - #[clap(flatten)] - pub(crate) build_arguments: BuildArgs, - /// Run the server in interactive mode #[arg(long, default_missing_value="true", num_args=0..=1, short = 'i')] pub interactive: Option, + + /// Arguments for the build itself + #[clap(flatten)] + pub build_arguments: BuildArgs, } impl ServeArgs { + /// Start the tui, builder, etc by resolving the arguments and then running the actual top-level serve function pub async fn serve(mut self) -> Result<()> { let mut krate = DioxusCrate::new(&self.build_arguments.target_args) .context("Failed to load Dioxus workspace")?; @@ -105,6 +106,14 @@ impl ServeArgs { pub fn should_hotreload(&self) -> bool { self.hot_reload.unwrap_or(true) } + + pub fn build_args(&self) -> BuildArgs { + self.build_arguments.clone() + } + + pub fn interactive_tty(&self) -> bool { + std::io::stdout().is_tty() && self.interactive.unwrap_or(true) + } } impl std::ops::Deref for ServeArgs { diff --git a/packages/cli/src/dioxus_crate.rs b/packages/cli/src/dioxus_crate.rs index fbd3c9b5da..6b7afae7d8 100644 --- a/packages/cli/src/dioxus_crate.rs +++ b/packages/cli/src/dioxus_crate.rs @@ -61,11 +61,28 @@ impl DioxusCrate { /// Compose an asset directory. Represents the typical "public" directory /// with publicly available resources (configurable in the `Dioxus.toml`). - pub fn asset_dir(&self) -> PathBuf { + pub fn legacy_asset_dir(&self) -> PathBuf { self.crate_dir() .join(&self.dioxus_config.application.asset_dir) } + /// Get the list of files in the legacy asset directory + pub fn legacy_asset_dir_files(&self) -> Vec { + let mut files = vec![]; + + let Ok(read_dir) = self.legacy_asset_dir().read_dir() else { + return files; + }; + + for entry in read_dir { + if let Ok(entry) = entry { + files.push(entry.path()); + } + } + + files + } + /// Compose an out directory. Represents the typical "dist" directory that /// is "distributed" after building an application (configurable in the /// `Dioxus.toml`). diff --git a/packages/cli/src/fastfs.rs b/packages/cli/src/fastfs.rs new file mode 100644 index 0000000000..bbf836ef02 --- /dev/null +++ b/packages/cli/src/fastfs.rs @@ -0,0 +1,4 @@ +//! Methods for working with the filesystem that are faster than the std fs methods +//! Uses stuff like rayon, caching, and other optimizations +//! +//! Allows configuration in case you want to do some work while copying and allows you to track progress diff --git a/packages/cli/src/main.rs b/packages/cli/src/main.rs index 2d8b43d9a2..f16411d43c 100644 --- a/packages/cli/src/main.rs +++ b/packages/cli/src/main.rs @@ -11,12 +11,14 @@ pub mod config; pub mod dioxus_crate; pub mod dx_build_info; pub mod error; +pub mod fastfs; pub mod metadata; pub mod serve; pub mod settings; pub mod tools; pub mod tracer; +pub use bundler::AppBundle; pub use cli::*; pub use dioxus_crate::*; pub use error::*; @@ -35,55 +37,29 @@ async fn main() -> anyhow::Result<()> { } let args = Cli::parse(); - match args.action { Translate(opts) => opts .translate() - .context(error_wrapper("Translation of HTML into RSX failed")), - - New(opts) => opts - .create() - .context(error_wrapper("Creating new project failed")), - - Init(opts) => opts - .init() - .context(error_wrapper("Initializing a new project failed")), - - Config(opts) => opts - .config() - .context(error_wrapper("Configuring new project failed")), - - Autoformat(opts) => opts - .autoformat() - .context(error_wrapper("Error autoformatting RSX")), - - Check(opts) => opts - .check() - .await - .context(error_wrapper("Error checking RSX")), - - Build(mut opts) => opts - .run() - .await - .context(error_wrapper("Building project failed")), - - Clean(opts) => opts - .clean() - .context(error_wrapper("Cleaning project failed")), - - Serve(opts) => opts - .serve() - .await - .context(error_wrapper("Serving project failed")), - - Bundle(opts) => opts - .bundle() - .await - .context(error_wrapper("Bundling project failed")), - } -} + .context("⛔️ Translation of HTML into RSX failed:"), + + New(opts) => opts.create().context("🚫 Creating new project failed:"), + + Init(opts) => opts.init().context("🚫 Initializing a new project failed:"), + + Config(opts) => opts.config().context("🚫 Configuring new project failed:"), + + Autoformat(opts) => opts.autoformat().context("🚫 Error autoformatting RSX:"), -/// Simplifies error messages that use the same pattern. -fn error_wrapper(message: &str) -> String { - format!("🚫 {message}:") + Check(opts) => opts.check().await.context("🚫 Error checking RSX:"), + + Clean(opts) => opts.clean().context("🚫 Cleaning project failed:"), + + Build(opts) => opts.run().await.context("🚫 Building project failed:"), + + Serve(opts) => opts.serve().await.context("🚫 Serving project failed:"), + + Bundle(opts) => opts.bundle().await.context("🚫 Bundling project failed:"), + + HttpServer(opts) => opts.serve().await.context("🚫 Serving project failed:"), + } } diff --git a/packages/cli/src/metadata.rs b/packages/cli/src/metadata.rs index b9acbdf751..3eab9b3afb 100644 --- a/packages/cli/src/metadata.rs +++ b/packages/cli/src/metadata.rs @@ -32,7 +32,7 @@ const MAX_ANCESTORS: u32 = 10; /// Returns the root of the crate that the command is run from /// /// If the command is run from the workspace root, this will return the top-level Cargo.toml -pub(crate) fn crate_root() -> Result { +pub fn crate_root() -> Result { // From the current directory we work our way up, looking for `Cargo.toml` env::current_dir() .ok() diff --git a/packages/cli/src/serve/detect.rs b/packages/cli/src/serve/detect.rs index 504731da14..8518d8d847 100644 --- a/packages/cli/src/serve/detect.rs +++ b/packages/cli/src/serve/detect.rs @@ -3,7 +3,7 @@ /// We determine this based on whether the keyword `microsoft` or `wsl` is contained within the [`WSL_1`] or [`WSL_2`] files. /// This may fail in the future as it isn't guaranteed by Microsoft. /// See https://github.com/microsoft/WSL/issues/423#issuecomment-221627364 -pub(crate) fn is_wsl() -> bool { +pub fn is_wsl() -> bool { const WSL_1: &str = "/proc/sys/kernel/osrelease"; const WSL_2: &str = "/proc/version"; const WSL_KEYWORDS: [&str; 2] = ["microsoft", "wsl"]; diff --git a/packages/cli/src/serve/mod.rs b/packages/cli/src/serve/mod.rs index 1f000d9ed8..d485cda89c 100644 --- a/packages/cli/src/serve/mod.rs +++ b/packages/cli/src/serve/mod.rs @@ -1,6 +1,6 @@ use crate::builder::{BuildUpdate, Builder, Platform, Stage, UpdateBuildProgress, UpdateStage}; use crate::cli::serve::ServeArgs; -use crate::dioxus_crate::DioxusCrate; +use crate::DioxusCrate; use crate::Result; mod detect; @@ -10,13 +10,16 @@ mod output; mod proxy; mod runner; mod server; +mod tracer; mod update; mod util; mod watcher; +use dioxus_html::tr; use output::*; use runner::*; use server::*; +use tracer::*; use update::*; use util::*; use watcher::*; @@ -30,19 +33,12 @@ use watcher::*; /// - Web: we need to attach a filesystem server to our devtools webserver to serve the project. We /// want to emulate GithubPages here since most folks are deploying there and expect things like /// basepath to match. -/// - Fullstack: We spin up the same dev server but in this case the fullstack server itself needs to -/// proxy all dev requests to our dev server. Todo: we might just want "web" to use the -/// fullstack server by default, such that serving web on non-wasm platforms is just a -/// serving a proper server. /// - Desktop: We spin up the dev server but without a filesystem server. /// - Mobile: Basically the same as desktop. /// -/// -/// Notes: -/// - All filesystem changes are tracked here -/// - We send all updates to connected websocket connections. Even desktop connects via the websocket -/// - Right now desktop compiles tokio-tungstenite to do the connection but we could in theory reuse -/// the websocket logic from the webview for thinner builds. +/// When fullstack is enabled, we'll also build for the `server` target and then hotreload the server. +/// The "server" is special here since "fullstack" is functionaly just an addition to the regular client +/// setup. /// /// Todos(Jon): /// - I'd love to be able to configure the CLI while it's running so we can change settings on the fly. @@ -50,14 +46,12 @@ use watcher::*; /// - I want us to be able to detect a `server_fn` in the project and then upgrade from a static server /// to a dynamic one on the fly. pub async fn serve_all(args: ServeArgs, krate: DioxusCrate) -> Result<()> { - // Start each component of the devserver. - // Start the screen first, since it'll start to swallow the tracing logs from the rest of the the cli - let mut screen = Output::start(&args).expect("Failed to open terminal logger"); + // Start the tracer so it captures logs from the build engine before we start the builder + let mut tracer = TraceController::start(&args); // Note that starting the builder will queue up a build immediately - let mut builder = Builder::start(&krate, args.build_arguments.clone())?; - - // The watcher and devserver are started after but don't really matter in order + let mut builder = Builder::start(&krate, args.build_args())?; + let mut screen = Output::start(&args).expect("Failed to open terminal logger"); let mut devserver = DevServer::start(&args, &krate); let mut watcher = Watcher::start(&args, &krate); let mut runner = AppRunner::start(&args, &krate); @@ -76,6 +70,7 @@ pub async fn serve_all(args: ServeArgs, krate: DioxusCrate) -> Result<()> { msg = devserver.wait() => msg, msg = screen.wait() => msg, msg = runner.wait() => msg, + msg = tracer.wait() => msg, }; match msg { @@ -148,53 +143,39 @@ pub async fn serve_all(args: ServeArgs, krate: DioxusCrate) -> Result<()> { } } - ServeUpdate::BuildUpdate(BuildUpdate::BuildFailed { err, target }) => { + ServeUpdate::BuildUpdate(BuildUpdate::BuildFailed { err, .. }) => { devserver.send_build_error(err).await; } ServeUpdate::BuildUpdate(BuildUpdate::BuildReady { target, result }) => { - // if !results.is_empty() { - // builder.children.clear(); - // } - - // // If we have a build result, open it - // for build_result in results.iter() { - // let child = server.open(build_result); - - // match child { - // Ok(Some(child_proc)) => builder - // .children - // .push((build_result.target_platform, child_proc)), - // Err(e) => { - // tracing::error!("Failed to open build result: {e}"); - // break; - // } - // _ => {} - // } - // } - - // // Make sure we immediately capture the stdout/stderr of the executable - - // // otherwise it'll clobber our terminal output - // screen.new_ready_app(&mut builder, results); - - // // And then finally tell the server to reload - // server.send_reload_command().await; + match runner.open(result).await { + Ok(handle) => { + // Make sure we immediately capture the stdout/stderr of the executable - + // otherwise it'll clobber our terminal output + screen.new_ready_app(handle); + + // And then finally tell the server to reload + devserver.send_reload_command().await; + } + Err(e) => { + tracing::error!("Failed to open app: {}", e); + } + } } ServeUpdate::StdoutReceived { target, msg } => {} ServeUpdate::StderrReceived { target, msg } => {} - ServeUpdate::BuildUpdate(BuildUpdate::Finished) => { - // nothing - the builder just signals that there are no more pending builds - } + // nothing - the builder just signals that there are no more pending builds + // maybe ping the logger to wipe any logs and/or clear the screen + ServeUpdate::BuildUpdate(BuildUpdate::Finished) => {} // If the process exited *cleanly*, we can exit ServeUpdate::ProcessExited { status, target_platform, } => { - todo!("process exited") // // Then remove the child process // builder // .children @@ -214,13 +195,25 @@ pub async fn serve_all(args: ServeArgs, krate: DioxusCrate) -> Result<()> { } // Handle TUI input and maybe even rebuild the app - ServeUpdate::TuiInput { rebuild } => { - if rebuild { + ServeUpdate::TuiInput { event } => { + let should_rebuild = screen.handle_input(event)?; + if should_rebuild { builder.build(args.build_arguments.clone())?; devserver.start_build().await } } + ServeUpdate::TracingLog { log } => { + // screen.push_log( + // LogSource::Internal, + // BuildMessage { + // level: Level::INFO, + // message: MessageType::Text(log), + // source: MessageSource::Dev, + // }, + // ); + } + // A fatal error occured and we need to exit + cleanup ServeUpdate::Fatal { err } => break, } @@ -230,6 +223,7 @@ pub async fn serve_all(args: ServeArgs, krate: DioxusCrate) -> Result<()> { _ = devserver.shutdown().await; _ = screen.shutdown(); _ = builder.abort_all(); + _ = tracer.shutdown(); Ok(()) } diff --git a/packages/cli/src/serve/output.rs b/packages/cli/src/serve/output.rs index 45e7351287..55274f4de4 100644 --- a/packages/cli/src/serve/output.rs +++ b/packages/cli/src/serve/output.rs @@ -1,7 +1,7 @@ use crate::builder::*; use crate::config::AddressArguments; use crate::serve::ServeArgs; -use crate::{builder::Platform, dioxus_crate::DioxusCrate, tracer::CLILogControl}; +use crate::{builder::Platform, dioxus_crate::DioxusCrate}; use core::panic; use crossterm::{ event::{Event, EventStream, KeyCode, KeyModifiers, MouseEventKind}, @@ -18,13 +18,12 @@ use std::{ fmt::Display, io::{self, stdout}, rc::Rc, - sync::atomic::Ordering, time::{Duration, Instant}, }; use tracing::Level; -use super::{update::ServeUpdate, Builder, DevServer, Watcher}; +use super::{update::ServeUpdate, AppHandle, Builder, DevServer, Watcher}; #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum LogSource { @@ -70,7 +69,6 @@ impl BuildProgress { pub struct Output { term: Rc>>, - log_control: CLILogControl, // optional since when there's no tty there's no eventstream to read from - just stdin events: Option, @@ -79,7 +77,7 @@ pub struct Output { _rustc_nightly: bool, _dx_version: String, interactive: bool, - pub(crate) build_progress: BuildProgress, + pub build_progress: BuildProgress, is_cli_release: bool, platform: Platform, @@ -104,20 +102,12 @@ type TerminalBackend = Terminal>; impl Output { pub fn start(cfg: &ServeArgs) -> io::Result { - // Start a tracing instance just for serving. - // This ensures that any tracing we do while serving doesn't break the TUI itself, and instead is - // redirected to the serve process. - let log_control = crate::tracer::build_tracing(); - - let interactive = std::io::stdout().is_tty() && cfg.interactive.unwrap_or(true); - + let interactive = cfg.interactive_tty(); let mut events = None; if interactive { - log_control.tui_enabled.store(true, Ordering::SeqCst); enable_raw_mode()?; stdout().execute(EnterAlternateScreen)?; - // workaround for ci where the terminal is not fully initialized // this stupid bug // https://github.com/crossterm-rs/crossterm/issues/659 @@ -159,14 +149,13 @@ impl Output { Ok(Self { term: Rc::new(RefCell::new(term)), - log_control, events, _rustc_version, _rustc_nightly, _dx_version: dx_version, - interactive, is_cli_release, platform, + interactive, fly_modal_open: false, build_progress: Default::default(), scroll: 0, @@ -180,7 +169,7 @@ impl Output { /// Add a message from stderr to the logs fn push_stderr(&mut self, platform: Platform, stderr: String) { - todo!() + // self.set_tab(Tab::BuildLog); // self.running_apps @@ -205,7 +194,7 @@ impl Output { /// Add a message from stdout to the logs fn push_stdout(&mut self, platform: Platform, stdout: String) { - todo!() + // self.running_apps // .get_mut(&platform) // .unwrap() @@ -232,84 +221,16 @@ impl Output { /// /// Also tick animations every few ms pub async fn wait(&mut self) -> ServeUpdate { - todo!() - // fn ok_and_some(f: F) -> impl Future - // where - // F: Future, E>>, - // { - // next_or_pending(async move { f.await.ok().flatten() }) - // } - - // let user_input = async { - // let events = self.events.as_mut()?; - // events.next().await - // }; - - // let user_input = ok_and_some(user_input.map(|e| e.transpose())); - - // let has_running_apps = !self.running_apps.is_empty(); - // let next_stdout = self.running_apps.values_mut().map(|app| { - // let future = async move { - // let (stdout, stderr) = match &mut app.output { - // Some(out) => ( - // ok_and_some(out.stdout.next_line()), - // ok_and_some(out.stderr.next_line()), - // ), - // None => return futures_util::future::pending().await, - // }; - - // tokio::select! { - // line = stdout => (app.result.target_platform, Some(line), None), - // line = stderr => (app.result.target_platform, None, Some(line)), - // } - // }; - // Box::pin(future) - // }); - - // let next_stdout = async { - // if has_running_apps { - // select_all(next_stdout).await.0 - // } else { - // futures_util::future::pending().await - // } - // }; - - // let tui_log_rx = &mut self.log_control.tui_rx; - // let next_tui_log = next_or_pending(tui_log_rx.next()); - - // tokio::select! { - // (platform, stdout, stderr) = next_stdout => { - // if let Some(stdout) = stdout { - // self.push_stdout(platform, stdout); - // } - // if let Some(stderr) = stderr { - // self.push_stderr(platform, stderr); - // } - // }, - - // // Handle internal CLI tracing logs. - // log = next_tui_log => { - // self.push_log(LogSource::Internal, BuildMessage { - // level: Level::INFO, - // message: MessageType::Text(log), - // source: MessageSource::Dev, - // }); - // } - - // event = user_input => { - // if self.handle_events(event).await? { - // return Ok(ServeUpdate::TuiInput { rebuild: true }); - // } - // } - // } + let event = tokio::select! { + Some(Ok(event)) = self.events.as_mut().unwrap().next(), if self.events.is_some() => event + }; - // Ok(ServeUpdate::TuiInput { rebuild: false }) + ServeUpdate::TuiInput { event } } pub fn shutdown(&mut self) -> io::Result<()> { // if we're a tty then we need to disable the raw mode if self.interactive { - self.log_control.tui_enabled.store(false, Ordering::SeqCst); disable_raw_mode()?; stdout().execute(LeaveAlternateScreen)?; self.drain_print_logs(); @@ -359,6 +280,36 @@ impl Output { /// Handle an input event, returning `true` if the event should cause the program to restart. pub fn handle_input(&mut self, input: Event) -> io::Result { + // let mut events = vec![event]; + + // // Collect all the events within the next 10ms in one stream + // let collect_events = async { + // loop { + // let Some(Ok(next)) = self.events.as_mut().unwrap().next().await else { + // break; + // }; + // events.push(next); + // } + // }; + // tokio::select! { + // _ = collect_events => {}, + // _ = tokio::time::sleep(Duration::from_millis(10)) => {} + // } + + // // Debounce events within the same frame + // let mut handled = HashSet::new(); + // for event in events { + // if !handled.contains(&event) { + // if self.handle_input(event.clone())? { + // // Restart the running app. + // return Ok(true); + // } + // handled.insert(event); + // } + // } + + // Ok(false) + // handle ctrlc if let Event::Key(key) = input { if let KeyCode::Char('c') = key.code { @@ -517,8 +468,7 @@ impl Output { } } - pub fn new_ready_app(&mut self, build_engine: &mut Builder, target: Platform) { - todo!() + pub fn new_ready_app(&mut self, handle: &AppHandle) { // for result in results { // let out = build_engine // .finished @@ -810,38 +760,6 @@ impl Output { }); } - async fn handle_events(&mut self, event: Event) -> io::Result { - let mut events = vec![event]; - - // Collect all the events within the next 10ms in one stream - let collect_events = async { - loop { - let Some(Ok(next)) = self.events.as_mut().unwrap().next().await else { - break; - }; - events.push(next); - } - }; - tokio::select! { - _ = collect_events => {}, - _ = tokio::time::sleep(Duration::from_millis(10)) => {} - } - - // Debounce events within the same frame - let mut handled = HashSet::new(); - for event in events { - if !handled.contains(&event) { - if self.handle_input(event.clone())? { - // Restart the running app. - return Ok(true); - } - handled.insert(event); - } - } - - Ok(false) - } - fn render_fly_modal(&mut self, frame: &mut Frame, area: Rect) { if !self.fly_modal_open { return; diff --git a/packages/cli/src/serve/proxy.rs b/packages/cli/src/serve/proxy.rs index 279de3b70f..9939525603 100644 --- a/packages/cli/src/serve/proxy.rs +++ b/packages/cli/src/serve/proxy.rs @@ -91,7 +91,7 @@ pub fn add_proxy(mut router: Router, proxy: &WebProxyConfig) -> Result { Ok(router) } -pub(crate) fn proxy_to( +pub fn proxy_to( url: Uri, nocache: bool, handle_error: fn(Error) -> Response, diff --git a/packages/cli/src/serve/runner.rs b/packages/cli/src/serve/runner.rs index 026c8bdd18..401cd4f51e 100644 --- a/packages/cli/src/serve/runner.rs +++ b/packages/cli/src/serve/runner.rs @@ -1,14 +1,15 @@ -use std::collections::HashMap; +use std::{collections::HashMap, fs, net::SocketAddr, path::PathBuf, process::Stdio}; use crate::{ - builder::{BuildResult, BuildUpdate, Platform}, + builder::{AppBundle, BuildUpdate, Platform}, cli::serve::ServeArgs, - DioxusCrate, + DioxusCrate, Result, }; use tokio::{ io::{AsyncBufReadExt, BufReader, Lines}, - process::{ChildStderr, ChildStdout}, + process::{ChildStderr, ChildStdout, Command}, }; +use uuid::Uuid; use super::ServeUpdate; @@ -25,12 +26,14 @@ use tokio::process::Child; /// A handle to a running app pub struct AppHandle { - pub app: BuildResult, + pub app: AppBundle, + pub executable: PathBuf, pub child: Option, pub stdout: Lines>, pub stderr: Lines>, pub stdout_line: String, pub stderr_line: String, + pub id: Uuid, } impl AppRunner { @@ -54,6 +57,155 @@ impl AppRunner { // ((target, exit_status), _, _) = futures_util::future::select_all(processes) => { // BuildUpdate::ProcessExited { status: exit_status, target_platform: target } // } + + // let has_running_apps = !self.running_apps.is_empty(); + // let next_stdout = self.running_apps.values_mut().map(|app| { + // let future = async move { + // let (stdout, stderr) = match &mut app.output { + // Some(out) => ( + // ok_and_some(out.stdout.next_line()), + // ok_and_some(out.stderr.next_line()), + // ), + // None => return futures_util::future::pending().await, + // }; + + // tokio::select! { + // line = stdout => (app.result.target_platform, Some(line), None), + // line = stderr => (app.result.target_platform, None, Some(line)), + // } + // }; + // Box::pin(future) + // }); + + // let next_stdout = async { + // if has_running_apps { + // select_all(next_stdout).await.0 + // } else { + // futures_util::future::pending().await + // } + // }; + // (platform, stdout, stderr) = next_stdout => { + // if let Some(stdout) = stdout { + // self.push_stdout(platform, stdout); + // } + // if let Some(stderr) = stderr { + // self.push_stderr(platform, stderr); + // } + // }, + + // pending + futures_util::future::pending().await + } + + /// Finally "bundle" this app and return a handle to it + pub async fn open(&self, build: AppBundle) -> Result<&AppHandle> { + let ip = self.ip().to_string(); + + if build.build.platform() == Platform::Server { + tracing::trace!( + "Proxying fullstack server from port {:?}", + self.fullstack_address() + ); + } + + let work_dir = std::env::temp_dir(); + + // First, we need to "install" the app + let exe = build.finish(work_dir).await?; + let mut open = match build.build.platform() { + // Run `dx http-server` to serve the app + Platform::Web => todo!(), + + // Open up the .ipa for the .app + Platform::Ios => todo!(), + + Platform::Desktop => Command::new("open"), + Platform::Android => todo!("Android not supported yet"), + Platform::Server | Platform::Liveview => Command::new(exe.display().to_string()), + }; + + // open the exe with some arguments/envvars/etc + // we're going to try and configure this binary from the environment, if we can + // + // web can't be configured like this, so instead, we'll need to plumb a meta tag into the + // index.html during dev + let _ = open + .env( + dioxus_runtime_config::FULLSTACK_ADDRESS_ENV, + self.fullstack_address() + .as_ref() + .map(|addr| addr.to_string()) + .unwrap_or_else(|| "127.0.0.1:8080".to_string()), + ) + .env( + dioxus_runtime_config::IOS_DEVSERVER_ADDR_ENV, + format!("ws://{}/_dioxus", ip), + ) + .env( + dioxus_runtime_config::DEVSERVER_RAW_ADDR_ENV, + format!("ws://{}/_dioxus", ip), + ) + .env("CARGO_MANIFEST_DIR", build.build.krate.crate_dir()) + .stderr(Stdio::piped()) + .stdout(Stdio::piped()) + .kill_on_drop(true); + + todo!() + } + + fn install_app(&self, build: &AppBundle) -> Result<()> { + todo!() + } + + fn fullstack_address(&self) -> Option { + todo!() + } + + fn ip(&self) -> SocketAddr { todo!() } + + fn open_bundled_ios_app(&self, build: &AppBundle) -> std::io::Result> { + // command = "xcrun" + // args = [ + // "simctl", + // "install", + // "booted", + // "target/aarch64-apple-ios-sim/debug/bundle/ios/DioxusApp.app", + // ] + + // [tasks.run_ios_sim] + // args = ["simctl", "launch", "--console", "booted", "com.dioxuslabs"] + // command = "xcrun" + // dependencies = ["build_ios_sim", "install_ios_sim"] + + // [tasks.serve-sim] + // dependencies = ["build_ios_sim", "install_ios_sim", "run_ios_sim"] + + // APP_PATH="target/aarch64-apple-ios/debug/bundle/ios/DioxusApp.app" + + // # get the device id by jq-ing the json of the device list + // xcrun devicectl list devices --json-output target/deviceid.json + // DEVICE_UUID=$(jq -r '.result.devices[0].identifier' target/deviceid.json) + + // xcrun devicectl device install app --device "${DEVICE_UUID}" "${APP_PATH}" --json-output target/xcrun.json + + // # get the installation url by jq-ing the json of the device install + // INSTALLATION_URL=$(jq -r '.result.installedApplications[0].installationURL' target/xcrun.json) + + // # launch the app + // # todo: we can just background it immediately and then pick it up for loading its logs + // xcrun devicectl device process launch --device "${DEVICE_UUID}" "${INSTALLATION_URL}" + + // # # launch the app and put it in background + // # xcrun devicectl device process launch --no-activate --verbose --device "${DEVICE_UUID}" "${INSTALLATION_URL}" --json-output "${XCRUN_DEVICE_PROCESS_LAUNCH_LOG_DIR}" + + // # # Extract background PID of status app + // # STATUS_PID=$(jq -r '.result.process.processIdentifier' "${XCRUN_DEVICE_PROCESS_LAUNCH_LOG_DIR}") + // # "${GIT_ROOT}/scripts/wait-for-metro-port.sh" 2>&1 + + // # # now that metro is ready, resume the app from background + // # xcrun devicectl device process resume --device "${DEVICE_UUID}" --pid "${STATUS_PID}" > "${XCRUN_DEVICE_PROCESS_RESUME_LOG_DIR}" 2>&1 + todo!("Open mobile apps") + } } diff --git a/packages/cli/src/serve/server.rs b/packages/cli/src/serve/server.rs index 447995357f..0e8b940e06 100644 --- a/packages/cli/src/serve/server.rs +++ b/packages/cli/src/serve/server.rs @@ -3,7 +3,7 @@ use crate::{ serve::{next_or_pending, ServeArgs}, }; use crate::{ - builder::{BuildRequest, BuildResult}, + builder::{AppBundle, BuildRequest}, dioxus_crate::DioxusCrate, }; use crate::{config::WebHttpsConfig, serve::update::ServeUpdate}; @@ -352,106 +352,6 @@ impl DevServer { .map(|port| SocketAddr::new(self.ip.ip(), port)) } - /// Open the executable if this is a native build - pub fn open(&self, build: &BuildResult) -> std::io::Result> { - match build.request.platform() { - Platform::Web => Ok(None), - Platform::Ios => self.open_bundled_ios_app(build), - Platform::Android => todo!("Android not supported yet"), - Platform::Desktop | Platform::Server | Platform::Liveview => { - self.open_unbundled_native_app(build) - } - } - } - - fn open_unbundled_native_app(&self, build: &BuildResult) -> std::io::Result> { - if build.request.platform() == Platform::Server { - tracing::trace!( - "Proxying fullstack server from port {:?}", - self.fullstack_address() - ); - } - - tracing::info!( - "Opening exectuable with dev server ip {}", - self.ip.to_string() - ); - - // open the exe with some arguments/envvars/etc - // we're going to try and configure this binary from the environment, if we can - // - // web can't be configured like this, so instead, we'll need to plumb a meta tag into the - // index.html during dev - let res = Command::new(build.bundle.path()) - .env( - dioxus_runtime_config::FULLSTACK_ADDRESS_ENV, - self.fullstack_address() - .as_ref() - .map(|addr| addr.to_string()) - .unwrap_or_else(|| "127.0.0.1:8080".to_string()), - ) - .env( - dioxus_runtime_config::IOS_DEVSERVER_ADDR_ENV, - format!("ws://{}/_dioxus", self.ip.to_string()), - ) - .env( - dioxus_runtime_config::DEVSERVER_RAW_ADDR_ENV, - format!("ws://{}/_dioxus", self.ip.to_string()), - ) - .env("CARGO_MANIFEST_DIR", build.request.krate.crate_dir()) - .stderr(Stdio::piped()) - .stdout(Stdio::piped()) - .kill_on_drop(true) - .current_dir(build.request.krate.workspace_dir()) - .spawn()?; - - Ok(Some(res)) - } - - fn open_bundled_ios_app(&self, build: &BuildResult) -> std::io::Result> { - // command = "xcrun" - // args = [ - // "simctl", - // "install", - // "booted", - // "target/aarch64-apple-ios-sim/debug/bundle/ios/DioxusApp.app", - // ] - - // [tasks.run_ios_sim] - // args = ["simctl", "launch", "--console", "booted", "com.dioxuslabs"] - // command = "xcrun" - // dependencies = ["build_ios_sim", "install_ios_sim"] - - // [tasks.serve-sim] - // dependencies = ["build_ios_sim", "install_ios_sim", "run_ios_sim"] - - // APP_PATH="target/aarch64-apple-ios/debug/bundle/ios/DioxusApp.app" - - // # get the device id by jq-ing the json of the device list - // xcrun devicectl list devices --json-output target/deviceid.json - // DEVICE_UUID=$(jq -r '.result.devices[0].identifier' target/deviceid.json) - - // xcrun devicectl device install app --device "${DEVICE_UUID}" "${APP_PATH}" --json-output target/xcrun.json - - // # get the installation url by jq-ing the json of the device install - // INSTALLATION_URL=$(jq -r '.result.installedApplications[0].installationURL' target/xcrun.json) - - // # launch the app - // # todo: we can just background it immediately and then pick it up for loading its logs - // xcrun devicectl device process launch --device "${DEVICE_UUID}" "${INSTALLATION_URL}" - - // # # launch the app and put it in background - // # xcrun devicectl device process launch --no-activate --verbose --device "${DEVICE_UUID}" "${INSTALLATION_URL}" --json-output "${XCRUN_DEVICE_PROCESS_LAUNCH_LOG_DIR}" - - // # # Extract background PID of status app - // # STATUS_PID=$(jq -r '.result.process.processIdentifier' "${XCRUN_DEVICE_PROCESS_LAUNCH_LOG_DIR}") - // # "${GIT_ROOT}/scripts/wait-for-metro-port.sh" 2>&1 - - // # # now that metro is ready, resume the app from background - // # xcrun devicectl device process resume --device "${DEVICE_UUID}" --pid "${STATUS_PID}" > "${XCRUN_DEVICE_PROCESS_RESUME_LOG_DIR}" 2>&1 - todo!("Open mobile apps") - } - /// Sets up and returns a router /// /// Steps include: @@ -703,7 +603,7 @@ pub fn get_rustls_without_mkcert(web_config: &WebHttpsConfig) -> Result<(String, } /// Open the browser to the address -pub(crate) fn open_browser(base_path: Option, address: SocketAddr, https: bool) { +pub fn open_browser(base_path: Option, address: SocketAddr, https: bool) { let protocol = if https { "https" } else { "http" }; let base_path = match base_path.as_deref() { Some(base_path) => format!("/{}", base_path.trim_matches('/')), diff --git a/packages/cli/src/serve/tracer.rs b/packages/cli/src/serve/tracer.rs new file mode 100644 index 0000000000..ffa147b647 --- /dev/null +++ b/packages/cli/src/serve/tracer.rs @@ -0,0 +1,108 @@ +use crate::cli::serve::ServeArgs; +use futures_channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender}; +use futures_util::StreamExt; +use std::{ + env, io, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, Mutex, + }, +}; +use tracing_subscriber::{prelude::*, EnvFilter}; +const LOG_ENV: &str = "DIOXUS_LOG"; + +use super::ServeUpdate; + +pub struct TraceController { + pub tui_rx: UnboundedReceiver, + pub tui_enabled: Arc, +} + +impl TraceController { + pub fn start(cfg: &ServeArgs) -> Self { + // Start a tracing instance just for serving. + // This ensures that any tracing we do while serving doesn't break the TUI itself, and instead is + // redirected to the serve process. + // If {LOG_ENV} is set, default to env, otherwise filter to cli + // and manganis warnings and errors from other crates + let mut filter = EnvFilter::new("error,dx=info,dioxus-cli=info"); + if env::var(LOG_ENV).is_ok() { + filter = EnvFilter::from_env(LOG_ENV); + } + + // Create writer controller and custom writer. + let (tui_tx, tui_rx) = unbounded(); + let tui_enabled = Arc::new(AtomicBool::new(true)); + + let writer_control = Self { + tui_rx, + tui_enabled: tui_enabled.clone(), + }; + + let cli_writer = Mutex::new(Writer { + stdout: io::stdout(), + tui_tx, + tui_enabled, + }); + + // Build tracing + let fmt_layer = tracing_subscriber::fmt::layer() + .with_writer(cli_writer) + .with_filter(filter); + + let sub = tracing_subscriber::registry().with(fmt_layer); + + #[cfg(feature = "tokio-console")] + let sub = sub.with(console_subscriber::spawn()); + + sub.init(); + + writer_control + } + + /// Wait for the internal logger to send a message + pub async fn wait(&mut self) -> ServeUpdate { + ServeUpdate::TracingLog { + log: self.tui_rx.next().await.expect("tracer should never die"), + } + } + + pub fn shutdown(&self) { + self.tui_enabled.store(false, Ordering::SeqCst); + } +} + +/// Represents the CLI's custom tracing writer for conditionally writing logs between outputs. +struct Writer { + stdout: io::Stdout, + tui_tx: UnboundedSender, + tui_enabled: Arc, +} + +// Implement a conditional writer so that logs are routed to the appropriate place. +impl io::Write for Writer { + fn write(&mut self, buf: &[u8]) -> io::Result { + if self.tui_enabled.load(Ordering::SeqCst) { + let len = buf.len(); + + let as_string = String::from_utf8(buf.to_vec()) + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + self.tui_tx + .unbounded_send(as_string) + .map_err(|e| io::Error::new(io::ErrorKind::BrokenPipe, e))?; + + Ok(len) + } else { + self.stdout.write(buf) + } + } + + fn flush(&mut self) -> io::Result<()> { + if !self.tui_enabled.load(Ordering::SeqCst) { + self.stdout.flush() + } else { + Ok(()) + } + } +} diff --git a/packages/cli/src/serve/update.rs b/packages/cli/src/serve/update.rs index 7a2983deef..6320bd847f 100644 --- a/packages/cli/src/serve/update.rs +++ b/packages/cli/src/serve/update.rs @@ -1,7 +1,9 @@ -use crate::builder::{BuildRequest, BuildResult, BuildUpdate, Platform, UpdateBuildProgress}; +use crate::builder::{AppBundle, BuildRequest, BuildUpdate, Platform, UpdateBuildProgress}; use axum::extract::ws::Message as WsMessage; use std::{path::PathBuf, process::ExitStatus}; +use super::LogSource; + /// One fat enum to rule them all.... /// /// Thanks to libraries like winit for the inspiration @@ -40,7 +42,12 @@ pub enum ServeUpdate { }, TuiInput { - rebuild: bool, + event: crossterm::event::Event, + }, + + TracingLog { + // source: LogSource, + log: String, }, Fatal { diff --git a/packages/cli/src/serve/util.rs b/packages/cli/src/serve/util.rs index 2c8c94c74b..103abe6640 100644 --- a/packages/cli/src/serve/util.rs +++ b/packages/cli/src/serve/util.rs @@ -8,7 +8,7 @@ use std::task::Poll; use tokio::task::yield_now; // Grab the output of a future that returns an option or wait forever -pub(crate) fn next_or_pending(f: F) -> impl Future +pub fn next_or_pending(f: F) -> impl Future where F: IntoFuture>, { diff --git a/packages/cli/src/serve/watcher.rs b/packages/cli/src/serve/watcher.rs index f8c08f3c4b..e528356b98 100644 --- a/packages/cli/src/serve/watcher.rs +++ b/packages/cli/src/serve/watcher.rs @@ -20,7 +20,7 @@ use std::{path::PathBuf, time::Duration}; /// directories. pub struct Watcher { rx: UnboundedReceiver, - krate: DioxusCrate, + _krate: DioxusCrate, file_map: FileMap, ignore: Gitignore, applied_hot_reload_message: Option, @@ -127,7 +127,7 @@ impl Watcher { Self { _tx: tx, - krate: krate.clone(), + _krate: krate.clone(), rx, _watcher: watcher, file_map, diff --git a/packages/cli/src/tracer.rs b/packages/cli/src/tracer.rs index 40216c3511..8b13789179 100644 --- a/packages/cli/src/tracer.rs +++ b/packages/cli/src/tracer.rs @@ -1,96 +1 @@ -use futures_channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender}; -use std::{ - env, io, - sync::{ - atomic::{AtomicBool, Ordering}, - Arc, Mutex, - }, -}; -use tracing_subscriber::{prelude::*, EnvFilter}; -const LOG_ENV: &str = "DIOXUS_LOG"; - -/// Build tracing infrastructure. -pub fn build_tracing() -> CLILogControl { - // If {LOG_ENV} is set, default to env, otherwise filter to cli - // and manganis warnings and errors from other crates - let mut filter = EnvFilter::new("error,dx=info,dioxus-cli=info"); - if env::var(LOG_ENV).is_ok() { - filter = EnvFilter::from_env(LOG_ENV); - } - - // Create writer controller and custom writer. - let (tui_tx, tui_rx) = unbounded(); - let tui_enabled = Arc::new(AtomicBool::new(false)); - - let writer_control = CLILogControl { - tui_rx, - tui_enabled: tui_enabled.clone(), - }; - let cli_writer = Mutex::new(CLIWriter::new(tui_enabled, tui_tx)); - - // Build tracing - let fmt_layer = tracing_subscriber::fmt::layer() - .with_writer(cli_writer) - .with_filter(filter); - let sub = tracing_subscriber::registry().with(fmt_layer); - - #[cfg(feature = "tokio-console")] - let sub = sub.with(console_subscriber::spawn()); - - sub.init(); - - writer_control -} - -/// Contains the sync primitives to control the CLIWriter. -pub struct CLILogControl { - pub tui_rx: UnboundedReceiver, - pub tui_enabled: Arc, -} - -/// Represents the CLI's custom tracing writer for conditionally writing logs between outputs. -pub struct CLIWriter { - stdout: io::Stdout, - tui_tx: UnboundedSender, - tui_enabled: Arc, -} - -impl CLIWriter { - /// Create a new CLIWriter with required sync primitives for conditionally routing logs. - pub fn new(tui_enabled: Arc, tui_tx: UnboundedSender) -> Self { - Self { - stdout: io::stdout(), - tui_tx, - tui_enabled, - } - } -} - -// Implement a conditional writer so that logs are routed to the appropriate place. -impl io::Write for CLIWriter { - fn write(&mut self, buf: &[u8]) -> io::Result { - if self.tui_enabled.load(Ordering::SeqCst) { - let len = buf.len(); - - let as_string = String::from_utf8(buf.to_vec()) - .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; - - self.tui_tx - .unbounded_send(as_string) - .map_err(|e| io::Error::new(io::ErrorKind::BrokenPipe, e))?; - - Ok(len) - } else { - self.stdout.write(buf) - } - } - - fn flush(&mut self) -> io::Result<()> { - if !self.tui_enabled.load(Ordering::SeqCst) { - self.stdout.flush() - } else { - Ok(()) - } - } -} diff --git a/packages/fullstack/Cargo.toml b/packages/fullstack/Cargo.toml index c0a5496b32..ede29feead 100644 --- a/packages/fullstack/Cargo.toml +++ b/packages/fullstack/Cargo.toml @@ -12,7 +12,7 @@ resolver = "2" [dependencies] # server functions -server_fn = { version = "0.6.5", features = ["json", "url", "browser"], default-features = false } +server_fn = { version = "0.6.5", features = ["json", "url"], default-features = false } dioxus_server_macro = { workspace = true } # axum @@ -82,7 +82,7 @@ mounted = ["dioxus-web?/mounted"] devtools = ["dep:dioxus-devtools", "dioxus-web?/devtools"] document = ["dioxus-web?/document"] web = ["dep:dioxus-web", "dep:web-sys"] -desktop = ["dep:dioxus-desktop", "server_fn/reqwest", "dioxus_server_macro/reqwest"] +desktop = ["dep:dioxus-desktop", "server_fn/reqwest", "dioxus_server_macro/reqwest", "server_fn/browser"] mobile = ["dep:dioxus-mobile"] default-tls = ["server_fn/default-tls"] rustls = ["server_fn/rustls", "dep:rustls", "dep:hyper-rustls"] From 49849f82290f89f306160579ef63bdb52bce4e9c Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 4 Sep 2024 22:08:50 -0700 Subject: [PATCH 063/139] bundled hotreload --- packages/cli/src/builder/cargo.rs | 2 +- packages/cli/src/builder/prepare_html.rs | 8 +- packages/cli/src/bundler/app.rs | 7 +- packages/cli/src/cli/httpserver.rs | 4 - packages/cli/src/main.rs | 3 + packages/cli/src/serve/mod.rs | 42 ++++--- packages/cli/src/serve/runner.rs | 152 ++++++++++++++--------- packages/cli/src/serve/tracer.rs | 43 ++++--- packages/cli/src/serve/watcher.rs | 41 +++--- packages/devtools-types/src/lib.rs | 3 - packages/devtools/tests/roundtrip.rs | 1 - packages/manganis-core/src/asset.rs | 7 +- packages/manganis/src/builder.rs | 2 +- 13 files changed, 175 insertions(+), 140 deletions(-) diff --git a/packages/cli/src/builder/cargo.rs b/packages/cli/src/builder/cargo.rs index 8886d01038..c081bda26e 100644 --- a/packages/cli/src/builder/cargo.rs +++ b/packages/cli/src/builder/cargo.rs @@ -9,7 +9,7 @@ use tokio::process::Command; impl BuildRequest { pub async fn build(self) -> Result { - tracing::info!("🚅 Running build [Desktop] command..."); + tracing::info!("🚅 Running build command..."); // Install any tooling that might be required for this build. self.verify_tooling().await?; diff --git a/packages/cli/src/builder/prepare_html.rs b/packages/cli/src/builder/prepare_html.rs index 91681c4ad6..8b7980fcbb 100644 --- a/packages/cli/src/builder/prepare_html.rs +++ b/packages/cli/src/builder/prepare_html.rs @@ -98,9 +98,9 @@ impl BuildRequest { " // We can't use a module script here because we need to start the script immediately when streaming - import("/{base_path}/assets/dioxus/{app_name}.js").then( + import("/{base_path}/wasm/{app_name}.js").then( ({ default: init }) => { - init("/{base_path}/assets/dioxus/{app_name}_bg.wasm").then((wasm) => { + init("/{base_path}/wasm/{app_name}_bg.wasm").then((wasm) => { if (wasm.__wbindgen_start == undefined) { wasm.main(); } @@ -121,8 +121,8 @@ impl BuildRequest { // And try to insert preload links for the wasm and js files *html = html.replace( " - + r#" + Result { + let workdir = build.krate.out_dir(); + std::fs::create_dir_all(&workdir)?; + let bundle = Self { - workdir: std::env::temp_dir(), + workdir, build, executable, assets, @@ -72,7 +75,7 @@ impl AppBundle { // Web is a simple fs copy of the workdir to the output location Platform::Web => { let output_location = destination.join(self.build.app_name()); - copy_dir_to(self.workdir.clone(), output_location.clone(), false)?; + // copy_dir_to(self.workdir.clone(), output_location.clone(), false)?; Ok(output_location) } diff --git a/packages/cli/src/cli/httpserver.rs b/packages/cli/src/cli/httpserver.rs index 4f7f94cfca..de54f1db00 100644 --- a/packages/cli/src/cli/httpserver.rs +++ b/packages/cli/src/cli/httpserver.rs @@ -1,7 +1,3 @@ -use std::process::exit; - -use dioxus_rsx::{BodyNode, CallBody, TemplateBody}; - use super::*; /// Translate some source file into Dioxus code diff --git a/packages/cli/src/main.rs b/packages/cli/src/main.rs index f16411d43c..2a6dc2a7eb 100644 --- a/packages/cli/src/main.rs +++ b/packages/cli/src/main.rs @@ -36,6 +36,9 @@ async fn main() -> anyhow::Result<()> { return link::dump_link_args(); } + // Start the tracer so it captures logs from the build engine before we start the builder + crate::serve::TraceController::initialize(); + let args = Cli::parse(); match args.action { Translate(opts) => opts diff --git a/packages/cli/src/serve/mod.rs b/packages/cli/src/serve/mod.rs index d485cda89c..aba8c245c5 100644 --- a/packages/cli/src/serve/mod.rs +++ b/packages/cli/src/serve/mod.rs @@ -15,11 +15,10 @@ mod update; mod util; mod watcher; -use dioxus_html::tr; use output::*; use runner::*; use server::*; -use tracer::*; +pub use tracer::*; use update::*; use util::*; use watcher::*; @@ -46,8 +45,7 @@ use watcher::*; /// - I want us to be able to detect a `server_fn` in the project and then upgrade from a static server /// to a dynamic one on the fly. pub async fn serve_all(args: ServeArgs, krate: DioxusCrate) -> Result<()> { - // Start the tracer so it captures logs from the build engine before we start the builder - let mut tracer = TraceController::start(&args); + let mut tracer = tracer::TraceController::start(); // Note that starting the builder will queue up a build immediately let mut builder = Builder::start(&krate, args.build_args())?; @@ -75,13 +73,13 @@ pub async fn serve_all(args: ServeArgs, krate: DioxusCrate) -> Result<()> { match msg { ServeUpdate::FilesChanged { files } => { - if files.is_empty() || args.should_hotreload() { + if files.is_empty() || !args.should_hotreload() { continue; } // if change is hotreloadable, hotreload it // and then send that update to all connected clients - if let Some(hr) = watcher.attempt_hot_reload(&krate, files) { + if let Some(hr) = watcher.attempt_hot_reload(files, &runner) { // Only send a hotreload message for templates and assets - otherwise we'll just get a full rebuild if hr.templates.is_empty() && hr.assets.is_empty() { continue; @@ -148,7 +146,9 @@ pub async fn serve_all(args: ServeArgs, krate: DioxusCrate) -> Result<()> { } ServeUpdate::BuildUpdate(BuildUpdate::BuildReady { target, result }) => { - match runner.open(result).await { + tracing::info!("Opening app for [{}]", target); + + match runner.open(result, devserver.ip).await { Ok(handle) => { // Make sure we immediately capture the stdout/stderr of the executable - // otherwise it'll clobber our terminal output @@ -196,22 +196,26 @@ pub async fn serve_all(args: ServeArgs, krate: DioxusCrate) -> Result<()> { // Handle TUI input and maybe even rebuild the app ServeUpdate::TuiInput { event } => { - let should_rebuild = screen.handle_input(event)?; - if should_rebuild { - builder.build(args.build_arguments.clone())?; - devserver.start_build().await + let should_rebuild = screen.handle_input(event); + match should_rebuild { + Ok(true) => { + builder.build(args.build_arguments.clone())?; + devserver.start_build().await + } + Ok(false) => {} + Err(_) => break, } } ServeUpdate::TracingLog { log } => { - // screen.push_log( - // LogSource::Internal, - // BuildMessage { - // level: Level::INFO, - // message: MessageType::Text(log), - // source: MessageSource::Dev, - // }, - // ); + screen.push_log( + LogSource::Internal, + crate::builder::BuildMessage { + level: tracing::Level::INFO, + message: crate::builder::MessageType::Text(log), + source: crate::builder::MessageSource::Dev, + }, + ); } // A fatal error occured and we need to exit + cleanup diff --git a/packages/cli/src/serve/runner.rs b/packages/cli/src/serve/runner.rs index 401cd4f51e..1fad12ffb0 100644 --- a/packages/cli/src/serve/runner.rs +++ b/packages/cli/src/serve/runner.rs @@ -1,18 +1,18 @@ -use std::{collections::HashMap, fs, net::SocketAddr, path::PathBuf, process::Stdio}; - +use super::ServeUpdate; use crate::{ builder::{AppBundle, BuildUpdate, Platform}, cli::serve::ServeArgs, DioxusCrate, Result, }; +use manganis_core::ResourceAsset; +use std::{collections::HashMap, fs, net::SocketAddr, path::PathBuf, process::Stdio}; +use tokio::process::Child; use tokio::{ io::{AsyncBufReadExt, BufReader, Lines}, process::{ChildStderr, ChildStdout, Command}, }; use uuid::Uuid; -use super::ServeUpdate; - pub struct AppRunner { /// Ongoing apps running in place /// @@ -22,23 +22,23 @@ pub struct AppRunner { pub running: HashMap, } -use tokio::process::Child; - /// A handle to a running app pub struct AppHandle { pub app: AppBundle, pub executable: PathBuf, - pub child: Option, - pub stdout: Lines>, - pub stderr: Lines>, - pub stdout_line: String, - pub stderr_line: String, pub id: Uuid, + pub child: Option, + // pub stdout: Lines>, + // pub stderr: Lines>, + // pub stdout_line: String, + // pub stderr_line: String, } impl AppRunner { pub fn start(serve: &ServeArgs, config: &DioxusCrate) -> Self { - todo!() + Self { + running: Default::default(), + } } pub async fn wait(&mut self) -> ServeUpdate { @@ -93,78 +93,89 @@ impl AppRunner { // } // }, - // pending futures_util::future::pending().await } /// Finally "bundle" this app and return a handle to it - pub async fn open(&self, build: AppBundle) -> Result<&AppHandle> { - let ip = self.ip().to_string(); - - if build.build.platform() == Platform::Server { - tracing::trace!( - "Proxying fullstack server from port {:?}", - self.fullstack_address() - ); + pub async fn open(&mut self, app: AppBundle, fullstack_addr: SocketAddr) -> Result<&AppHandle> { + let platform = app.build.platform(); + + if platform == Platform::Server { + tracing::trace!("Proxying fullstack server from port {:?}", fullstack_addr); } let work_dir = std::env::temp_dir(); + let executable = app.finish(work_dir).await?; + + // stdout: BufReader::new(stdout).lines(), + // stderr: BufReader::new(stderr).lines(), + // stdout_line: String::new(), + // stderr_line: String::new(), + + let handle = AppHandle { + app, + executable, + child: None, + // stdout: BufReader::new(stdout).lines(), + // stderr: BufReader::new(stderr).lines(), + // stdout_line: String::new(), + // stderr_line: String::new(), + id: Uuid::new_v4(), + }; + + if let Some(previous) = self.running.insert(platform, handle) { + // close the old app, gracefully, hopefully + } - // First, we need to "install" the app - let exe = build.finish(work_dir).await?; - let mut open = match build.build.platform() { - // Run `dx http-server` to serve the app - Platform::Web => todo!(), + Ok(self.running.get(&platform).unwrap()) - // Open up the .ipa for the .app - Platform::Ios => todo!(), + // // First, we need to "install" the app + // let exe = build.finish(work_dir).await?; + // let mut open = match build.build.platform() { + // // Run `dx http-server` to serve the app + // Platform::Web => todo!(), - Platform::Desktop => Command::new("open"), - Platform::Android => todo!("Android not supported yet"), - Platform::Server | Platform::Liveview => Command::new(exe.display().to_string()), - }; + // // Open up the .ipa for the .app + // Platform::Ios => todo!(), + + // Platform::Desktop => Command::new("open"), + // Platform::Android => todo!("Android not supported yet"), + // Platform::Server | Platform::Liveview => Command::new(exe.display().to_string()), + // }; // open the exe with some arguments/envvars/etc // we're going to try and configure this binary from the environment, if we can // // web can't be configured like this, so instead, we'll need to plumb a meta tag into the // index.html during dev - let _ = open - .env( - dioxus_runtime_config::FULLSTACK_ADDRESS_ENV, - self.fullstack_address() - .as_ref() - .map(|addr| addr.to_string()) - .unwrap_or_else(|| "127.0.0.1:8080".to_string()), - ) - .env( - dioxus_runtime_config::IOS_DEVSERVER_ADDR_ENV, - format!("ws://{}/_dioxus", ip), - ) - .env( - dioxus_runtime_config::DEVSERVER_RAW_ADDR_ENV, - format!("ws://{}/_dioxus", ip), - ) - .env("CARGO_MANIFEST_DIR", build.build.krate.crate_dir()) - .stderr(Stdio::piped()) - .stdout(Stdio::piped()) - .kill_on_drop(true); - - todo!() + // let _ = open + // .env( + // dioxus_runtime_config::FULLSTACK_ADDRESS_ENV, + // self.fullstack_address() + // .as_ref() + // .map(|addr| addr.to_string()) + // .unwrap_or_else(|| "127.0.0.1:8080".to_string()), + // ) + // .env( + // dioxus_runtime_config::IOS_DEVSERVER_ADDR_ENV, + // format!("ws://{}/_dioxus", ip), + // ) + // .env( + // dioxus_runtime_config::DEVSERVER_RAW_ADDR_ENV, + // format!("ws://{}/_dioxus", ip), + // ) + // .env("CARGO_MANIFEST_DIR", build.build.krate.crate_dir()) + // .stderr(Stdio::piped()) + // .stdout(Stdio::piped()) + // .kill_on_drop(true); + + // todo!() } fn install_app(&self, build: &AppBundle) -> Result<()> { todo!() } - fn fullstack_address(&self) -> Option { - todo!() - } - - fn ip(&self) -> SocketAddr { - todo!() - } - fn open_bundled_ios_app(&self, build: &AppBundle) -> std::io::Result> { // command = "xcrun" // args = [ @@ -209,3 +220,20 @@ impl AppRunner { todo!("Open mobile apps") } } + +impl AppHandle { + /// Update an asset in the running apps + /// + /// Might need to upload the asset to the simulator or overwrite it within the bundle + /// + /// Returns the name of the asset in the bundle if it exists + pub fn update_asset(&self, path: &PathBuf) -> Option { + let resource = self.app.assets.assets.get(path).cloned()?; + + self.app + .assets + .copy_asset_to(&self.app.asset_dir(), path, false, false); + + Some(resource.bundled.into()) + } +} diff --git a/packages/cli/src/serve/tracer.rs b/packages/cli/src/serve/tracer.rs index ffa147b647..4c962f2028 100644 --- a/packages/cli/src/serve/tracer.rs +++ b/packages/cli/src/serve/tracer.rs @@ -1,11 +1,11 @@ -use crate::cli::serve::ServeArgs; use futures_channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender}; use futures_util::StreamExt; +use once_cell::sync::OnceCell; use std::{ env, io, sync::{ atomic::{AtomicBool, Ordering}, - Arc, Mutex, + Mutex, }, }; use tracing_subscriber::{prelude::*, EnvFilter}; @@ -13,36 +13,28 @@ const LOG_ENV: &str = "DIOXUS_LOG"; use super::ServeUpdate; +static TUI_ENABLED: AtomicBool = AtomicBool::new(false); +static TUI_TX: OnceCell> = OnceCell::new(); + pub struct TraceController { pub tui_rx: UnboundedReceiver, - pub tui_enabled: Arc, } impl TraceController { - pub fn start(cfg: &ServeArgs) -> Self { + pub fn initialize() { // Start a tracing instance just for serving. // This ensures that any tracing we do while serving doesn't break the TUI itself, and instead is // redirected to the serve process. // If {LOG_ENV} is set, default to env, otherwise filter to cli // and manganis warnings and errors from other crates let mut filter = EnvFilter::new("error,dx=info,dioxus-cli=info"); + if env::var(LOG_ENV).is_ok() { filter = EnvFilter::from_env(LOG_ENV); } - // Create writer controller and custom writer. - let (tui_tx, tui_rx) = unbounded(); - let tui_enabled = Arc::new(AtomicBool::new(true)); - - let writer_control = Self { - tui_rx, - tui_enabled: tui_enabled.clone(), - }; - let cli_writer = Mutex::new(Writer { stdout: io::stdout(), - tui_tx, - tui_enabled, }); // Build tracing @@ -56,8 +48,15 @@ impl TraceController { let sub = sub.with(console_subscriber::spawn()); sub.init(); + } + + pub fn start() -> Self { + // Create writer controller and custom writer. + let (tui_tx, tui_rx) = unbounded(); + TUI_TX.set(tui_tx.clone()).unwrap(); + TUI_ENABLED.store(true, Ordering::SeqCst); - writer_control + Self { tui_rx } } /// Wait for the internal logger to send a message @@ -68,27 +67,27 @@ impl TraceController { } pub fn shutdown(&self) { - self.tui_enabled.store(false, Ordering::SeqCst); + TUI_ENABLED.store(false, Ordering::SeqCst); } } /// Represents the CLI's custom tracing writer for conditionally writing logs between outputs. struct Writer { stdout: io::Stdout, - tui_tx: UnboundedSender, - tui_enabled: Arc, } // Implement a conditional writer so that logs are routed to the appropriate place. impl io::Write for Writer { fn write(&mut self, buf: &[u8]) -> io::Result { - if self.tui_enabled.load(Ordering::SeqCst) { + if TUI_ENABLED.load(Ordering::SeqCst) { let len = buf.len(); let as_string = String::from_utf8(buf.to_vec()) .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; - self.tui_tx + TUI_TX + .get() + .unwrap() .unbounded_send(as_string) .map_err(|e| io::Error::new(io::ErrorKind::BrokenPipe, e))?; @@ -99,7 +98,7 @@ impl io::Write for Writer { } fn flush(&mut self) -> io::Result<()> { - if !self.tui_enabled.load(Ordering::SeqCst) { + if !TUI_ENABLED.load(Ordering::SeqCst) { self.stdout.flush() } else { Ok(()) diff --git a/packages/cli/src/serve/watcher.rs b/packages/cli/src/serve/watcher.rs index e528356b98..fd604fc872 100644 --- a/packages/cli/src/serve/watcher.rs +++ b/packages/cli/src/serve/watcher.rs @@ -1,4 +1,4 @@ -use super::detect::is_wsl; +use super::{detect::is_wsl, AppRunner}; use super::{hot_reloading_file_map::HotreloadError, update::ServeUpdate}; use crate::serve::hot_reloading_file_map::FileMap; use crate::{cli::serve::ServeArgs, dioxus_crate::DioxusCrate}; @@ -20,7 +20,7 @@ use std::{path::PathBuf, time::Duration}; /// directories. pub struct Watcher { rx: UnboundedReceiver, - _krate: DioxusCrate, + krate: DioxusCrate, file_map: FileMap, ignore: Gitignore, applied_hot_reload_message: Option, @@ -127,7 +127,7 @@ impl Watcher { Self { _tx: tx, - _krate: krate.clone(), + krate: krate.clone(), rx, _watcher: watcher, file_map, @@ -200,17 +200,16 @@ impl Watcher { pub fn attempt_hot_reload( &mut self, - krate: &DioxusCrate, modified_files: Vec, + runner: &AppRunner, ) -> Option { // If we have any changes to the rust files, we need to update the file map - let crate_dir = krate.crate_dir(); + let crate_dir = self.krate.crate_dir(); let mut templates = vec![]; // Prepare the hotreload message we need to send let mut edited_rust_files = Vec::new(); let mut assets = Vec::new(); - let mut unknown_files = Vec::new(); for path in modified_files { // for various assets that might be linked in, we just try to hotreloading them forcefully @@ -221,22 +220,37 @@ impl Watcher { match ext { "rs" => edited_rust_files.push(path), - _ if path.starts_with("assets") => assets.push(path), - _ => unknown_files.push(path), + + // Look through the runners to see if any of them have an asset that matches the path + _ => { + tracing::info!("Hotreloading asset {path:?}"); + for runner in runner.running.values() { + if let Some(bundled_name) = runner.update_asset(&path) { + tracing::info!("Hotreloading bundled asset {bundled_name:?}"); + assets.push(bundled_name); + } + } + } } } + assets.dedup(); + + // Process the rust files for rust_file in edited_rust_files { match self.file_map.update_rsx::(&rust_file, &crate_dir) { Ok(hotreloaded_templates) => { templates.extend(hotreloaded_templates); } + // If the file is not reloadable, we need to rebuild Err(HotreloadError::Notreloadable) => return None, + // The rust file may have failed to parse, but that is most likely // because the user is in the middle of adding new code // We just ignore the error and let Rust analyzer warn about the problem Err(HotreloadError::Parse) => {} + // Otherwise just log the error Err(err) => { tracing::error!("Error hotreloading file {rust_file:?}: {err}") @@ -244,11 +258,7 @@ impl Watcher { } } - let msg = HotReloadMsg { - templates, - assets, - unknown_files, - }; + let msg = HotReloadMsg { templates, assets }; self.add_hot_reload_message(&msg); @@ -280,18 +290,13 @@ impl Watcher { .collect(); let mut assets: HashSet = std::mem::take(&mut applied.assets).into_iter().collect(); - let mut unknown_files: HashSet = std::mem::take(&mut applied.unknown_files) - .into_iter() - .collect(); for template in &msg.templates { templates.insert(template.location.clone(), template.clone()); } assets.extend(msg.assets.iter().cloned()); - unknown_files.extend(msg.unknown_files.iter().cloned()); applied.templates = templates.into_values().collect(); applied.assets = assets.into_iter().collect(); - applied.unknown_files = unknown_files.into_iter().collect(); } } diff --git a/packages/devtools-types/src/lib.rs b/packages/devtools-types/src/lib.rs index 20355de3b7..22fe98c1f1 100644 --- a/packages/devtools-types/src/lib.rs +++ b/packages/devtools-types/src/lib.rs @@ -37,7 +37,4 @@ pub enum ClientMsg { pub struct HotReloadMsg { pub templates: Vec, pub assets: Vec, - - /// A file changed that's not an asset or a rust file - best of luck! - pub unknown_files: Vec, } diff --git a/packages/devtools/tests/roundtrip.rs b/packages/devtools/tests/roundtrip.rs index 45811a39eb..ca779625c9 100644 --- a/packages/devtools/tests/roundtrip.rs +++ b/packages/devtools/tests/roundtrip.rs @@ -23,7 +23,6 @@ fn roundtrip() { let line = serde_json::to_string(&DevserverMsg::HotReload(HotReloadMsg { templates: vec![], assets: vec!["/asd/bcc".into()], - unknown_files: vec![], })) .unwrap(); writer.write(line.as_bytes()).unwrap(); diff --git a/packages/manganis-core/src/asset.rs b/packages/manganis-core/src/asset.rs index afdcde19d6..3e01ac5edd 100644 --- a/packages/manganis-core/src/asset.rs +++ b/packages/manganis-core/src/asset.rs @@ -41,19 +41,20 @@ impl ResourceAsset { // /users/dioxus/dev/app/ // is the root of // /users/dioxus/dev/app/assets/blah.css - let mfst_dir = std::env::var("CARGO_MANIFEST_DIR") + let manifest_dir = std::env::var("CARGO_MANIFEST_DIR") .map(PathBuf::from) .unwrap(); // 1. the input file should be a pathbuf let input = PathBuf::from(raw); - // 2. - let absolute = mfst_dir + // 2. absolute path to the asset + let absolute = manifest_dir .join(raw.trim_start_matches('/')) .canonicalize() .unwrap(); + // 3. the bundled path is the unique name of the asset let bundled = Self::make_unique_name(absolute.clone()); Ok(Self { diff --git a/packages/manganis/src/builder.rs b/packages/manganis/src/builder.rs index 3eb92932d7..fff6d2fc83 100644 --- a/packages/manganis/src/builder.rs +++ b/packages/manganis/src/builder.rs @@ -42,7 +42,7 @@ impl Asset { // todo: actually properly resolve this base_path() - .unwrap_or_else(|| std::env::current_dir().unwrap_or("/".into())) + .unwrap_or_else(|| std::env::current_dir().unwrap_or("/assets/".into())) .join(PathBuf::from(self.bundled.trim_start_matches('/'))) } } From c34083460daacd246f0cc561138ffede0e586de9 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 4 Sep 2024 23:17:59 -0700 Subject: [PATCH 064/139] kick stylsheets --- packages/cli/src/assets.rs | 3 - packages/cli/src/builder/assets.rs | 34 ---- packages/cli/src/builder/mod.rs | 8 +- packages/cli/src/builder/prepare_html.rs | 215 ----------------------- packages/cli/src/builder/tooling.rs | 1 + packages/cli/src/builder/web.rs | 209 +++++++++++++++++++++- packages/cli/src/bundler/app.rs | 98 ++++------- packages/cli/src/bundler/web.rs | 8 + packages/cli/src/dioxus_crate.rs | 2 +- packages/cli/src/serve/mod.rs | 5 +- packages/cli/src/serve/output.rs | 8 +- packages/cli/src/serve/runner.rs | 80 ++++++--- packages/interpreter/src/js/hash.txt | 2 +- packages/interpreter/src/js/native.js | 2 +- packages/interpreter/src/ts/native.ts | 43 ++++- 15 files changed, 352 insertions(+), 366 deletions(-) delete mode 100644 packages/cli/src/builder/assets.rs delete mode 100644 packages/cli/src/builder/prepare_html.rs create mode 100644 packages/cli/src/builder/tooling.rs diff --git a/packages/cli/src/assets.rs b/packages/cli/src/assets.rs index 70226de70c..73fbe6b9c8 100644 --- a/packages/cli/src/assets.rs +++ b/packages/cli/src/assets.rs @@ -216,9 +216,6 @@ impl AssetManifest { // Otherwise, let's attempt to optimize the the asset we're copying } - - /// Sync the assets with the folder - will update any assets that have changed since the last sync - pub fn sync_with_folder(&self, destination: &Path) {} } pub fn copy_dir_to(src_dir: PathBuf, dest_dir: PathBuf, pre_compress: bool) -> std::io::Result<()> { diff --git a/packages/cli/src/builder/assets.rs b/packages/cli/src/builder/assets.rs deleted file mode 100644 index 5470e38910..0000000000 --- a/packages/cli/src/builder/assets.rs +++ /dev/null @@ -1,34 +0,0 @@ -use super::BuildRequest; -use super::Platform; -use crate::builder::progress::UpdateBuildProgress; -use crate::builder::progress::UpdateStage; -use crate::Result; -use crate::{ - assets::{copy_dir_to, AssetManifest}, - link::LINK_OUTPUT_ENV_VAR, -}; -use crate::{builder::progress::Stage, link::InterceptedArgs}; -use anyhow::Context; -use core::str; -use futures_channel::mpsc::UnboundedSender; -use manganis_core::ResourceAsset; -use rayon::prelude::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator}; -use std::{ - env::current_exe, - fs::{self, create_dir_all}, - io::Read, - sync::{atomic::AtomicUsize, Arc}, -}; -use std::{ - io::{BufWriter, Write}, - path::Path, -}; -use std::{path::PathBuf, process::Stdio}; -use tokio::process::Command; -use tracing::Level; - -impl BuildRequest { - // pub fn copy_assets_dir(&self) -> anyhow::Result<()> { - - // } -} diff --git a/packages/cli/src/builder/mod.rs b/packages/cli/src/builder/mod.rs index 1cb715ba83..0357f7634a 100644 --- a/packages/cli/src/builder/mod.rs +++ b/packages/cli/src/builder/mod.rs @@ -5,17 +5,15 @@ /// Uses a request -> response architecture that allows you to monitor the progress with an optional message /// receiver. mod builder; -mod request; -mod result; - -mod assets; mod bundle; mod cargo; mod handle; mod platform; -mod prepare_html; mod profiles; mod progress; +mod request; +mod result; +mod tooling; mod web; use crate::build::BuildArgs; diff --git a/packages/cli/src/builder/prepare_html.rs b/packages/cli/src/builder/prepare_html.rs deleted file mode 100644 index 8b7980fcbb..0000000000 --- a/packages/cli/src/builder/prepare_html.rs +++ /dev/null @@ -1,215 +0,0 @@ -//! Build the HTML file to load a web application. The index.html file may be created from scratch or modified from the `index.html` file in the crate root. - -use super::BuildRequest; -use crate::builder::progress::{ - BuildMessage, MessageSource, MessageType, Stage, UpdateBuildProgress, UpdateStage, -}; -use crate::Result; - -use std::fmt::Write; -use std::path::{Path, PathBuf}; -use tracing::Level; - -const DEFAULT_HTML: &str = include_str!("../../assets/index.html"); -const TOAST_HTML: &str = include_str!("../../assets/toast.html"); - -impl BuildRequest { - pub fn prepare_html(&self) -> Result { - let mut html = html_or_default(&self.krate.crate_dir()); - - // Inject any resources from the config into the html - self.inject_resources(&mut html)?; - - // Inject loading scripts if they are not already present - self.inject_loading_scripts(&mut html); - - // Replace any special placeholders in the HTML with resolved values - self.replace_template_placeholders(&mut html); - - let title = self.krate.dioxus_config.web.app.title.clone(); - - replace_or_insert_before("{app_title}", " bool { - !self.build.release - } - - // Inject any resources from the config into the html - fn inject_resources(&self, html: &mut String) -> Result<()> { - // Collect all resources into a list of styles and scripts - let resources = &self.krate.dioxus_config.web.resource; - let mut style_list = resources.style.clone().unwrap_or_default(); - let mut script_list = resources.script.clone().unwrap_or_default(); - - if self.is_dev_build() { - style_list.extend(resources.dev.style.iter().cloned()); - script_list.extend(resources.dev.script.iter().cloned()); - } - - let mut head_resources = String::new(); - - // Add all styles to the head - for style in &style_list { - writeln!( - &mut head_resources, - "", - &style.to_str().unwrap(), - )?; - } - - // Add all scripts to the head - for script in &script_list { - writeln!( - &mut head_resources, - "", - &script.to_str().unwrap(), - )?; - } - - if !style_list.is_empty() { - self.send_resource_deprecation_warning(style_list, ResourceType::Style); - } - if !script_list.is_empty() { - self.send_resource_deprecation_warning(script_list, ResourceType::Script); - } - - // Inject any resources from manganis into the head - // if let Some(assets) = assets { - // head_resources.push_str(&assets.head()); - // } - - replace_or_insert_before("{style_include}", " - // We can't use a module script here because we need to start the script immediately when streaming - import("/{base_path}/wasm/{app_name}.js").then( - ({ default: init }) => { - init("/{base_path}/wasm/{app_name}_bg.wasm").then((wasm) => { - if (wasm.__wbindgen_start == undefined) { - wasm.main(); - } - }); - } - ); - - {DX_TOAST_UTILITIES} - html.replace("{DX_TOAST_UTILITIES}", TOAST_HTML), - false => html.replace("{DX_TOAST_UTILITIES}", ""), - }; - - // And try to insert preload links for the wasm and js files - *html = html.replace( - " - - , variant: ResourceType) { - const RESOURCE_DEPRECATION_MESSAGE: &str = r#"The `web.resource` config has been deprecated in favor of head components and will be removed in a future release. Instead of including assets in the config, you can include assets with the `asset!` macro and add them to the head with `document::Link` and `Script` components."#; - - let replacement_components = paths - .iter() - .map(|path| { - let path = if path.exists() { - path.to_path_buf() - } else { - // If the path is absolute, make it relative to the current directory before we join it - // The path is actually a web path which is relative to the root of the website - let path = path.strip_prefix("/").unwrap_or(path); - let asset_dir_path = self.krate.legacy_asset_dir().join(path); - if let Ok(absolute_path) = asset_dir_path.canonicalize() { - let absolute_crate_root = self.krate.crate_dir().canonicalize().unwrap(); - PathBuf::from("./") - .join(absolute_path.strip_prefix(absolute_crate_root).unwrap()) - } else { - path.to_path_buf() - } - }; - match variant { - ResourceType::Style => { - format!(" Stylesheet {{ href: asset!(\"{}\") }}", path.display()) - } - ResourceType::Script => { - format!(" Script {{ src: asset!(\"{}\") }}", path.display()) - } - } - }) - .collect::>(); - let replacement_components = format!("rsx! {{\n{}\n}}", replacement_components.join("\n")); - let section_name = match variant { - ResourceType::Style => "web.resource.style", - ResourceType::Script => "web.resource.script", - }; - - let message = format!( - "{RESOURCE_DEPRECATION_MESSAGE}\nTo migrate to head components, remove `{section_name}` and include the following rsx in your root component:\n```rust\n{replacement_components}\n```" - ); - - _ = self.progress.unbounded_send(UpdateBuildProgress { - platform: self.platform(), - stage: Stage::OptimizingWasm, - update: UpdateStage::AddMessage(BuildMessage { - level: Level::WARN, - message: MessageType::Text(message), - source: MessageSource::Build, - }), - }); - } -} - -enum ResourceType { - Style, - Script, -} - -/// Read the html file from the crate root or use the default html file -fn html_or_default(crate_root: &Path) -> String { - let custom_html_file = crate_root.join("index.html"); - std::fs::read_to_string(custom_html_file).unwrap_or_else(|_| String::from(DEFAULT_HTML)) -} - -/// Replace a string or insert the new contents before a marker -fn replace_or_insert_before( - replace: &str, - or_insert_before: &str, - with: &str, - content: &mut String, -) { - if content.contains(replace) { - *content = content.replace(replace, with); - } else if let Some(pos) = content.find(or_insert_before) { - content.insert_str(pos, with); - } -} diff --git a/packages/cli/src/builder/tooling.rs b/packages/cli/src/builder/tooling.rs new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/packages/cli/src/builder/tooling.rs @@ -0,0 +1 @@ + diff --git a/packages/cli/src/builder/web.rs b/packages/cli/src/builder/web.rs index c5f436ba38..5ab8684183 100644 --- a/packages/cli/src/builder/web.rs +++ b/packages/cli/src/builder/web.rs @@ -1,14 +1,18 @@ use super::{BuildRequest, Platform}; -use crate::assets::pre_compress_folder; -use crate::builder::progress::Stage; -use crate::builder::progress::UpdateBuildProgress; -use crate::builder::progress::UpdateStage; +use crate::builder::progress::{ + BuildMessage, MessageSource, MessageType, Stage, UpdateBuildProgress, UpdateStage, +}; use crate::error::{Error, Result}; use anyhow::Context; +use std::fmt::Write; use std::path::{Path, PathBuf}; use tokio::process::Command; +use tracing::Level; use wasm_bindgen_cli_support::Bindgen; +const DEFAULT_HTML: &str = include_str!("../../assets/index.html"); +const TOAST_HTML: &str = include_str!("../../assets/toast.html"); + impl BuildRequest { pub async fn run_wasm_bindgen(&self, input_path: &Path, bindgen_outdir: &Path) -> Result<()> { tracing::info!("Running wasm-bindgen"); @@ -146,8 +150,205 @@ impl BuildRequest { Err(Error::BuildFailed(format!("WASM bindgen build failed!\nThis is probably due to the Bindgen version, dioxus-cli is using `{cli_bindgen_version}` which is not compatible with your crate.\nPlease reinstall the dioxus cli to fix this issue.\nYou can reinstall the dioxus cli by running `cargo install dioxus-cli --force` and then rebuild your project"))) } + pub fn prepare_html(&self) -> Result { + let mut html = { + let crate_root: &Path = &self.krate.crate_dir(); + let custom_html_file = crate_root.join("index.html"); + std::fs::read_to_string(custom_html_file).unwrap_or_else(|_| String::from(DEFAULT_HTML)) + }; + + // Inject any resources from the config into the html + self.inject_resources(&mut html)?; + + // Inject loading scripts if they are not already present + self.inject_loading_scripts(&mut html); + + // Replace any special placeholders in the HTML with resolved values + self.replace_template_placeholders(&mut html); + + let title = self.krate.dioxus_config.web.app.title.clone(); + + replace_or_insert_before("{app_title}", " bool { + !self.build.release + } + + // Inject any resources from the config into the html + fn inject_resources(&self, html: &mut String) -> Result<()> { + // Collect all resources into a list of styles and scripts + let resources = &self.krate.dioxus_config.web.resource; + let mut style_list = resources.style.clone().unwrap_or_default(); + let mut script_list = resources.script.clone().unwrap_or_default(); + + if self.is_dev_build() { + style_list.extend(resources.dev.style.iter().cloned()); + script_list.extend(resources.dev.script.iter().cloned()); + } + + let mut head_resources = String::new(); + + // Add all styles to the head + for style in &style_list { + writeln!( + &mut head_resources, + "", + &style.to_str().unwrap(), + )?; + } + + // Add all scripts to the head + for script in &script_list { + writeln!( + &mut head_resources, + "", + &script.to_str().unwrap(), + )?; + } + + if !style_list.is_empty() { + self.send_resource_deprecation_warning(style_list, ResourceType::Style); + } + if !script_list.is_empty() { + self.send_resource_deprecation_warning(script_list, ResourceType::Script); + } + + // Inject any resources from manganis into the head + // if let Some(assets) = assets { + // head_resources.push_str(&assets.head()); + // } + + replace_or_insert_before("{style_include}", " + // We can't use a module script here because we need to start the script immediately when streaming + import("/{base_path}/wasm/{app_name}.js").then( + ({ default: init }) => { + init("/{base_path}/wasm/{app_name}_bg.wasm").then((wasm) => { + if (wasm.__wbindgen_start == undefined) { + wasm.main(); + } + }); + } + ); + + {DX_TOAST_UTILITIES} + html.replace("{DX_TOAST_UTILITIES}", TOAST_HTML), + false => html.replace("{DX_TOAST_UTILITIES}", ""), + }; + + // And try to insert preload links for the wasm and js files + *html = html.replace( + " + + , variant: ResourceType) { + const RESOURCE_DEPRECATION_MESSAGE: &str = r#"The `web.resource` config has been deprecated in favor of head components and will be removed in a future release. Instead of including assets in the config, you can include assets with the `asset!` macro and add them to the head with `document::Link` and `Script` components."#; + + let replacement_components = paths + .iter() + .map(|path| { + let path = if path.exists() { + path.to_path_buf() + } else { + // If the path is absolute, make it relative to the current directory before we join it + // The path is actually a web path which is relative to the root of the website + let path = path.strip_prefix("/").unwrap_or(path); + let asset_dir_path = self.krate.legacy_asset_dir().join(path); + if let Ok(absolute_path) = asset_dir_path.canonicalize() { + let absolute_crate_root = self.krate.crate_dir().canonicalize().unwrap(); + PathBuf::from("./") + .join(absolute_path.strip_prefix(absolute_crate_root).unwrap()) + } else { + path.to_path_buf() + } + }; + match variant { + ResourceType::Style => { + format!(" Stylesheet {{ href: asset!(\"{}\") }}", path.display()) + } + ResourceType::Script => { + format!(" Script {{ src: asset!(\"{}\") }}", path.display()) + } + } + }) + .collect::>(); + let replacement_components = format!("rsx! {{\n{}\n}}", replacement_components.join("\n")); + let section_name = match variant { + ResourceType::Style => "web.resource.style", + ResourceType::Script => "web.resource.script", + }; + + let message = format!( + "{RESOURCE_DEPRECATION_MESSAGE}\nTo migrate to head components, remove `{section_name}` and include the following rsx in your root component:\n```rust\n{replacement_components}\n```" + ); + + _ = self.progress.unbounded_send(UpdateBuildProgress { + platform: self.platform(), + stage: Stage::OptimizingWasm, + update: UpdateStage::AddMessage(BuildMessage { + level: Level::WARN, + message: MessageType::Text(message), + source: MessageSource::Build, + }), + }); + } + /// Check if the build is targeting the web platform pub fn targeting_web(&self) -> bool { self.platform() == Platform::Web } } + +enum ResourceType { + Style, + Script, +} + +/// Replace a string or insert the new contents before a marker +fn replace_or_insert_before( + replace: &str, + or_insert_before: &str, + with: &str, + content: &mut String, +) { + if content.contains(replace) { + *content = content.replace(replace, with); + } else if let Some(pos) = content.find(or_insert_before) { + content.insert_str(pos, with); + } +} diff --git a/packages/cli/src/bundler/app.rs b/packages/cli/src/bundler/app.rs index 68718d94b4..2c9ea4e1e0 100644 --- a/packages/cli/src/bundler/app.rs +++ b/packages/cli/src/bundler/app.rs @@ -1,37 +1,9 @@ -use std::path::PathBuf; - -use crate::link::InterceptedArgs; +use crate::assets::AssetManifest; +use crate::builder::{BuildRequest, Platform}; use crate::Result; -use crate::{ - assets::{copy_dir_to, AssetManifest}, - link::LINK_OUTPUT_ENV_VAR, -}; -use anyhow::Context; -use core::str; -use futures_channel::mpsc::UnboundedSender; -use manganis_core::ResourceAsset; -use rayon::prelude::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator}; -use std::process::Stdio; -use std::{ - env::current_exe, - fs::{self, create_dir_all}, - io::Read, - sync::{atomic::AtomicUsize, Arc}, -}; -use std::{ - io::{BufWriter, Write}, - path::Path, -}; -use tokio::process::Command; -use tracing::Level; - -use super::*; -use crate::DioxusCrate; -use crate::{ - build::BuildArgs, - builder::{BuildRequest, Platform}, - config::BundleConfig, -}; +use rayon::prelude::{IntoParallelRefIterator, ParallelIterator}; +use std::path::PathBuf; +use std::sync::atomic::AtomicUsize; pub struct AppBundle { pub build: BuildRequest, @@ -46,16 +18,14 @@ impl AppBundle { assets: AssetManifest, executable: PathBuf, ) -> Result { - let workdir = build.krate.out_dir(); - std::fs::create_dir_all(&workdir)?; - let bundle = Self { - workdir, + workdir: build.krate.out_dir(), build, executable, assets, }; + bundle.prepare_workdir()?; bundle.write_main_executable().await?; bundle.write_assets().await?; bundle.write_metadata().await?; @@ -64,34 +34,43 @@ impl AppBundle { Ok(bundle) } - pub fn open(&self) {} - /// Take the workdir and copy it to the output location, returning the path to final bundle /// /// Perform any finishing steps here: /// - Signing the bundle pub async fn finish(&self, destination: PathBuf) -> Result { match self.build.platform() { - // Web is a simple fs copy of the workdir to the output location + // Nothing special to do - just copy the workdir to the output location Platform::Web => { let output_location = destination.join(self.build.app_name()); - // copy_dir_to(self.workdir.clone(), output_location.clone(), false)?; Ok(output_location) } + // Create a final .app/.exe/etc depending on the host platform, not dependent on the host + Platform::Desktop => { + // for now, until we have bundled hotreload, just copy the executable to the output location + Ok(self.executable.clone()) + // let output_location = destination.join(self.build.app_name()); + // Ok(output_location) + } + Platform::Server => todo!(), + Platform::Liveview => todo!(), + // Create a .ipa, only from macOS Platform::Ios => todo!(), // Create a .exe, from linux/mac/windows Platform::Android => todo!(), - - // Create a final .app/.exe/etc depending on the host platform, not dependent on the host - Platform::Desktop => todo!(), - Platform::Server => todo!(), - Platform::Liveview => todo!(), } } + // Create the workdir and then clean its contents, in case it already exists + fn prepare_workdir(&self) -> Result<()> { + _ = std::fs::remove_dir_all(&self.workdir); + _ = std::fs::create_dir_all(&self.workdir); + Ok(()) + } + /// Take the output of rustc and make it into the main exe of the bundle /// /// For wasm, we'll want to run `wasm-bindgen` to make it a wasm binary along with some other optimizations @@ -131,8 +110,12 @@ impl AppBundle { std::fs::write(self.workdir.join("index.html"), self.build.prepare_html()?)?; } + // Move the executable to the workdir + Platform::Desktop => { + std::fs::copy(self.executable.clone(), self.workdir.join("app"))?; + } + Platform::Ios => {} - Platform::Desktop => {} Platform::Server => {} Platform::Liveview => {} Platform::Android => todo!("android not yet supported!"), @@ -150,11 +133,10 @@ impl AppBundle { /// Should be the same on all platforms - just copy over the assets from the manifest into the output directory async fn write_assets(&self) -> Result<()> { let asset_dir = self.asset_dir(); - let assets = self.all_source_assets(); + let asset_count = assets.len(); let assets_finished = AtomicUsize::new(0); - let optimize = false; let pre_compress = false; @@ -240,22 +222,4 @@ impl AppBundle { Ok(()) } - - /// The folder where the bundles will be built - /// - /// ``` - /// dist/ - /// app-windows-11-arch-x64.exe - /// app-macos-11-arch-x64.app - /// app-macos-11-installer.dmg - /// app-linux-11-arch-x64.AppImage - /// server.sh - /// web - /// index.html - /// assets - /// logo.png - /// ``` - fn bundle_root(&self) -> PathBuf { - todo!() - } } diff --git a/packages/cli/src/bundler/web.rs b/packages/cli/src/bundler/web.rs index e69de29bb2..2f80e96645 100644 --- a/packages/cli/src/bundler/web.rs +++ b/packages/cli/src/bundler/web.rs @@ -0,0 +1,8 @@ +use super::AppBundle; +use crate::Result; + +impl AppBundle { + fn prepare_html(&self) -> Result { + todo!() + } +} diff --git a/packages/cli/src/dioxus_crate.rs b/packages/cli/src/dioxus_crate.rs index 6b7afae7d8..db01b102f4 100644 --- a/packages/cli/src/dioxus_crate.rs +++ b/packages/cli/src/dioxus_crate.rs @@ -66,7 +66,7 @@ impl DioxusCrate { .join(&self.dioxus_config.application.asset_dir) } - /// Get the list of files in the legacy asset directory + /// Get the list of files in the "legacy" asset directory pub fn legacy_asset_dir_files(&self) -> Vec { let mut files = vec![]; diff --git a/packages/cli/src/serve/mod.rs b/packages/cli/src/serve/mod.rs index aba8c245c5..5eaa56eaaf 100644 --- a/packages/cli/src/serve/mod.rs +++ b/packages/cli/src/serve/mod.rs @@ -148,7 +148,10 @@ pub async fn serve_all(args: ServeArgs, krate: DioxusCrate) -> Result<()> { ServeUpdate::BuildUpdate(BuildUpdate::BuildReady { target, result }) => { tracing::info!("Opening app for [{}]", target); - match runner.open(result, devserver.ip).await { + match runner + .open(result, devserver.ip, devserver.fullstack_address()) + .await + { Ok(handle) => { // Make sure we immediately capture the stdout/stderr of the executable - // otherwise it'll clobber our terminal output diff --git a/packages/cli/src/serve/output.rs b/packages/cli/src/serve/output.rs index 55274f4de4..f1af42b5d2 100644 --- a/packages/cli/src/serve/output.rs +++ b/packages/cli/src/serve/output.rs @@ -508,11 +508,11 @@ impl Output { pub fn render( &mut self, - _opts: &ServeArgs, - _krate: &DioxusCrate, - _builder: &Builder, + args: &ServeArgs, + krate: &DioxusCrate, + builder: &Builder, server: &DevServer, - _watcher: &Watcher, + watcher: &Watcher, ) { // just drain the build logs if !self.interactive { diff --git a/packages/cli/src/serve/runner.rs b/packages/cli/src/serve/runner.rs index 1fad12ffb0..0bee94e0cc 100644 --- a/packages/cli/src/serve/runner.rs +++ b/packages/cli/src/serve/runner.rs @@ -1,5 +1,6 @@ use super::ServeUpdate; use crate::{ + build, builder::{AppBundle, BuildUpdate, Platform}, cli::serve::ServeArgs, DioxusCrate, Result, @@ -97,11 +98,20 @@ impl AppRunner { } /// Finally "bundle" this app and return a handle to it - pub async fn open(&mut self, app: AppBundle, fullstack_addr: SocketAddr) -> Result<&AppHandle> { + pub async fn open( + &mut self, + app: AppBundle, + devserver_ip: SocketAddr, + fullstack_address: Option, + ) -> Result<&AppHandle> { let platform = app.build.platform(); + let ip = devserver_ip.to_string(); if platform == Platform::Server { - tracing::trace!("Proxying fullstack server from port {:?}", fullstack_addr); + tracing::trace!( + "Proxying fullstack server from port {:?}", + fullstack_address + ); } let work_dir = std::env::temp_dir(); @@ -112,7 +122,7 @@ impl AppRunner { // stdout_line: String::new(), // stderr_line: String::new(), - let handle = AppHandle { + let mut handle = AppHandle { app, executable, child: None, @@ -123,6 +133,44 @@ impl AppRunner { id: Uuid::new_v4(), }; + // open the exe with some arguments/envvars/etc + // we're going to try and configure this binary from the environment, if we can + // + // web can't be configured like this, so instead, we'll need to plumb a meta tag into the + // index.html during dev + match handle.app.build.platform() { + Platform::Web => {} + Platform::Desktop => { + let mut cmd = Command::new(handle.executable.clone()); + cmd.env( + dioxus_runtime_config::FULLSTACK_ADDRESS_ENV, + fullstack_address + .as_ref() + .map(|addr| addr.to_string()) + .unwrap_or_else(|| "127.0.0.1:8080".to_string()), + ) + .env( + dioxus_runtime_config::IOS_DEVSERVER_ADDR_ENV, + format!("ws://{}/_dioxus", ip), + ) + .env( + dioxus_runtime_config::DEVSERVER_RAW_ADDR_ENV, + format!("ws://{}/_dioxus", ip), + ) + .env("CARGO_MANIFEST_DIR", handle.app.build.krate.crate_dir()) + .stderr(Stdio::piped()) + .stdout(Stdio::piped()) + .kill_on_drop(true); + + let child = cmd.spawn()?; + handle.child = Some(child); + } + Platform::Ios => {} + Platform::Android => {} + Platform::Server => {} + Platform::Liveview => {} + } + if let Some(previous) = self.running.insert(platform, handle) { // close the old app, gracefully, hopefully } @@ -143,32 +191,6 @@ impl AppRunner { // Platform::Server | Platform::Liveview => Command::new(exe.display().to_string()), // }; - // open the exe with some arguments/envvars/etc - // we're going to try and configure this binary from the environment, if we can - // - // web can't be configured like this, so instead, we'll need to plumb a meta tag into the - // index.html during dev - // let _ = open - // .env( - // dioxus_runtime_config::FULLSTACK_ADDRESS_ENV, - // self.fullstack_address() - // .as_ref() - // .map(|addr| addr.to_string()) - // .unwrap_or_else(|| "127.0.0.1:8080".to_string()), - // ) - // .env( - // dioxus_runtime_config::IOS_DEVSERVER_ADDR_ENV, - // format!("ws://{}/_dioxus", ip), - // ) - // .env( - // dioxus_runtime_config::DEVSERVER_RAW_ADDR_ENV, - // format!("ws://{}/_dioxus", ip), - // ) - // .env("CARGO_MANIFEST_DIR", build.build.krate.crate_dir()) - // .stderr(Stdio::piped()) - // .stdout(Stdio::piped()) - // .kill_on_drop(true); - // todo!() } diff --git a/packages/interpreter/src/js/hash.txt b/packages/interpreter/src/js/hash.txt index 111177a44a..d8667e6824 100644 --- a/packages/interpreter/src/js/hash.txt +++ b/packages/interpreter/src/js/hash.txt @@ -1 +1 @@ -[6449103750905854967, 4461869229701639737, 13069001215487072322, 8716623267269178440, 5336385715226370016, 14456089431355876478, 6210436390350364769, 5052021921702764563, 17534315583914394253, 5638004933879392817] \ No newline at end of file +[6449103750905854967, 4461869229701639737, 13069001215487072322, 8716623267269178440, 5336385715226370016, 14456089431355876478, 13513584612037509338, 5052021921702764563, 17534315583914394253, 5638004933879392817] \ No newline at end of file diff --git a/packages/interpreter/src/js/native.js b/packages/interpreter/src/js/native.js index 30d1171dba..ff50e65fa8 100644 --- a/packages/interpreter/src/js/native.js +++ b/packages/interpreter/src/js/native.js @@ -1 +1 @@ -function retrieveValues(event,target){let contents={values:{}},form=target.closest("form");if(form){if(event.type==="input"||event.type==="change"||event.type==="submit"||event.type==="reset"||event.type==="click")contents=retrieveFormValues(form)}return contents}function retrieveFormValues(form){const formData=new FormData(form),contents={};return formData.forEach((value,key)=>{if(contents[key])contents[key].push(value);else contents[key]=[value]}),{valid:form.checkValidity(),values:contents}}function retrieveSelectValue(target){let options=target.selectedOptions,values=[];for(let i=0;icontents={...contents,...obj};if(event instanceof WheelEvent)extend(serializeWheelEvent(event));if(event instanceof MouseEvent)extend(serializeMouseEvent(event));if(event instanceof KeyboardEvent)extend(serializeKeyboardEvent(event));if(event instanceof InputEvent)extend(serializeInputEvent(event,target));if(event instanceof PointerEvent)extend(serializePointerEvent(event));if(event instanceof AnimationEvent)extend(serializeAnimationEvent(event));if(event instanceof TransitionEvent)extend({property_name:event.propertyName,elapsed_time:event.elapsedTime,pseudo_element:event.pseudoElement});if(event instanceof CompositionEvent)extend({data:event.data});if(event instanceof DragEvent)extend(serializeDragEvent(event));if(event instanceof FocusEvent)extend({});if(event instanceof ClipboardEvent)extend({});if(event instanceof CustomEvent){const detail=event.detail;if(detail instanceof ResizeObserverEntry)extend(serializeResizeEventDetail(detail))}if(typeof TouchEvent!=="undefined"&&event instanceof TouchEvent)extend(serializeTouchEvent(event));if(event.type==="submit"||event.type==="reset"||event.type==="click"||event.type==="change"||event.type==="input")extend(serializeInputEvent(event,target));if(event instanceof DragEvent);return contents}var toSerializableResizeObserverSize=function(size,is_inline_width){return[is_inline_width?size.inlineSize:size.blockSize,is_inline_width?size.blockSize:size.inlineSize]};function serializeResizeEventDetail(detail){let is_inline_width=!0;if(detail.target instanceof HTMLElement){if(window.getComputedStyle(detail.target).getPropertyValue("writing-mode")!=="horizontal-tb")is_inline_width=!1}return{border_box_size:detail.borderBoxSize!==void 0?toSerializableResizeObserverSize(detail.borderBoxSize[0],is_inline_width):detail.contentRect,content_box_size:detail.contentBoxSize!==void 0?toSerializableResizeObserverSize(detail.contentBoxSize[0],is_inline_width):detail.contentRect,content_rect:detail.contentRect}}var serializeInputEvent=function(event,target){let contents={};if(target instanceof HTMLElement){let values=retrieveValues(event,target);contents.values=values.values,contents.valid=values.valid}if(event.target instanceof HTMLInputElement){let target2=event.target,value=target2.value??target2.textContent??"";if(target2.type==="checkbox")value=target2.checked?"true":"false";else if(target2.type==="radio")value=target2.value;contents.value=value}if(event.target instanceof HTMLTextAreaElement)contents.value=event.target.value;if(event.target instanceof HTMLSelectElement)contents.value=retrieveSelectValue(event.target).join(",");if(contents.value===void 0)contents.value="";return contents},serializeWheelEvent=function(event){return{delta_x:event.deltaX,delta_y:event.deltaY,delta_z:event.deltaZ,delta_mode:event.deltaMode}},serializeTouchEvent=function(event){return{alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,shift_key:event.shiftKey,changed_touches:event.changedTouches,target_touches:event.targetTouches,touches:event.touches}},serializePointerEvent=function(event){return{alt_key:event.altKey,button:event.button,buttons:event.buttons,client_x:event.clientX,client_y:event.clientY,ctrl_key:event.ctrlKey,meta_key:event.metaKey,page_x:event.pageX,page_y:event.pageY,screen_x:event.screenX,screen_y:event.screenY,shift_key:event.shiftKey,pointer_id:event.pointerId,width:event.width,height:event.height,pressure:event.pressure,tangential_pressure:event.tangentialPressure,tilt_x:event.tiltX,tilt_y:event.tiltY,twist:event.twist,pointer_type:event.pointerType,is_primary:event.isPrimary}},serializeMouseEvent=function(event){return{alt_key:event.altKey,button:event.button,buttons:event.buttons,client_x:event.clientX,client_y:event.clientY,ctrl_key:event.ctrlKey,meta_key:event.metaKey,offset_x:event.offsetX,offset_y:event.offsetY,page_x:event.pageX,page_y:event.pageY,screen_x:event.screenX,screen_y:event.screenY,shift_key:event.shiftKey}},serializeKeyboardEvent=function(event){return{char_code:event.charCode,is_composing:event.isComposing,key:event.key,alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,key_code:event.keyCode,shift_key:event.shiftKey,location:event.location,repeat:event.repeat,which:event.which,code:event.code}},serializeAnimationEvent=function(event){return{animation_name:event.animationName,elapsed_time:event.elapsedTime,pseudo_element:event.pseudoElement}},serializeDragEvent=function(event){let files=void 0;if(event.dataTransfer&&event.dataTransfer.files&&event.dataTransfer.files.length>0)files={files:{placeholder:[]}};return{mouse:{alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,shift_key:event.shiftKey,...serializeMouseEvent(event)},files}};var handleVirtualdomEventSync=function(endpoint,contents){const xhr=new XMLHttpRequest;return xhr.open("POST",endpoint,!1),xhr.setRequestHeader("Content-Type","application/json"),xhr.send(contents),JSON.parse(xhr.responseText)},getTargetId=function(target){if(!(target instanceof Node))return null;let ourTarget=target,realId=null;while(realId==null){if(ourTarget===null)return null;if(ourTarget instanceof Element)realId=ourTarget.getAttribute("data-dioxus-id");ourTarget=ourTarget.parentNode}return parseInt(realId)},JSChannel_;if(RawInterpreter!==void 0&&RawInterpreter!==null)JSChannel_=RawInterpreter;class NativeInterpreter extends JSChannel_{intercept_link_redirects;ipc;editsPath;eventsPath;kickStylesheets;queuedBytes=[];liveview;constructor(editsPath,eventsPath){super();this.editsPath=editsPath,this.eventsPath=eventsPath,this.kickStylesheets=!1}initialize(root){this.intercept_link_redirects=!0,this.liveview=!1,window.addEventListener("dragover",function(e){if(e.target instanceof Element&&e.target.tagName!="INPUT")e.preventDefault()},!1),window.addEventListener("drop",function(e){if(!(e.target instanceof Element))return;e.preventDefault()},!1),window.addEventListener("click",(event)=>{const target=event.target;if(target instanceof HTMLInputElement&&target.getAttribute("type")==="file"){let target_id=getTargetId(target);if(target_id!==null){const message=this.serializeIpcMessage("file_dialog",{event:"change&input",accept:target.getAttribute("accept"),directory:target.getAttribute("webkitdirectory")==="true",multiple:target.hasAttribute("multiple"),target:target_id,bubbles:event.bubbles});this.ipc.postMessage(message),event.preventDefault()}}}),this.ipc=window.ipc;const handler=(event)=>this.handleEvent(event,event.type,!0);super.initialize(root,handler)}serializeIpcMessage(method,params={}){return JSON.stringify({method,params})}scrollTo(id,behavior){const node=this.nodes[id];if(node instanceof HTMLElement)node.scrollIntoView({behavior})}getScrollHeight(id){const node=this.nodes[id];if(node instanceof HTMLElement)return node.scrollHeight}getScrollLeft(id){const node=this.nodes[id];if(node instanceof HTMLElement)return node.scrollLeft}getScrollTop(id){const node=this.nodes[id];if(node instanceof HTMLElement)return node.scrollTop}getScrollWidth(id){const node=this.nodes[id];if(node instanceof HTMLElement)return node.scrollWidth}getClientRect(id){const node=this.nodes[id];if(node instanceof HTMLElement){const rect=node.getBoundingClientRect();return{type:"GetClientRect",origin:[rect.x,rect.y],size:[rect.width,rect.height]}}}setFocus(id,focus){const node=this.nodes[id];if(node instanceof HTMLElement)if(focus)node.focus();else node.blur()}loadChild(array){let node=this.stack[this.stack.length-1];for(let i=0;i0;end--)node=node.nextSibling}return node}appendChildren(id,many){const root=this.nodes[id],els=this.stack.splice(this.stack.length-many);for(let k=0;k{this.flushQueuedBytes(),this.waitForRequest(headless)})}waitForRequest(headless){fetch(new Request(this.editsPath)).then((response)=>response.arrayBuffer()).then((bytes)=>{this.rafEdits(headless,bytes)})}kickAllStylesheetsOnPage(){let stylesheets=document.querySelectorAll("link[rel=stylesheet]");for(let i=0;i{sheet.href=sheet.href+"?"+Math.random()})}}async readFiles(target,contents,bubbles,realId,name){let files=target.files,file_contents={};for(let i=0;i{if(contents[key])contents[key].push(value);else contents[key]=[value]}),{valid:form.checkValidity(),values:contents}}function retrieveSelectValue(target){let options=target.selectedOptions,values=[];for(let i=0;icontents={...contents,...obj};if(event instanceof WheelEvent)extend(serializeWheelEvent(event));if(event instanceof MouseEvent)extend(serializeMouseEvent(event));if(event instanceof KeyboardEvent)extend(serializeKeyboardEvent(event));if(event instanceof InputEvent)extend(serializeInputEvent(event,target));if(event instanceof PointerEvent)extend(serializePointerEvent(event));if(event instanceof AnimationEvent)extend(serializeAnimationEvent(event));if(event instanceof TransitionEvent)extend({property_name:event.propertyName,elapsed_time:event.elapsedTime,pseudo_element:event.pseudoElement});if(event instanceof CompositionEvent)extend({data:event.data});if(event instanceof DragEvent)extend(serializeDragEvent(event));if(event instanceof FocusEvent)extend({});if(event instanceof ClipboardEvent)extend({});if(event instanceof CustomEvent){const detail=event.detail;if(detail instanceof ResizeObserverEntry)extend(serializeResizeEventDetail(detail))}if(typeof TouchEvent!=="undefined"&&event instanceof TouchEvent)extend(serializeTouchEvent(event));if(event.type==="submit"||event.type==="reset"||event.type==="click"||event.type==="change"||event.type==="input")extend(serializeInputEvent(event,target));if(event instanceof DragEvent);return contents}var toSerializableResizeObserverSize=function(size,is_inline_width){return[is_inline_width?size.inlineSize:size.blockSize,is_inline_width?size.blockSize:size.inlineSize]};function serializeResizeEventDetail(detail){let is_inline_width=!0;if(detail.target instanceof HTMLElement){if(window.getComputedStyle(detail.target).getPropertyValue("writing-mode")!=="horizontal-tb")is_inline_width=!1}return{border_box_size:detail.borderBoxSize!==void 0?toSerializableResizeObserverSize(detail.borderBoxSize[0],is_inline_width):detail.contentRect,content_box_size:detail.contentBoxSize!==void 0?toSerializableResizeObserverSize(detail.contentBoxSize[0],is_inline_width):detail.contentRect,content_rect:detail.contentRect}}var serializeInputEvent=function(event,target){let contents={};if(target instanceof HTMLElement){let values=retrieveValues(event,target);contents.values=values.values,contents.valid=values.valid}if(event.target instanceof HTMLInputElement){let target2=event.target,value=target2.value??target2.textContent??"";if(target2.type==="checkbox")value=target2.checked?"true":"false";else if(target2.type==="radio")value=target2.value;contents.value=value}if(event.target instanceof HTMLTextAreaElement)contents.value=event.target.value;if(event.target instanceof HTMLSelectElement)contents.value=retrieveSelectValue(event.target).join(",");if(contents.value===void 0)contents.value="";return contents},serializeWheelEvent=function(event){return{delta_x:event.deltaX,delta_y:event.deltaY,delta_z:event.deltaZ,delta_mode:event.deltaMode}},serializeTouchEvent=function(event){return{alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,shift_key:event.shiftKey,changed_touches:event.changedTouches,target_touches:event.targetTouches,touches:event.touches}},serializePointerEvent=function(event){return{alt_key:event.altKey,button:event.button,buttons:event.buttons,client_x:event.clientX,client_y:event.clientY,ctrl_key:event.ctrlKey,meta_key:event.metaKey,page_x:event.pageX,page_y:event.pageY,screen_x:event.screenX,screen_y:event.screenY,shift_key:event.shiftKey,pointer_id:event.pointerId,width:event.width,height:event.height,pressure:event.pressure,tangential_pressure:event.tangentialPressure,tilt_x:event.tiltX,tilt_y:event.tiltY,twist:event.twist,pointer_type:event.pointerType,is_primary:event.isPrimary}},serializeMouseEvent=function(event){return{alt_key:event.altKey,button:event.button,buttons:event.buttons,client_x:event.clientX,client_y:event.clientY,ctrl_key:event.ctrlKey,meta_key:event.metaKey,offset_x:event.offsetX,offset_y:event.offsetY,page_x:event.pageX,page_y:event.pageY,screen_x:event.screenX,screen_y:event.screenY,shift_key:event.shiftKey}},serializeKeyboardEvent=function(event){return{char_code:event.charCode,is_composing:event.isComposing,key:event.key,alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,key_code:event.keyCode,shift_key:event.shiftKey,location:event.location,repeat:event.repeat,which:event.which,code:event.code}},serializeAnimationEvent=function(event){return{animation_name:event.animationName,elapsed_time:event.elapsedTime,pseudo_element:event.pseudoElement}},serializeDragEvent=function(event){let files=void 0;if(event.dataTransfer&&event.dataTransfer.files&&event.dataTransfer.files.length>0)files={files:{placeholder:[]}};return{mouse:{alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,shift_key:event.shiftKey,...serializeMouseEvent(event)},files}};var handleVirtualdomEventSync=function(endpoint,contents){const xhr=new XMLHttpRequest;return xhr.open("POST",endpoint,!1),xhr.setRequestHeader("Content-Type","application/json"),xhr.send(contents),JSON.parse(xhr.responseText)},getTargetId=function(target){if(!(target instanceof Node))return null;let ourTarget=target,realId=null;while(realId==null){if(ourTarget===null)return null;if(ourTarget instanceof Element)realId=ourTarget.getAttribute("data-dioxus-id");ourTarget=ourTarget.parentNode}return parseInt(realId)},JSChannel_;if(RawInterpreter!==void 0&&RawInterpreter!==null)JSChannel_=RawInterpreter;class NativeInterpreter extends JSChannel_{intercept_link_redirects;ipc;editsPath;eventsPath;kickStylesheets;queuedBytes=[];liveview;constructor(editsPath,eventsPath){super();this.editsPath=editsPath,this.eventsPath=eventsPath,this.kickStylesheets=!1}initialize(root){this.intercept_link_redirects=!0,this.liveview=!1,window.addEventListener("dragover",function(e){if(e.target instanceof Element&&e.target.tagName!="INPUT")e.preventDefault()},!1),window.addEventListener("drop",function(e){if(!(e.target instanceof Element))return;e.preventDefault()},!1),window.addEventListener("click",(event)=>{const target=event.target;if(target instanceof HTMLInputElement&&target.getAttribute("type")==="file"){let target_id=getTargetId(target);if(target_id!==null){const message=this.serializeIpcMessage("file_dialog",{event:"change&input",accept:target.getAttribute("accept"),directory:target.getAttribute("webkitdirectory")==="true",multiple:target.hasAttribute("multiple"),target:target_id,bubbles:event.bubbles});this.ipc.postMessage(message),event.preventDefault()}}}),this.ipc=window.ipc;const handler=(event)=>this.handleEvent(event,event.type,!0);super.initialize(root,handler)}serializeIpcMessage(method,params={}){return JSON.stringify({method,params})}scrollTo(id,behavior){const node=this.nodes[id];if(node instanceof HTMLElement)node.scrollIntoView({behavior})}getScrollHeight(id){const node=this.nodes[id];if(node instanceof HTMLElement)return node.scrollHeight}getScrollLeft(id){const node=this.nodes[id];if(node instanceof HTMLElement)return node.scrollLeft}getScrollTop(id){const node=this.nodes[id];if(node instanceof HTMLElement)return node.scrollTop}getScrollWidth(id){const node=this.nodes[id];if(node instanceof HTMLElement)return node.scrollWidth}getClientRect(id){const node=this.nodes[id];if(node instanceof HTMLElement){const rect=node.getBoundingClientRect();return{type:"GetClientRect",origin:[rect.x,rect.y],size:[rect.width,rect.height]}}}setFocus(id,focus){const node=this.nodes[id];if(node instanceof HTMLElement)if(focus)node.focus();else node.blur()}loadChild(array){let node=this.stack[this.stack.length-1];for(let i=0;i0;end--)node=node.nextSibling}return node}appendChildren(id,many){const root=this.nodes[id],els=this.stack.splice(this.stack.length-many);for(let k=0;k{this.flushQueuedBytes(),this.waitForRequest(headless)})}waitForRequest(headless){fetch(new Request(this.editsPath)).then((response)=>response.arrayBuffer()).then((bytes)=>{this.rafEdits(headless,bytes)})}kickAllStylesheetsOnPage(){let stylesheets=document.querySelectorAll("link[rel=stylesheet]");for(let i=0;i{let entropy_string=Math.random().toString().substring(0,10),[href]=sheet.href.split("?entropy=");sheet.href=href+"?entropy="+entropy_string})}this.kickCachedLinks()}kickCachedLinks(){Object.values(this.templates).forEach((template)=>{template.forEach((node)=>{let walker=document.createTreeWalker(node,NodeFilter.SHOW_ELEMENT);while(walker.nextNode()){let element=walker.currentNode;if(element.tagName==="LINK"){let attr=element.getAttribute("href");if(attr){let entropy_string=Math.random().toString().substring(0,10),[href]=attr.split("?entropy=");element.setAttribute("href",href+"?entropy="+entropy_string)}}}})})}async readFiles(target,contents,bubbles,realId,name){let files=target.files,file_contents={};for(let i=0;i { - sheet.href = sheet.href + "?" + Math.random(); + // Change the href invalidate parameter to a random number + let entropy = Math.random(); + + // Cap the entropy to_string length to 10 characters + let entropy_string = entropy.toString().substring(0, 10); + + // Split the href into the original and the query string + let [href] = sheet.href.split("?entropy="); + sheet.href = href + "?entropy=" + entropy_string; }); } + + // Modify the templates' links to include a random entropy parameter + this.kickCachedLinks(); + } + + // modify all the links in our saved templates to include a random entropy parameter + // ensures that templates, when rendered again, have some entropy + kickCachedLinks() { + Object.values(this.templates).forEach((template) => { + template.forEach((node) => { + let walker = document.createTreeWalker(node, NodeFilter.SHOW_ELEMENT); + while (walker.nextNode()) { + let element = walker.currentNode as HTMLElement; + if (element.tagName === "LINK") { + // if it has a href attribute, change it + let attr = element.getAttribute("href"); + if (attr) { + // Change the href invalidate parameter to a random number + let entropy = Math.random(); + + // Cap the entropy to_string length to 10 characters + let entropy_string = entropy.toString().substring(0, 10); + + // Split the href into the original and the query string + let [href] = attr.split("?entropy="); + element.setAttribute("href", href + "?entropy=" + entropy_string); + } + } + } + }); + }); } // A liveview only function From c3bf9eedd2d3ee5aed298ababe5740ee52fed2a9 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 4 Sep 2024 23:59:34 -0700 Subject: [PATCH 065/139] clean up a bit more, split up eventloop --- packages/cli/src/builder/bundle.rs | 1 - packages/cli/src/builder/mod.rs | 5 - packages/cli/src/builder/request.rs | 2 +- packages/cli/src/bundler/web.rs | 2 +- packages/cli/src/serve/mod.rs | 304 ++++++++++++++------------ packages/cli/src/serve/runner.rs | 48 ++-- packages/cli/src/serve/server.rs | 8 +- packages/cli/src/serve/update.rs | 4 - packages/cli/src/serve/util.rs | 25 --- packages/cli/src/serve/watcher.rs | 1 + packages/interpreter/src/js/hash.txt | 2 +- packages/interpreter/src/js/native.js | 2 +- packages/interpreter/src/ts/native.ts | 2 + packages/runtime-config/src/lib.rs | 2 +- 14 files changed, 188 insertions(+), 220 deletions(-) delete mode 100644 packages/cli/src/builder/bundle.rs delete mode 100644 packages/cli/src/serve/util.rs diff --git a/packages/cli/src/builder/bundle.rs b/packages/cli/src/builder/bundle.rs deleted file mode 100644 index 8b13789179..0000000000 --- a/packages/cli/src/builder/bundle.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/packages/cli/src/builder/mod.rs b/packages/cli/src/builder/mod.rs index 0357f7634a..542a7b11f2 100644 --- a/packages/cli/src/builder/mod.rs +++ b/packages/cli/src/builder/mod.rs @@ -5,7 +5,6 @@ /// Uses a request -> response architecture that allows you to monitor the progress with an optional message /// receiver. mod builder; -mod bundle; mod cargo; mod handle; mod platform; @@ -16,10 +15,6 @@ mod result; mod tooling; mod web; -use crate::build::BuildArgs; -use crate::Result; -use crate::{assets::AssetManifest, dioxus_crate::DioxusCrate}; - pub use builder::*; pub use platform::*; pub use progress::*; diff --git a/packages/cli/src/builder/request.rs b/packages/cli/src/builder/request.rs index de7538863c..cca13d53be 100644 --- a/packages/cli/src/builder/request.rs +++ b/packages/cli/src/builder/request.rs @@ -1,8 +1,8 @@ use super::profiles::*; use super::progress::ProgressTx; +use crate::build::BuildArgs; use crate::builder::Platform; use crate::dioxus_crate::DioxusCrate; -use crate::{build::BuildArgs, bundler::BundleFormat}; use std::path::PathBuf; /// An app that's built, bundled, processed, and a handle to its running app, if it exists diff --git a/packages/cli/src/bundler/web.rs b/packages/cli/src/bundler/web.rs index 2f80e96645..09059ead95 100644 --- a/packages/cli/src/bundler/web.rs +++ b/packages/cli/src/bundler/web.rs @@ -2,7 +2,7 @@ use super::AppBundle; use crate::Result; impl AppBundle { - fn prepare_html(&self) -> Result { + pub fn prepare_html(&self) -> Result { todo!() } } diff --git a/packages/cli/src/serve/mod.rs b/packages/cli/src/serve/mod.rs index 5eaa56eaaf..e0456ae65c 100644 --- a/packages/cli/src/serve/mod.rs +++ b/packages/cli/src/serve/mod.rs @@ -2,6 +2,7 @@ use crate::builder::{BuildUpdate, Builder, Platform, Stage, UpdateBuildProgress, use crate::cli::serve::ServeArgs; use crate::DioxusCrate; use crate::Result; +use std::ops::ControlFlow; mod detect; mod hot_reloading_file_map; @@ -12,7 +13,6 @@ mod runner; mod server; mod tracer; mod update; -mod util; mod watcher; use output::*; @@ -20,7 +20,6 @@ use runner::*; use server::*; pub use tracer::*; use update::*; -use util::*; use watcher::*; /// For *all* builds, the CLI spins up a dedicated webserver, file watcher, and build infrastructure to serve the project. @@ -71,166 +70,189 @@ pub async fn serve_all(args: ServeArgs, krate: DioxusCrate) -> Result<()> { msg = tracer.wait() => msg, }; - match msg { - ServeUpdate::FilesChanged { files } => { - if files.is_empty() || !args.should_hotreload() { - continue; - } - - // if change is hotreloadable, hotreload it - // and then send that update to all connected clients - if let Some(hr) = watcher.attempt_hot_reload(files, &runner) { - // Only send a hotreload message for templates and assets - otherwise we'll just get a full rebuild - if hr.templates.is_empty() && hr.assets.is_empty() { - continue; - } + match handle_msg( + msg, + &args, + &mut devserver, + &mut screen, + &mut builder, + &mut runner, + &mut watcher, + ) + .await + { + Ok(ControlFlow::Break(())) => break, + Ok(ControlFlow::Continue(())) => {} + Err(e) => { + tracing::error!("Error handling message: {}", e); + break; + } + } + } - devserver.send_hotreload(hr).await; - } else { - // We're going to kick off a new build, interrupting the current build if it's ongoing - builder.build(args.build_arguments.clone())?; + // Kill the clients first + _ = devserver.shutdown().await; + _ = screen.shutdown(); + _ = builder.abort_all(); + _ = tracer.shutdown(); - // Clear the hot reload changes - watcher.clear_hot_reload_changes(); + Ok(()) +} - // Tell the server to show a loading page for any new requests - devserver.start_build().await; - } +async fn handle_msg( + msg: ServeUpdate, + args: &ServeArgs, + devserver: &mut DevServer, + screen: &mut Output, + builder: &mut Builder, + runner: &mut AppRunner, + watcher: &mut Watcher, +) -> Result> { + match msg { + ServeUpdate::FilesChanged { files } => { + if files.is_empty() || !args.should_hotreload() { + return Ok(ControlFlow::Continue(())); } - // Run the server in the background - // Waiting for updates here lets us tap into when clients are added/removed - ServeUpdate::NewConnection => { - if let Some(msg) = watcher.applied_hot_reload_changes() { - devserver.send_hotreload(msg).await; + // if change is hotreloadable, hotreload it + // and then send that update to all connected clients + if let Some(hr) = watcher.attempt_hot_reload(files, &runner) { + // Only send a hotreload message for templates and assets - otherwise we'll just get a full rebuild + if hr.templates.is_empty() && hr.assets.is_empty() { + return Ok(ControlFlow::Continue(())); } - } - // Received a message from the devtools server - currently we only use this for - // logging, so we just forward it the tui - ServeUpdate::WsMessage(msg) => { - screen.new_ws_message(Platform::Web, msg); - } + devserver.send_hotreload(hr).await; + } else { + // We're going to kick off a new build, interrupting the current build if it's ongoing + builder.build(args.build_arguments.clone())?; - // Wait for logs from the build engine - // These will cause us to update the screen - // We also can check the status of the builds here in case we have multiple ongoing builds - ServeUpdate::BuildUpdate(BuildUpdate::Progress(update)) => { - let update_clone = update.clone(); - screen.new_build_logs(update.platform, update_clone); - devserver - .update_build_status(screen.build_progress.progress(), update.stage.to_string()) - .await; - - match update { - // Send rebuild start message. - UpdateBuildProgress { - stage: Stage::Compiling, - update: UpdateStage::Start, - platform: _, - } => devserver.send_reload_start().await, - - // Send rebuild failed message. - UpdateBuildProgress { - stage: Stage::Finished, - update: UpdateStage::Failed(_), - platform: _, - } => devserver.send_reload_failed().await, - - _ => {} - } - } + // Clear the hot reload changes + watcher.clear_hot_reload_changes(); - ServeUpdate::BuildUpdate(BuildUpdate::BuildFailed { err, .. }) => { - devserver.send_build_error(err).await; + // Tell the server to show a loading page for any new requests + devserver.start_build().await; } + } - ServeUpdate::BuildUpdate(BuildUpdate::BuildReady { target, result }) => { - tracing::info!("Opening app for [{}]", target); - - match runner - .open(result, devserver.ip, devserver.fullstack_address()) - .await - { - Ok(handle) => { - // Make sure we immediately capture the stdout/stderr of the executable - - // otherwise it'll clobber our terminal output - screen.new_ready_app(handle); - - // And then finally tell the server to reload - devserver.send_reload_command().await; - } - Err(e) => { - tracing::error!("Failed to open app: {}", e); - } - } + // Run the server in the background + // Waiting for updates here lets us tap into when clients are added/removed + ServeUpdate::NewConnection => { + if let Some(msg) = watcher.applied_hot_reload_changes() { + devserver.send_hotreload(msg).await; } + } + + // Received a message from the devtools server - currently we only use this for + // logging, so we just forward it the tui + ServeUpdate::WsMessage(msg) => { + screen.new_ws_message(Platform::Web, msg); + } + + // Wait for logs from the build engine + // These will cause us to update the screen + // We also can check the status of the builds here in case we have multiple ongoing builds + ServeUpdate::BuildUpdate(BuildUpdate::Progress(update)) => { + let update_clone = update.clone(); + screen.new_build_logs(update.platform, update_clone); + devserver + .update_build_status(screen.build_progress.progress(), update.stage.to_string()) + .await; - ServeUpdate::StdoutReceived { target, msg } => {} - - ServeUpdate::StderrReceived { target, msg } => {} - - // nothing - the builder just signals that there are no more pending builds - // maybe ping the logger to wipe any logs and/or clear the screen - ServeUpdate::BuildUpdate(BuildUpdate::Finished) => {} - - // If the process exited *cleanly*, we can exit - ServeUpdate::ProcessExited { - status, - target_platform, - } => { - // // Then remove the child process - // builder - // .children - // .retain(|(platform, _)| *platform != target_platform); - // match status { - // Ok(status) => { - // if status.success() { - // break; - // } else { - // tracing::error!("Application exited with status: {status}"); - // } - // } - // Err(e) => { - // tracing::error!("Application exited with error: {e}"); - // } - // } + match update { + // Send rebuild start message. + UpdateBuildProgress { + stage: Stage::Compiling, + update: UpdateStage::Start, + platform: _, + } => devserver.send_reload_start().await, + + // Send rebuild failed message. + UpdateBuildProgress { + stage: Stage::Finished, + update: UpdateStage::Failed(_), + platform: _, + } => devserver.send_reload_failed().await, + + _ => {} } + } + + ServeUpdate::BuildUpdate(BuildUpdate::BuildFailed { err, .. }) => { + devserver.send_build_error(err).await; + } + + ServeUpdate::BuildUpdate(BuildUpdate::BuildReady { target, result }) => { + tracing::info!("Opening app for [{}]", target); - // Handle TUI input and maybe even rebuild the app - ServeUpdate::TuiInput { event } => { - let should_rebuild = screen.handle_input(event); - match should_rebuild { - Ok(true) => { - builder.build(args.build_arguments.clone())?; - devserver.start_build().await - } - Ok(false) => {} - Err(_) => break, + match runner + .open(result, devserver.ip, devserver.fullstack_address()) + .await + { + Ok(handle) => { + // Make sure we immediately capture the stdout/stderr of the executable - + // otherwise it'll clobber our terminal output + screen.new_ready_app(handle); + + // And then finally tell the server to reload + devserver.send_reload_command().await; + } + Err(e) => { + tracing::error!("Failed to open app: {}", e); } } + } + + ServeUpdate::StdoutReceived { target, msg } => {} + + ServeUpdate::StderrReceived { target, msg } => {} - ServeUpdate::TracingLog { log } => { - screen.push_log( - LogSource::Internal, - crate::builder::BuildMessage { - level: tracing::Level::INFO, - message: crate::builder::MessageType::Text(log), - source: crate::builder::MessageSource::Dev, - }, - ); + // nothing - the builder just signals that there are no more pending builds + // maybe ping the logger to wipe any logs and/or clear the screen + ServeUpdate::BuildUpdate(BuildUpdate::Finished) => {} + + // If the process exited *cleanly*, we can exit + ServeUpdate::ProcessExited { + status, + target_platform, + } => { + // // Then remove the child process + // builder + // .children + // .retain(|(platform, _)| *platform != target_platform); + // match status { + // Ok(status) => { + // if status.success() { + // break; + // } else { + // tracing::error!("Application exited with status: {status}"); + // } + // } + // Err(e) => { + // tracing::error!("Application exited with error: {e}"); + // } + // } + } + + ServeUpdate::TuiInput { event } => { + let should_rebuild = screen.handle_input(event)?; + if should_rebuild { + builder.build(args.build_arguments.clone())?; + devserver.start_build().await } + } - // A fatal error occured and we need to exit + cleanup - ServeUpdate::Fatal { err } => break, + ServeUpdate::TracingLog { log } => { + screen.push_log( + LogSource::Internal, + crate::builder::BuildMessage { + level: tracing::Level::INFO, + message: crate::builder::MessageType::Text(log), + source: crate::builder::MessageSource::Dev, + }, + ); } } - // Kill the clients first - _ = devserver.shutdown().await; - _ = screen.shutdown(); - _ = builder.abort_all(); - _ = tracer.shutdown(); - - Ok(()) + Ok(ControlFlow::Continue(())) } diff --git a/packages/cli/src/serve/runner.rs b/packages/cli/src/serve/runner.rs index 0bee94e0cc..4133b39840 100644 --- a/packages/cli/src/serve/runner.rs +++ b/packages/cli/src/serve/runner.rs @@ -25,12 +25,12 @@ pub struct AppRunner { /// A handle to a running app pub struct AppHandle { + pub id: Uuid, pub app: AppBundle, pub executable: PathBuf, - pub id: Uuid, pub child: Option, - // pub stdout: Lines>, - // pub stderr: Lines>, + pub stdout: Option>, + pub stderr: Option>, // pub stdout_line: String, // pub stderr_line: String, } @@ -117,20 +117,13 @@ impl AppRunner { let work_dir = std::env::temp_dir(); let executable = app.finish(work_dir).await?; - // stdout: BufReader::new(stdout).lines(), - // stderr: BufReader::new(stderr).lines(), - // stdout_line: String::new(), - // stderr_line: String::new(), - let mut handle = AppHandle { app, executable, child: None, - // stdout: BufReader::new(stdout).lines(), - // stderr: BufReader::new(stderr).lines(), - // stdout_line: String::new(), - // stderr_line: String::new(), id: Uuid::new_v4(), + stderr: None, + stdout: None, }; // open the exe with some arguments/envvars/etc @@ -158,11 +151,19 @@ impl AppRunner { format!("ws://{}/_dioxus", ip), ) .env("CARGO_MANIFEST_DIR", handle.app.build.krate.crate_dir()) + .env( + "SIMCTL_CHILD_CARGO_MANIFEST_DIR", + handle.app.build.krate.crate_dir(), + ) .stderr(Stdio::piped()) .stdout(Stdio::piped()) .kill_on_drop(true); - let child = cmd.spawn()?; + let mut child = cmd.spawn()?; + let stdout = BufReader::new(child.stdout.take().unwrap()); + let stderr = BufReader::new(child.stderr.take().unwrap()); + handle.stdout = Some(stdout); + handle.stderr = Some(stderr); handle.child = Some(child); } Platform::Ios => {} @@ -176,28 +177,9 @@ impl AppRunner { } Ok(self.running.get(&platform).unwrap()) - - // // First, we need to "install" the app - // let exe = build.finish(work_dir).await?; - // let mut open = match build.build.platform() { - // // Run `dx http-server` to serve the app - // Platform::Web => todo!(), - - // // Open up the .ipa for the .app - // Platform::Ios => todo!(), - - // Platform::Desktop => Command::new("open"), - // Platform::Android => todo!("Android not supported yet"), - // Platform::Server | Platform::Liveview => Command::new(exe.display().to_string()), - // }; - - // todo!() - } - - fn install_app(&self, build: &AppBundle) -> Result<()> { - todo!() } + #[allow(unused)] fn open_bundled_ios_app(&self, build: &AppBundle) -> std::io::Result> { // command = "xcrun" // args = [ diff --git a/packages/cli/src/serve/server.rs b/packages/cli/src/serve/server.rs index 0e8b940e06..a5cd02adcf 100644 --- a/packages/cli/src/serve/server.rs +++ b/packages/cli/src/serve/server.rs @@ -1,7 +1,4 @@ -use crate::{ - builder::Platform, - serve::{next_or_pending, ServeArgs}, -}; +use crate::{builder::Platform, serve::ServeArgs}; use crate::{ builder::{AppBundle, BuildRequest}, dioxus_crate::DioxusCrate, @@ -255,7 +252,6 @@ impl DevServer { .enumerate() .map(|(idx, socket)| async move { (idx, socket.next().await) }) .collect::>(); - let next_new_message = next_or_pending(new_message.next()); tokio::select! { new_hot_reload_socket = &mut new_hot_reload_socket => { @@ -282,7 +278,7 @@ impl DevServer { panic!("Could not receive a socket - the devtools could not boot - the port is likely already in use"); } } - (idx, message) = next_new_message => { + Some((idx, message)) = new_message.next() => { match message { Some(Ok(message)) => return ServeUpdate::WsMessage(message), _ => { diff --git a/packages/cli/src/serve/update.rs b/packages/cli/src/serve/update.rs index 6320bd847f..9ddac7bfb6 100644 --- a/packages/cli/src/serve/update.rs +++ b/packages/cli/src/serve/update.rs @@ -49,8 +49,4 @@ pub enum ServeUpdate { // source: LogSource, log: String, }, - - Fatal { - err: Box, - }, } diff --git a/packages/cli/src/serve/util.rs b/packages/cli/src/serve/util.rs deleted file mode 100644 index 103abe6640..0000000000 --- a/packages/cli/src/serve/util.rs +++ /dev/null @@ -1,25 +0,0 @@ -use crate::builder::{Platform, Stage, UpdateBuildProgress, UpdateStage}; -use crate::cli::serve::ServeArgs; -use crate::dioxus_crate::DioxusCrate; -use crate::Result; -use futures_util::FutureExt; -use std::future::{poll_fn, Future, IntoFuture}; -use std::task::Poll; -use tokio::task::yield_now; - -// Grab the output of a future that returns an option or wait forever -pub fn next_or_pending(f: F) -> impl Future -where - F: IntoFuture>, -{ - let pinned = f.into_future().fuse(); - let mut pinned = Box::pin(pinned); - poll_fn(move |cx| { - let next = pinned.as_mut().poll(cx); - match next { - Poll::Ready(Some(next)) => Poll::Ready(next), - _ => Poll::Pending, - } - }) - .fuse() -} diff --git a/packages/cli/src/serve/watcher.rs b/packages/cli/src/serve/watcher.rs index fd604fc872..fc2035eda0 100644 --- a/packages/cli/src/serve/watcher.rs +++ b/packages/cli/src/serve/watcher.rs @@ -41,6 +41,7 @@ impl Watcher { allow_watch_path.push(krate.dioxus_config.application.asset_dir.clone()); allow_watch_path.push("Cargo.toml".to_string().into()); allow_watch_path.push("Dioxus.toml".to_string().into()); + allow_watch_path.push("assets".to_string().into()); allow_watch_path.dedup(); let crate_dir = krate.crate_dir(); diff --git a/packages/interpreter/src/js/hash.txt b/packages/interpreter/src/js/hash.txt index d8667e6824..df1e3db408 100644 --- a/packages/interpreter/src/js/hash.txt +++ b/packages/interpreter/src/js/hash.txt @@ -1 +1 @@ -[6449103750905854967, 4461869229701639737, 13069001215487072322, 8716623267269178440, 5336385715226370016, 14456089431355876478, 13513584612037509338, 5052021921702764563, 17534315583914394253, 5638004933879392817] \ No newline at end of file +[6449103750905854967, 4461869229701639737, 13069001215487072322, 8716623267269178440, 5336385715226370016, 14456089431355876478, 7300879533173534275, 5052021921702764563, 17534315583914394253, 5638004933879392817] \ No newline at end of file diff --git a/packages/interpreter/src/js/native.js b/packages/interpreter/src/js/native.js index ff50e65fa8..31530d8f10 100644 --- a/packages/interpreter/src/js/native.js +++ b/packages/interpreter/src/js/native.js @@ -1 +1 @@ -function retrieveValues(event,target){let contents={values:{}},form=target.closest("form");if(form){if(event.type==="input"||event.type==="change"||event.type==="submit"||event.type==="reset"||event.type==="click")contents=retrieveFormValues(form)}return contents}function retrieveFormValues(form){const formData=new FormData(form),contents={};return formData.forEach((value,key)=>{if(contents[key])contents[key].push(value);else contents[key]=[value]}),{valid:form.checkValidity(),values:contents}}function retrieveSelectValue(target){let options=target.selectedOptions,values=[];for(let i=0;icontents={...contents,...obj};if(event instanceof WheelEvent)extend(serializeWheelEvent(event));if(event instanceof MouseEvent)extend(serializeMouseEvent(event));if(event instanceof KeyboardEvent)extend(serializeKeyboardEvent(event));if(event instanceof InputEvent)extend(serializeInputEvent(event,target));if(event instanceof PointerEvent)extend(serializePointerEvent(event));if(event instanceof AnimationEvent)extend(serializeAnimationEvent(event));if(event instanceof TransitionEvent)extend({property_name:event.propertyName,elapsed_time:event.elapsedTime,pseudo_element:event.pseudoElement});if(event instanceof CompositionEvent)extend({data:event.data});if(event instanceof DragEvent)extend(serializeDragEvent(event));if(event instanceof FocusEvent)extend({});if(event instanceof ClipboardEvent)extend({});if(event instanceof CustomEvent){const detail=event.detail;if(detail instanceof ResizeObserverEntry)extend(serializeResizeEventDetail(detail))}if(typeof TouchEvent!=="undefined"&&event instanceof TouchEvent)extend(serializeTouchEvent(event));if(event.type==="submit"||event.type==="reset"||event.type==="click"||event.type==="change"||event.type==="input")extend(serializeInputEvent(event,target));if(event instanceof DragEvent);return contents}var toSerializableResizeObserverSize=function(size,is_inline_width){return[is_inline_width?size.inlineSize:size.blockSize,is_inline_width?size.blockSize:size.inlineSize]};function serializeResizeEventDetail(detail){let is_inline_width=!0;if(detail.target instanceof HTMLElement){if(window.getComputedStyle(detail.target).getPropertyValue("writing-mode")!=="horizontal-tb")is_inline_width=!1}return{border_box_size:detail.borderBoxSize!==void 0?toSerializableResizeObserverSize(detail.borderBoxSize[0],is_inline_width):detail.contentRect,content_box_size:detail.contentBoxSize!==void 0?toSerializableResizeObserverSize(detail.contentBoxSize[0],is_inline_width):detail.contentRect,content_rect:detail.contentRect}}var serializeInputEvent=function(event,target){let contents={};if(target instanceof HTMLElement){let values=retrieveValues(event,target);contents.values=values.values,contents.valid=values.valid}if(event.target instanceof HTMLInputElement){let target2=event.target,value=target2.value??target2.textContent??"";if(target2.type==="checkbox")value=target2.checked?"true":"false";else if(target2.type==="radio")value=target2.value;contents.value=value}if(event.target instanceof HTMLTextAreaElement)contents.value=event.target.value;if(event.target instanceof HTMLSelectElement)contents.value=retrieveSelectValue(event.target).join(",");if(contents.value===void 0)contents.value="";return contents},serializeWheelEvent=function(event){return{delta_x:event.deltaX,delta_y:event.deltaY,delta_z:event.deltaZ,delta_mode:event.deltaMode}},serializeTouchEvent=function(event){return{alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,shift_key:event.shiftKey,changed_touches:event.changedTouches,target_touches:event.targetTouches,touches:event.touches}},serializePointerEvent=function(event){return{alt_key:event.altKey,button:event.button,buttons:event.buttons,client_x:event.clientX,client_y:event.clientY,ctrl_key:event.ctrlKey,meta_key:event.metaKey,page_x:event.pageX,page_y:event.pageY,screen_x:event.screenX,screen_y:event.screenY,shift_key:event.shiftKey,pointer_id:event.pointerId,width:event.width,height:event.height,pressure:event.pressure,tangential_pressure:event.tangentialPressure,tilt_x:event.tiltX,tilt_y:event.tiltY,twist:event.twist,pointer_type:event.pointerType,is_primary:event.isPrimary}},serializeMouseEvent=function(event){return{alt_key:event.altKey,button:event.button,buttons:event.buttons,client_x:event.clientX,client_y:event.clientY,ctrl_key:event.ctrlKey,meta_key:event.metaKey,offset_x:event.offsetX,offset_y:event.offsetY,page_x:event.pageX,page_y:event.pageY,screen_x:event.screenX,screen_y:event.screenY,shift_key:event.shiftKey}},serializeKeyboardEvent=function(event){return{char_code:event.charCode,is_composing:event.isComposing,key:event.key,alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,key_code:event.keyCode,shift_key:event.shiftKey,location:event.location,repeat:event.repeat,which:event.which,code:event.code}},serializeAnimationEvent=function(event){return{animation_name:event.animationName,elapsed_time:event.elapsedTime,pseudo_element:event.pseudoElement}},serializeDragEvent=function(event){let files=void 0;if(event.dataTransfer&&event.dataTransfer.files&&event.dataTransfer.files.length>0)files={files:{placeholder:[]}};return{mouse:{alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,shift_key:event.shiftKey,...serializeMouseEvent(event)},files}};var handleVirtualdomEventSync=function(endpoint,contents){const xhr=new XMLHttpRequest;return xhr.open("POST",endpoint,!1),xhr.setRequestHeader("Content-Type","application/json"),xhr.send(contents),JSON.parse(xhr.responseText)},getTargetId=function(target){if(!(target instanceof Node))return null;let ourTarget=target,realId=null;while(realId==null){if(ourTarget===null)return null;if(ourTarget instanceof Element)realId=ourTarget.getAttribute("data-dioxus-id");ourTarget=ourTarget.parentNode}return parseInt(realId)},JSChannel_;if(RawInterpreter!==void 0&&RawInterpreter!==null)JSChannel_=RawInterpreter;class NativeInterpreter extends JSChannel_{intercept_link_redirects;ipc;editsPath;eventsPath;kickStylesheets;queuedBytes=[];liveview;constructor(editsPath,eventsPath){super();this.editsPath=editsPath,this.eventsPath=eventsPath,this.kickStylesheets=!1}initialize(root){this.intercept_link_redirects=!0,this.liveview=!1,window.addEventListener("dragover",function(e){if(e.target instanceof Element&&e.target.tagName!="INPUT")e.preventDefault()},!1),window.addEventListener("drop",function(e){if(!(e.target instanceof Element))return;e.preventDefault()},!1),window.addEventListener("click",(event)=>{const target=event.target;if(target instanceof HTMLInputElement&&target.getAttribute("type")==="file"){let target_id=getTargetId(target);if(target_id!==null){const message=this.serializeIpcMessage("file_dialog",{event:"change&input",accept:target.getAttribute("accept"),directory:target.getAttribute("webkitdirectory")==="true",multiple:target.hasAttribute("multiple"),target:target_id,bubbles:event.bubbles});this.ipc.postMessage(message),event.preventDefault()}}}),this.ipc=window.ipc;const handler=(event)=>this.handleEvent(event,event.type,!0);super.initialize(root,handler)}serializeIpcMessage(method,params={}){return JSON.stringify({method,params})}scrollTo(id,behavior){const node=this.nodes[id];if(node instanceof HTMLElement)node.scrollIntoView({behavior})}getScrollHeight(id){const node=this.nodes[id];if(node instanceof HTMLElement)return node.scrollHeight}getScrollLeft(id){const node=this.nodes[id];if(node instanceof HTMLElement)return node.scrollLeft}getScrollTop(id){const node=this.nodes[id];if(node instanceof HTMLElement)return node.scrollTop}getScrollWidth(id){const node=this.nodes[id];if(node instanceof HTMLElement)return node.scrollWidth}getClientRect(id){const node=this.nodes[id];if(node instanceof HTMLElement){const rect=node.getBoundingClientRect();return{type:"GetClientRect",origin:[rect.x,rect.y],size:[rect.width,rect.height]}}}setFocus(id,focus){const node=this.nodes[id];if(node instanceof HTMLElement)if(focus)node.focus();else node.blur()}loadChild(array){let node=this.stack[this.stack.length-1];for(let i=0;i0;end--)node=node.nextSibling}return node}appendChildren(id,many){const root=this.nodes[id],els=this.stack.splice(this.stack.length-many);for(let k=0;k{this.flushQueuedBytes(),this.waitForRequest(headless)})}waitForRequest(headless){fetch(new Request(this.editsPath)).then((response)=>response.arrayBuffer()).then((bytes)=>{this.rafEdits(headless,bytes)})}kickAllStylesheetsOnPage(){let stylesheets=document.querySelectorAll("link[rel=stylesheet]");for(let i=0;i{let entropy_string=Math.random().toString().substring(0,10),[href]=sheet.href.split("?entropy=");sheet.href=href+"?entropy="+entropy_string})}this.kickCachedLinks()}kickCachedLinks(){Object.values(this.templates).forEach((template)=>{template.forEach((node)=>{let walker=document.createTreeWalker(node,NodeFilter.SHOW_ELEMENT);while(walker.nextNode()){let element=walker.currentNode;if(element.tagName==="LINK"){let attr=element.getAttribute("href");if(attr){let entropy_string=Math.random().toString().substring(0,10),[href]=attr.split("?entropy=");element.setAttribute("href",href+"?entropy="+entropy_string)}}}})})}async readFiles(target,contents,bubbles,realId,name){let files=target.files,file_contents={};for(let i=0;i{if(contents[key])contents[key].push(value);else contents[key]=[value]}),{valid:form.checkValidity(),values:contents}}function retrieveSelectValue(target){let options=target.selectedOptions,values=[];for(let i=0;icontents={...contents,...obj};if(event instanceof WheelEvent)extend(serializeWheelEvent(event));if(event instanceof MouseEvent)extend(serializeMouseEvent(event));if(event instanceof KeyboardEvent)extend(serializeKeyboardEvent(event));if(event instanceof InputEvent)extend(serializeInputEvent(event,target));if(event instanceof PointerEvent)extend(serializePointerEvent(event));if(event instanceof AnimationEvent)extend(serializeAnimationEvent(event));if(event instanceof TransitionEvent)extend({property_name:event.propertyName,elapsed_time:event.elapsedTime,pseudo_element:event.pseudoElement});if(event instanceof CompositionEvent)extend({data:event.data});if(event instanceof DragEvent)extend(serializeDragEvent(event));if(event instanceof FocusEvent)extend({});if(event instanceof ClipboardEvent)extend({});if(event instanceof CustomEvent){const detail=event.detail;if(detail instanceof ResizeObserverEntry)extend(serializeResizeEventDetail(detail))}if(typeof TouchEvent!=="undefined"&&event instanceof TouchEvent)extend(serializeTouchEvent(event));if(event.type==="submit"||event.type==="reset"||event.type==="click"||event.type==="change"||event.type==="input")extend(serializeInputEvent(event,target));if(event instanceof DragEvent);return contents}var toSerializableResizeObserverSize=function(size,is_inline_width){return[is_inline_width?size.inlineSize:size.blockSize,is_inline_width?size.blockSize:size.inlineSize]};function serializeResizeEventDetail(detail){let is_inline_width=!0;if(detail.target instanceof HTMLElement){if(window.getComputedStyle(detail.target).getPropertyValue("writing-mode")!=="horizontal-tb")is_inline_width=!1}return{border_box_size:detail.borderBoxSize!==void 0?toSerializableResizeObserverSize(detail.borderBoxSize[0],is_inline_width):detail.contentRect,content_box_size:detail.contentBoxSize!==void 0?toSerializableResizeObserverSize(detail.contentBoxSize[0],is_inline_width):detail.contentRect,content_rect:detail.contentRect}}var serializeInputEvent=function(event,target){let contents={};if(target instanceof HTMLElement){let values=retrieveValues(event,target);contents.values=values.values,contents.valid=values.valid}if(event.target instanceof HTMLInputElement){let target2=event.target,value=target2.value??target2.textContent??"";if(target2.type==="checkbox")value=target2.checked?"true":"false";else if(target2.type==="radio")value=target2.value;contents.value=value}if(event.target instanceof HTMLTextAreaElement)contents.value=event.target.value;if(event.target instanceof HTMLSelectElement)contents.value=retrieveSelectValue(event.target).join(",");if(contents.value===void 0)contents.value="";return contents},serializeWheelEvent=function(event){return{delta_x:event.deltaX,delta_y:event.deltaY,delta_z:event.deltaZ,delta_mode:event.deltaMode}},serializeTouchEvent=function(event){return{alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,shift_key:event.shiftKey,changed_touches:event.changedTouches,target_touches:event.targetTouches,touches:event.touches}},serializePointerEvent=function(event){return{alt_key:event.altKey,button:event.button,buttons:event.buttons,client_x:event.clientX,client_y:event.clientY,ctrl_key:event.ctrlKey,meta_key:event.metaKey,page_x:event.pageX,page_y:event.pageY,screen_x:event.screenX,screen_y:event.screenY,shift_key:event.shiftKey,pointer_id:event.pointerId,width:event.width,height:event.height,pressure:event.pressure,tangential_pressure:event.tangentialPressure,tilt_x:event.tiltX,tilt_y:event.tiltY,twist:event.twist,pointer_type:event.pointerType,is_primary:event.isPrimary}},serializeMouseEvent=function(event){return{alt_key:event.altKey,button:event.button,buttons:event.buttons,client_x:event.clientX,client_y:event.clientY,ctrl_key:event.ctrlKey,meta_key:event.metaKey,offset_x:event.offsetX,offset_y:event.offsetY,page_x:event.pageX,page_y:event.pageY,screen_x:event.screenX,screen_y:event.screenY,shift_key:event.shiftKey}},serializeKeyboardEvent=function(event){return{char_code:event.charCode,is_composing:event.isComposing,key:event.key,alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,key_code:event.keyCode,shift_key:event.shiftKey,location:event.location,repeat:event.repeat,which:event.which,code:event.code}},serializeAnimationEvent=function(event){return{animation_name:event.animationName,elapsed_time:event.elapsedTime,pseudo_element:event.pseudoElement}},serializeDragEvent=function(event){let files=void 0;if(event.dataTransfer&&event.dataTransfer.files&&event.dataTransfer.files.length>0)files={files:{placeholder:[]}};return{mouse:{alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,shift_key:event.shiftKey,...serializeMouseEvent(event)},files}};var handleVirtualdomEventSync=function(endpoint,contents){const xhr=new XMLHttpRequest;return xhr.open("POST",endpoint,!1),xhr.setRequestHeader("Content-Type","application/json"),xhr.send(contents),JSON.parse(xhr.responseText)},getTargetId=function(target){if(!(target instanceof Node))return null;let ourTarget=target,realId=null;while(realId==null){if(ourTarget===null)return null;if(ourTarget instanceof Element)realId=ourTarget.getAttribute("data-dioxus-id");ourTarget=ourTarget.parentNode}return parseInt(realId)},JSChannel_;if(RawInterpreter!==void 0&&RawInterpreter!==null)JSChannel_=RawInterpreter;class NativeInterpreter extends JSChannel_{intercept_link_redirects;ipc;editsPath;eventsPath;kickStylesheets;queuedBytes=[];liveview;constructor(editsPath,eventsPath){super();this.editsPath=editsPath,this.eventsPath=eventsPath,this.kickStylesheets=!1}initialize(root){this.intercept_link_redirects=!0,this.liveview=!1,window.addEventListener("dragover",function(e){if(e.target instanceof Element&&e.target.tagName!="INPUT")e.preventDefault()},!1),window.addEventListener("drop",function(e){if(!(e.target instanceof Element))return;e.preventDefault()},!1),window.addEventListener("click",(event)=>{const target=event.target;if(target instanceof HTMLInputElement&&target.getAttribute("type")==="file"){let target_id=getTargetId(target);if(target_id!==null){const message=this.serializeIpcMessage("file_dialog",{event:"change&input",accept:target.getAttribute("accept"),directory:target.getAttribute("webkitdirectory")==="true",multiple:target.hasAttribute("multiple"),target:target_id,bubbles:event.bubbles});this.ipc.postMessage(message),event.preventDefault()}}}),this.ipc=window.ipc;const handler=(event)=>this.handleEvent(event,event.type,!0);super.initialize(root,handler)}serializeIpcMessage(method,params={}){return JSON.stringify({method,params})}scrollTo(id,behavior){const node=this.nodes[id];if(node instanceof HTMLElement)node.scrollIntoView({behavior})}getScrollHeight(id){const node=this.nodes[id];if(node instanceof HTMLElement)return node.scrollHeight}getScrollLeft(id){const node=this.nodes[id];if(node instanceof HTMLElement)return node.scrollLeft}getScrollTop(id){const node=this.nodes[id];if(node instanceof HTMLElement)return node.scrollTop}getScrollWidth(id){const node=this.nodes[id];if(node instanceof HTMLElement)return node.scrollWidth}getClientRect(id){const node=this.nodes[id];if(node instanceof HTMLElement){const rect=node.getBoundingClientRect();return{type:"GetClientRect",origin:[rect.x,rect.y],size:[rect.width,rect.height]}}}setFocus(id,focus){const node=this.nodes[id];if(node instanceof HTMLElement)if(focus)node.focus();else node.blur()}loadChild(array){let node=this.stack[this.stack.length-1];for(let i=0;i0;end--)node=node.nextSibling}return node}appendChildren(id,many){const root=this.nodes[id],els=this.stack.splice(this.stack.length-many);for(let k=0;k{this.flushQueuedBytes(),this.waitForRequest(headless)})}waitForRequest(headless){fetch(new Request(this.editsPath)).then((response)=>response.arrayBuffer()).then((bytes)=>{this.rafEdits(headless,bytes)})}kickAllStylesheetsOnPage(){console.log("kicking all stylesheets");let stylesheets=document.querySelectorAll("link[rel=stylesheet]");for(let i=0;i{let entropy_string=Math.random().toString().substring(0,10),[href]=sheet.href.split("?entropy=");sheet.href=href+"?entropy="+entropy_string})}this.kickCachedLinks()}kickCachedLinks(){Object.values(this.templates).forEach((template)=>{template.forEach((node)=>{let walker=document.createTreeWalker(node,NodeFilter.SHOW_ELEMENT);while(walker.nextNode()){let element=walker.currentNode;if(element.tagName==="LINK"){let attr=element.getAttribute("href");if(attr){let entropy_string=Math.random().toString().substring(0,10),[href]=attr.split("?entropy=");element.setAttribute("href",href+"?entropy="+entropy_string)}}}})})}async readFiles(target,contents,bubbles,realId,name){let files=target.files,file_contents={};for(let i=0;i Date: Thu, 5 Sep 2024 00:11:52 -0700 Subject: [PATCH 066/139] Clean up stdout joiner --- packages/cli/src/assets.rs | 2 +- packages/cli/src/serve/mod.rs | 12 +++- packages/cli/src/serve/runner.rs | 108 ++++++++++++------------------ packages/cli/src/serve/update.rs | 8 +-- packages/cli/src/serve/watcher.rs | 4 +- 5 files changed, 56 insertions(+), 78 deletions(-) diff --git a/packages/cli/src/assets.rs b/packages/cli/src/assets.rs index 73fbe6b9c8..bc4ed46729 100644 --- a/packages/cli/src/assets.rs +++ b/packages/cli/src/assets.rs @@ -198,7 +198,7 @@ impl AssetManifest { destination: &Path, target_asset: &Path, optimize: bool, - pre_compress: bool, + _pre_compress: bool, ) { let src = self.assets.get(target_asset).unwrap(); diff --git a/packages/cli/src/serve/mod.rs b/packages/cli/src/serve/mod.rs index e0456ae65c..64111429fa 100644 --- a/packages/cli/src/serve/mod.rs +++ b/packages/cli/src/serve/mod.rs @@ -203,9 +203,15 @@ async fn handle_msg( } } - ServeUpdate::StdoutReceived { target, msg } => {} + ServeUpdate::StdoutReceived { + platform: target, + msg, + } => {} - ServeUpdate::StderrReceived { target, msg } => {} + ServeUpdate::StderrReceived { + platform: target, + msg, + } => {} // nothing - the builder just signals that there are no more pending builds // maybe ping the logger to wipe any logs and/or clear the screen @@ -214,7 +220,7 @@ async fn handle_msg( // If the process exited *cleanly*, we can exit ServeUpdate::ProcessExited { status, - target_platform, + platform: target_platform, } => { // // Then remove the child process // builder diff --git a/packages/cli/src/serve/runner.rs b/packages/cli/src/serve/runner.rs index 4133b39840..3bce6ee325 100644 --- a/packages/cli/src/serve/runner.rs +++ b/packages/cli/src/serve/runner.rs @@ -5,6 +5,8 @@ use crate::{ cli::serve::ServeArgs, DioxusCrate, Result, }; +use axum::serve::Serve; +use futures_util::stream::FuturesUnordered; use manganis_core::ResourceAsset; use std::{collections::HashMap, fs, net::SocketAddr, path::PathBuf, process::Stdio}; use tokio::process::Child; @@ -12,6 +14,7 @@ use tokio::{ io::{AsyncBufReadExt, BufReader, Lines}, process::{ChildStderr, ChildStdout, Command}, }; +use tokio_stream::StreamExt; use uuid::Uuid; pub struct AppRunner { @@ -23,18 +26,6 @@ pub struct AppRunner { pub running: HashMap, } -/// A handle to a running app -pub struct AppHandle { - pub id: Uuid, - pub app: AppBundle, - pub executable: PathBuf, - pub child: Option, - pub stdout: Option>, - pub stderr: Option>, - // pub stdout_line: String, - // pub stderr_line: String, -} - impl AppRunner { pub fn start(serve: &ServeArgs, config: &DioxusCrate) -> Self { Self { @@ -43,58 +34,29 @@ impl AppRunner { } pub async fn wait(&mut self) -> ServeUpdate { - // // Exits and stdout/stderr - // let processes = self.running.iter_mut().filter_map(|(target, request)| { - // let Some(child) = request.child else { - // return None; - // }; - - // Some(Box::pin(async move { - // // - // (*target, child.wait().await) - // })) - // }); - - // ((target, exit_status), _, _) = futures_util::future::select_all(processes) => { - // BuildUpdate::ProcessExited { status: exit_status, target_platform: target } - // } - - // let has_running_apps = !self.running_apps.is_empty(); - // let next_stdout = self.running_apps.values_mut().map(|app| { - // let future = async move { - // let (stdout, stderr) = match &mut app.output { - // Some(out) => ( - // ok_and_some(out.stdout.next_line()), - // ok_and_some(out.stderr.next_line()), - // ), - // None => return futures_util::future::pending().await, - // }; - - // tokio::select! { - // line = stdout => (app.result.target_platform, Some(line), None), - // line = stderr => (app.result.target_platform, None, Some(line)), - // } - // }; - // Box::pin(future) - // }); - - // let next_stdout = async { - // if has_running_apps { - // select_all(next_stdout).await.0 - // } else { - // futures_util::future::pending().await - // } - // }; - // (platform, stdout, stderr) = next_stdout => { - // if let Some(stdout) = stdout { - // self.push_stdout(platform, stdout); - // } - // if let Some(stderr) = stderr { - // self.push_stderr(platform, stderr); - // } - // }, - - futures_util::future::pending().await + let next = self.running.iter_mut().map(|(platform, handle)| async { + let platform = *platform; + + tokio::select! { + Ok(Some(msg)) = handle.stdout.as_mut().unwrap().next_line(), if handle.stdout.is_some() => { + ServeUpdate::StdoutReceived { platform, msg } + }, + Ok(Some(msg)) = handle.stderr.as_mut().unwrap().next_line(), if handle.stderr.is_some() => { + ServeUpdate::StderrReceived { platform, msg } + }, + status = handle.child.as_mut().unwrap().wait(), if handle.child.is_some() => { + match status { + Ok(status) => ServeUpdate::ProcessExited { status, platform }, + Err(_err) => todo!("handle error in process joining?"), + } + } + } + }); + + match FuturesUnordered::from_iter(next).next().await { + Some(msg) => msg, + None => futures_util::future::pending().await, + } } /// Finally "bundle" this app and return a handle to it @@ -162,8 +124,8 @@ impl AppRunner { let mut child = cmd.spawn()?; let stdout = BufReader::new(child.stdout.take().unwrap()); let stderr = BufReader::new(child.stderr.take().unwrap()); - handle.stdout = Some(stdout); - handle.stderr = Some(stderr); + handle.stdout = Some(stdout.lines()); + handle.stderr = Some(stderr.lines()); handle.child = Some(child); } Platform::Ios => {} @@ -225,13 +187,25 @@ impl AppRunner { } } +/// A handle to a running app +pub struct AppHandle { + pub id: Uuid, + pub app: AppBundle, + pub executable: PathBuf, + pub child: Option, + pub stdout: Option>>, + pub stderr: Option>>, + // pub stdout_line: String, + // pub stderr_line: String, +} + impl AppHandle { /// Update an asset in the running apps /// /// Might need to upload the asset to the simulator or overwrite it within the bundle /// /// Returns the name of the asset in the bundle if it exists - pub fn update_asset(&self, path: &PathBuf) -> Option { + pub fn hotreload_asset(&self, path: &PathBuf) -> Option { let resource = self.app.assets.assets.get(path).cloned()?; self.app diff --git a/packages/cli/src/serve/update.rs b/packages/cli/src/serve/update.rs index 9ddac7bfb6..9d48b79f19 100644 --- a/packages/cli/src/serve/update.rs +++ b/packages/cli/src/serve/update.rs @@ -19,7 +19,7 @@ pub enum ServeUpdate { /// /// We will poll lines and any content in a 50ms interval StdoutReceived { - target: Platform, + platform: Platform, msg: String, }, @@ -28,13 +28,13 @@ pub enum ServeUpdate { /// /// We will poll lines and any content in a 50ms interval StderrReceived { - target: Platform, + platform: Platform, msg: String, }, ProcessExited { - target_platform: Platform, - status: Result, + platform: Platform, + status: ExitStatus, }, FilesChanged { diff --git a/packages/cli/src/serve/watcher.rs b/packages/cli/src/serve/watcher.rs index fc2035eda0..327c48702c 100644 --- a/packages/cli/src/serve/watcher.rs +++ b/packages/cli/src/serve/watcher.rs @@ -224,10 +224,8 @@ impl Watcher { // Look through the runners to see if any of them have an asset that matches the path _ => { - tracing::info!("Hotreloading asset {path:?}"); for runner in runner.running.values() { - if let Some(bundled_name) = runner.update_asset(&path) { - tracing::info!("Hotreloading bundled asset {bundled_name:?}"); + if let Some(bundled_name) = runner.hotreload_asset(&path) { assets.push(bundled_name); } } From ce4339be3863a5001cc2325c0b45e90dfe821c52 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 5 Sep 2024 00:27:49 -0700 Subject: [PATCH 067/139] amazingly serve seems done --- packages/cli/src/builder/builder.rs | 6 +- packages/cli/src/serve/mod.rs | 72 ++++++++------------ packages/cli/src/serve/output.rs | 100 +++++++++++++++------------- packages/cli/src/serve/runner.rs | 37 ++++++---- 4 files changed, 107 insertions(+), 108 deletions(-) diff --git a/packages/cli/src/builder/builder.rs b/packages/cli/src/builder/builder.rs index 12610f1f47..9cf8fdeacd 100644 --- a/packages/cli/src/builder/builder.rs +++ b/packages/cli/src/builder/builder.rs @@ -33,7 +33,7 @@ pub enum BuildUpdate { }, /// All builds have finished and there's nothing left to do - Finished, + AllFinished, } impl Builder { @@ -93,7 +93,7 @@ impl Builder { pub async fn wait_for_finish(&mut self) { loop { let next = self.wait().await; - if let BuildUpdate::Finished = next { + if let BuildUpdate::AllFinished = next { return; } } @@ -106,7 +106,7 @@ impl Builder { /// Returns immediately with `Finished` if there are no more builds to run - don't poll-loop this! pub async fn wait(&mut self) -> BuildUpdate { if self.building.is_empty() { - return BuildUpdate::Finished; + return BuildUpdate::AllFinished; } tokio::select! { diff --git a/packages/cli/src/serve/mod.rs b/packages/cli/src/serve/mod.rs index 64111429fa..bf97e3521c 100644 --- a/packages/cli/src/serve/mod.rs +++ b/packages/cli/src/serve/mod.rs @@ -185,10 +185,11 @@ async fn handle_msg( ServeUpdate::BuildUpdate(BuildUpdate::BuildReady { target, result }) => { tracing::info!("Opening app for [{}]", target); - match runner + let handle = runner .open(result, devserver.ip, devserver.fullstack_address()) - .await - { + .await; + + match handle { Ok(handle) => { // Make sure we immediately capture the stdout/stderr of the executable - // otherwise it'll clobber our terminal output @@ -197,47 +198,37 @@ async fn handle_msg( // And then finally tell the server to reload devserver.send_reload_command().await; } + Err(e) => { tracing::error!("Failed to open app: {}", e); } } } - ServeUpdate::StdoutReceived { - platform: target, - msg, - } => {} - - ServeUpdate::StderrReceived { - platform: target, - msg, - } => {} - // nothing - the builder just signals that there are no more pending builds - // maybe ping the logger to wipe any logs and/or clear the screen - ServeUpdate::BuildUpdate(BuildUpdate::Finished) => {} + ServeUpdate::BuildUpdate(BuildUpdate::AllFinished) => {} // If the process exited *cleanly*, we can exit - ServeUpdate::ProcessExited { - status, - platform: target_platform, - } => { - // // Then remove the child process - // builder - // .children - // .retain(|(platform, _)| *platform != target_platform); - // match status { - // Ok(status) => { - // if status.success() { - // break; - // } else { - // tracing::error!("Application exited with status: {status}"); - // } - // } - // Err(e) => { - // tracing::error!("Application exited with error: {e}"); - // } - // } + ServeUpdate::ProcessExited { status, platform } => { + runner.shutdown().await; + + if !status.success() { + tracing::error!("Application [{platform}] exited with status: {status}"); + } + + return Ok(ControlFlow::Break(())); + } + + ServeUpdate::StdoutReceived { platform, msg } => { + screen.push_stdout(platform, msg); + } + + ServeUpdate::StderrReceived { platform, msg } => { + screen.push_stderr(platform, msg); + } + + ServeUpdate::TracingLog { log } => { + screen.push_inner_log(log); } ServeUpdate::TuiInput { event } => { @@ -247,17 +238,6 @@ async fn handle_msg( devserver.start_build().await } } - - ServeUpdate::TracingLog { log } => { - screen.push_log( - LogSource::Internal, - crate::builder::BuildMessage { - level: tracing::Level::INFO, - message: crate::builder::MessageType::Text(log), - source: crate::builder::MessageSource::Dev, - }, - ); - } } Ok(ControlFlow::Continue(())) diff --git a/packages/cli/src/serve/output.rs b/packages/cli/src/serve/output.rs index f1af42b5d2..cd7178f024 100644 --- a/packages/cli/src/serve/output.rs +++ b/packages/cli/src/serve/output.rs @@ -6,7 +6,6 @@ use core::panic; use crossterm::{ event::{Event, EventStream, KeyCode, KeyModifiers, MouseEventKind}, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, - tty::IsTty, ExecutableCommand, }; use dioxus_devtools_types::ClientMsg; @@ -25,48 +24,6 @@ use tracing::Level; use super::{update::ServeUpdate, AppHandle, Builder, DevServer, Watcher}; -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub enum LogSource { - Internal, - Target(Platform), -} - -impl Display for LogSource { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - LogSource::Internal => write!(f, "CLI"), - LogSource::Target(platform) => write!(f, "{platform}"), - } - } -} - -impl From for LogSource { - fn from(platform: Platform) -> Self { - LogSource::Target(platform) - } -} - -#[derive(Default)] -pub struct BuildProgress { - internal_logs: Vec, - build_logs: HashMap, -} - -impl BuildProgress { - pub fn progress(&self) -> f64 { - self.build_logs - .values() - .min_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)) - .map(|build| match build.stage { - Stage::Initializing => 0.0, - Stage::InstallingWasmTooling => 0.0, - Stage::Compiling => build.progress, - Stage::OptimizingWasm | Stage::OptimizingAssets | Stage::Finished => 1.0, - }) - .unwrap_or_default() - } -} - pub struct Output { term: Rc>>, @@ -168,7 +125,7 @@ impl Output { } /// Add a message from stderr to the logs - fn push_stderr(&mut self, platform: Platform, stderr: String) { + pub fn push_stderr(&mut self, platform: Platform, stderr: String) { // self.set_tab(Tab::BuildLog); @@ -193,7 +150,7 @@ impl Output { } /// Add a message from stdout to the logs - fn push_stdout(&mut self, platform: Platform, stdout: String) { + pub fn push_stdout(&mut self, platform: Platform, stdout: String) { // self.running_apps // .get_mut(&platform) @@ -429,6 +386,17 @@ impl Output { self.scroll = (self.num_lines_with_wrapping).saturating_sub(self.term_height); } + pub fn push_inner_log(&mut self, msg: String) { + self.push_log( + LogSource::Internal, + crate::builder::BuildMessage { + level: tracing::Level::INFO, + message: crate::builder::MessageType::Text(msg), + source: crate::builder::MessageSource::Dev, + }, + ); + } + pub fn push_log(&mut self, platform: impl Into, message: BuildMessage) { let source = platform.into(); let snapped = self.is_snapped(source); @@ -889,3 +857,45 @@ async fn rustc_version() -> String { }) .unwrap_or_else(|| "".to_string()) } + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum LogSource { + Internal, + Target(Platform), +} + +impl Display for LogSource { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + LogSource::Internal => write!(f, "CLI"), + LogSource::Target(platform) => write!(f, "{platform}"), + } + } +} + +impl From for LogSource { + fn from(platform: Platform) -> Self { + LogSource::Target(platform) + } +} + +#[derive(Default)] +pub struct BuildProgress { + internal_logs: Vec, + build_logs: HashMap, +} + +impl BuildProgress { + pub fn progress(&self) -> f64 { + self.build_logs + .values() + .min_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)) + .map(|build| match build.stage { + Stage::Initializing => 0.0, + Stage::InstallingWasmTooling => 0.0, + Stage::Compiling => build.progress, + Stage::OptimizingWasm | Stage::OptimizingAssets | Stage::Finished => 1.0, + }) + .unwrap_or_default() + } +} diff --git a/packages/cli/src/serve/runner.rs b/packages/cli/src/serve/runner.rs index 3bce6ee325..fe5e755e23 100644 --- a/packages/cli/src/serve/runner.rs +++ b/packages/cli/src/serve/runner.rs @@ -1,14 +1,11 @@ use super::ServeUpdate; use crate::{ - build, - builder::{AppBundle, BuildUpdate, Platform}, + builder::{AppBundle, Platform}, cli::serve::ServeArgs, DioxusCrate, Result, }; -use axum::serve::Serve; use futures_util::stream::FuturesUnordered; -use manganis_core::ResourceAsset; -use std::{collections::HashMap, fs, net::SocketAddr, path::PathBuf, process::Stdio}; +use std::{collections::HashMap, net::SocketAddr, path::PathBuf, process::Stdio}; use tokio::process::Child; use tokio::{ io::{AsyncBufReadExt, BufReader, Lines}, @@ -33,10 +30,22 @@ impl AppRunner { } } + pub async fn shutdown(&mut self) { + for (_, mut handle) in self.running.drain() { + if let Some(mut child) = handle.child.take() { + let _ = child.kill().await; + } + } + } + pub async fn wait(&mut self) -> ServeUpdate { - let next = self.running.iter_mut().map(|(platform, handle)| async { - let platform = *platform; + // If there are no running apps, we can just return pending to avoid deadlocking + if self.running.is_empty() { + return futures_util::future::pending().await; + } + self.running.iter_mut().map(|(platform, handle)| async { + let platform = *platform; tokio::select! { Ok(Some(msg)) = handle.stdout.as_mut().unwrap().next_line(), if handle.stdout.is_some() => { ServeUpdate::StdoutReceived { platform, msg } @@ -45,18 +54,18 @@ impl AppRunner { ServeUpdate::StderrReceived { platform, msg } }, status = handle.child.as_mut().unwrap().wait(), if handle.child.is_some() => { + tracing::info!("Child process exited with status: {status:?}"); match status { Ok(status) => ServeUpdate::ProcessExited { status, platform }, Err(_err) => todo!("handle error in process joining?"), } } } - }); - - match FuturesUnordered::from_iter(next).next().await { - Some(msg) => msg, - None => futures_util::future::pending().await, - } + }) + .collect::>() + .next() + .await + .expect("Stream to pending if not empty") } /// Finally "bundle" this app and return a handle to it @@ -134,7 +143,7 @@ impl AppRunner { Platform::Liveview => {} } - if let Some(previous) = self.running.insert(platform, handle) { + if let Some(_previous) = self.running.insert(platform, handle) { // close the old app, gracefully, hopefully } From 61b47b4645639951d50df520553fa6ca89f197f9 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 5 Sep 2024 00:36:26 -0700 Subject: [PATCH 068/139] lil bit more cleanup --- packages/cli/src/assets.rs | 5 - .../src/{dx_build_info.rs => build_info.rs} | 0 packages/cli/src/builder/builder.rs | 1 - packages/cli/src/builder/cargo.rs | 16 +- packages/cli/src/builder/handle.rs | 1 - packages/cli/src/builder/mod.rs | 1 - packages/cli/src/builder/progress.rs | 268 +++++++++--------- packages/cli/src/cli/bundle.rs | 10 +- packages/cli/src/cli/mod.rs | 4 +- packages/cli/src/main.rs | 5 +- packages/cli/src/serve/output.rs | 6 +- packages/cli/src/serve/server.rs | 5 +- packages/cli/src/tracer.rs | 1 - 13 files changed, 149 insertions(+), 174 deletions(-) rename packages/cli/src/{dx_build_info.rs => build_info.rs} (100%) delete mode 100644 packages/cli/src/builder/handle.rs delete mode 100644 packages/cli/src/tracer.rs diff --git a/packages/cli/src/assets.rs b/packages/cli/src/assets.rs index bc4ed46729..30063844ed 100644 --- a/packages/cli/src/assets.rs +++ b/packages/cli/src/assets.rs @@ -1,13 +1,8 @@ -use crate::builder::{ - BuildMessage, BuildRequest, MessageSource, MessageType, Stage, UpdateBuildProgress, UpdateStage, -}; use crate::Result; use anyhow::Context; use brotli::enc::BrotliEncoderParams; -use futures_channel::mpsc::UnboundedSender; use manganis_core::{LinkSection, ResourceAsset}; use object::{read::archive::ArchiveFile, File as ObjectFile, Object, ObjectSection}; -use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use std::ffi::OsString; use std::fs; use std::path::Path; diff --git a/packages/cli/src/dx_build_info.rs b/packages/cli/src/build_info.rs similarity index 100% rename from packages/cli/src/dx_build_info.rs rename to packages/cli/src/build_info.rs diff --git a/packages/cli/src/builder/builder.rs b/packages/cli/src/builder/builder.rs index 9cf8fdeacd..e9d293ce7e 100644 --- a/packages/cli/src/builder/builder.rs +++ b/packages/cli/src/builder/builder.rs @@ -1,7 +1,6 @@ use crate::build::BuildArgs; use crate::builder::*; use crate::dioxus_crate::DioxusCrate; -use crate::AppBundle; use crate::Result; use futures_util::StreamExt; use progress::{ProgressRx, ProgressTx, UpdateBuildProgress}; diff --git a/packages/cli/src/builder/cargo.rs b/packages/cli/src/builder/cargo.rs index c081bda26e..c808aa89ff 100644 --- a/packages/cli/src/builder/cargo.rs +++ b/packages/cli/src/builder/cargo.rs @@ -1,10 +1,9 @@ use super::{AppBundle, BuildRequest}; use crate::builder::Platform; -use crate::{assets::AssetManifest, builder::progress::*, link::LINK_OUTPUT_ENV_VAR}; +use crate::{assets::AssetManifest, link::LINK_OUTPUT_ENV_VAR}; use crate::{link::InterceptedArgs, Result}; use anyhow::Context; -use std::{env::current_exe, fs::create_dir_all}; -use std::{path::PathBuf, process::Stdio}; +use std::process::Stdio; use tokio::process::Command; impl BuildRequest { @@ -72,12 +71,12 @@ impl BuildRequest { // // NOTE: that -Csave-temps=y is needed to prevent rustc from deleting the incremental cache... // This might not be a "stable" way of keeping artifacts around, but it's in stable rustc - tokio::process::Command::new("cargo") + Command::new("cargo") .arg("rustc") .args(self.build_arguments()) .arg("--offline") /* don't use the network, should already be resolved */ .arg("--") - .arg(format!("-Clinker={}", current_exe().unwrap().display())) /* pass ourselves in */ + .arg(format!("-Clinker={}", std::env::current_exe().unwrap().display())) /* pass ourselves in */ .env(LINK_OUTPUT_ENV_VAR, tmp_file.path()) /* but with the env var pointing to the temp file */ .arg("-Csave-temps=y") /* don't delete the incremental cache */ .stdout(Stdio::piped()) @@ -86,12 +85,13 @@ impl BuildRequest { .await?; // Read the contents of the temp file - let args = std::fs::read_to_string(tmp_file.path()).expect("Failed to read linker output"); + let args = + std::fs::read_to_string(tmp_file.path()).context("Failed to read linker output")?; // Parse them as a Vec which is just our informal format for link args in the cli // Todo: this might be wrong-ish on windows? The format is weird - let args = - serde_json::from_str::(&args).expect("Failed to parse linker output"); + let args = serde_json::from_str::(&args) + .context("Failed to parse linker output")?; Ok(AssetManifest::new_from_linker_intercept(args)) } diff --git a/packages/cli/src/builder/handle.rs b/packages/cli/src/builder/handle.rs deleted file mode 100644 index 8b13789179..0000000000 --- a/packages/cli/src/builder/handle.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/packages/cli/src/builder/mod.rs b/packages/cli/src/builder/mod.rs index 542a7b11f2..f4be6631f7 100644 --- a/packages/cli/src/builder/mod.rs +++ b/packages/cli/src/builder/mod.rs @@ -6,7 +6,6 @@ /// receiver. mod builder; mod cargo; -mod handle; mod platform; mod profiles; mod progress; diff --git a/packages/cli/src/builder/progress.rs b/packages/cli/src/builder/progress.rs index cbe0f3a2a7..48a20c93ed 100644 --- a/packages/cli/src/builder/progress.rs +++ b/packages/cli/src/builder/progress.rs @@ -11,139 +11,6 @@ use std::{fmt::Display, path::Path}; use tokio::{io::AsyncBufReadExt, process::Command}; use tracing::Level; -pub type ProgressTx = UnboundedSender; -pub type ProgressRx = UnboundedReceiver; - -#[derive(Default, Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Copy)] -pub enum Stage { - #[default] - Initializing = 0, - InstallingWasmTooling = 1, - Compiling = 2, - OptimizingWasm = 3, - OptimizingAssets = 4, - Finished = 5, -} - -impl Deref for Stage { - type Target = str; - - fn deref(&self) -> &Self::Target { - match self { - Stage::Initializing => "Initializing", - Stage::InstallingWasmTooling => "Installing Wasm Tooling", - Stage::Compiling => "Compiling", - Stage::OptimizingWasm => "Optimizing Wasm", - Stage::OptimizingAssets => "Optimizing Assets", - Stage::Finished => "Finished", - } - } -} - -impl std::fmt::Display for Stage { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.deref()) - } -} - -#[derive(Debug, Clone)] -pub struct UpdateBuildProgress { - pub stage: Stage, - pub update: UpdateStage, - pub platform: Platform, -} - -impl UpdateBuildProgress { - pub fn to_std_out(&self) { - match &self.update { - UpdateStage::Start => println!("--- {} ---", self.stage), - UpdateStage::AddMessage(message) => match &message.message { - MessageType::Cargo(message) => { - println!("{}", message.rendered.clone().unwrap_or_default()); - } - MessageType::Text(message) => { - println!("{}", message); - } - }, - UpdateStage::SetProgress(progress) => { - println!("Build progress {:0.0}%", progress * 100.0); - } - UpdateStage::Failed(message) => { - println!("Build failed: {}", message); - } - } - } -} - -#[derive(Debug, Clone, PartialEq)] -pub enum UpdateStage { - Start, - AddMessage(BuildMessage), - SetProgress(f64), - Failed(String), -} - -#[derive(Debug, Clone, PartialEq)] -pub struct BuildMessage { - pub level: Level, - pub message: MessageType, - pub source: MessageSource, -} - -#[derive(Debug, Clone, PartialEq)] -pub enum MessageType { - Cargo(Diagnostic), - Text(String), -} - -/// Represents the source of where a message came from. -/// -/// The CLI will render a prefix according to the message type -/// but this prefix, [`MessageSource::to_string()`] shouldn't be used if a strict message source is required. -#[derive(Debug, Clone, PartialEq)] -pub enum MessageSource { - /// Represents any message from the running application. Renders `[app]` - App, - - /// Represents any generic message from the CLI. Renders `[dev]` - /// - /// Usage of Tracing inside of the CLI will be routed to this type. - Dev, - - /// Represents a message from the build process. Renders `[bld]` - /// - /// This is anything emitted from a build process such as cargo and optimizations. - Build, -} - -impl Display for MessageSource { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::App => write!(f, "app"), - Self::Dev => write!(f, "dev"), - Self::Build => write!(f, "bld"), - } - } -} - -impl From for BuildMessage { - fn from(message: Diagnostic) -> Self { - Self { - level: match message.level { - cargo_metadata::diagnostic::DiagnosticLevel::Ice - | cargo_metadata::diagnostic::DiagnosticLevel::FailureNote - | cargo_metadata::diagnostic::DiagnosticLevel::Error => Level::ERROR, - cargo_metadata::diagnostic::DiagnosticLevel::Warning => Level::WARN, - cargo_metadata::diagnostic::DiagnosticLevel::Note => Level::INFO, - cargo_metadata::diagnostic::DiagnosticLevel::Help => Level::DEBUG, - _ => Level::DEBUG, - }, - source: MessageSource::Build, - message: MessageType::Cargo(message), - } - } -} - impl BuildRequest { /// Run `cargo`, returning the location of the final exectuable /// @@ -323,9 +190,7 @@ impl BuildRequest { / 3.5) as usize }) } -} -impl BuildRequest { pub fn status_build_finished(&self) { tracing::info!("🚩 Build completed: [{}]", self.krate.out_dir().display()); @@ -361,3 +226,136 @@ impl BuildRequest { // }); } } + +pub type ProgressTx = UnboundedSender; +pub type ProgressRx = UnboundedReceiver; + +#[derive(Default, Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Copy)] +pub enum Stage { + #[default] + Initializing = 0, + InstallingWasmTooling = 1, + Compiling = 2, + OptimizingWasm = 3, + OptimizingAssets = 4, + Finished = 5, +} + +impl Deref for Stage { + type Target = str; + + fn deref(&self) -> &Self::Target { + match self { + Stage::Initializing => "Initializing", + Stage::InstallingWasmTooling => "Installing Wasm Tooling", + Stage::Compiling => "Compiling", + Stage::OptimizingWasm => "Optimizing Wasm", + Stage::OptimizingAssets => "Optimizing Assets", + Stage::Finished => "Finished", + } + } +} + +impl std::fmt::Display for Stage { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.deref()) + } +} + +#[derive(Debug, Clone)] +pub struct UpdateBuildProgress { + pub stage: Stage, + pub update: UpdateStage, + pub platform: Platform, +} + +impl UpdateBuildProgress { + pub fn to_std_out(&self) { + match &self.update { + UpdateStage::Start => println!("--- {} ---", self.stage), + UpdateStage::AddMessage(message) => match &message.message { + MessageType::Cargo(message) => { + println!("{}", message.rendered.clone().unwrap_or_default()); + } + MessageType::Text(message) => { + println!("{}", message); + } + }, + UpdateStage::SetProgress(progress) => { + println!("Build progress {:0.0}%", progress * 100.0); + } + UpdateStage::Failed(message) => { + println!("Build failed: {}", message); + } + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum UpdateStage { + Start, + AddMessage(BuildMessage), + SetProgress(f64), + Failed(String), +} + +#[derive(Debug, Clone, PartialEq)] +pub struct BuildMessage { + pub level: Level, + pub message: MessageType, + pub source: MessageSource, +} + +#[derive(Debug, Clone, PartialEq)] +pub enum MessageType { + Cargo(Diagnostic), + Text(String), +} + +/// Represents the source of where a message came from. +/// +/// The CLI will render a prefix according to the message type +/// but this prefix, [`MessageSource::to_string()`] shouldn't be used if a strict message source is required. +#[derive(Debug, Clone, PartialEq)] +pub enum MessageSource { + /// Represents any message from the running application. Renders `[app]` + App, + + /// Represents any generic message from the CLI. Renders `[dev]` + /// + /// Usage of Tracing inside of the CLI will be routed to this type. + Dev, + + /// Represents a message from the build process. Renders `[bld]` + /// + /// This is anything emitted from a build process such as cargo and optimizations. + Build, +} + +impl Display for MessageSource { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::App => write!(f, "app"), + Self::Dev => write!(f, "dev"), + Self::Build => write!(f, "bld"), + } + } +} + +impl From for BuildMessage { + fn from(message: Diagnostic) -> Self { + Self { + level: match message.level { + cargo_metadata::diagnostic::DiagnosticLevel::Ice + | cargo_metadata::diagnostic::DiagnosticLevel::FailureNote + | cargo_metadata::diagnostic::DiagnosticLevel::Error => Level::ERROR, + cargo_metadata::diagnostic::DiagnosticLevel::Warning => Level::WARN, + cargo_metadata::diagnostic::DiagnosticLevel::Note => Level::INFO, + cargo_metadata::diagnostic::DiagnosticLevel::Help => Level::DEBUG, + _ => Level::DEBUG, + }, + source: MessageSource::Build, + message: MessageType::Cargo(message), + } + } +} diff --git a/packages/cli/src/cli/bundle.rs b/packages/cli/src/cli/bundle.rs index a70e3931ec..6b7d3b7d0d 100644 --- a/packages/cli/src/cli/bundle.rs +++ b/packages/cli/src/cli/bundle.rs @@ -20,14 +20,6 @@ pub struct Bundle { pub build_arguments: BuildArgs, } -impl std::ops::Deref for Bundle { - type Target = BuildArgs; - - fn deref(&self) -> &Self::Target { - &self.build_arguments - } -} - #[derive(Clone, Debug)] pub enum PackageType { MacOsBundle, @@ -185,7 +177,7 @@ impl Bundle { ); } - if let Some(target) = &self.target_args.target { + if let Some(target) = &self.build_arguments.target_args.target { settings = settings.target(target.to_string()); } diff --git a/packages/cli/src/cli/mod.rs b/packages/cli/src/cli/mod.rs index 1d836b9aa2..f9215608a2 100644 --- a/packages/cli/src/cli/mod.rs +++ b/packages/cli/src/cli/mod.rs @@ -27,8 +27,8 @@ use std::{ pub static VERSION: Lazy = Lazy::new(|| { format!( "{} ({})", - crate::dx_build_info::PKG_VERSION, - crate::dx_build_info::GIT_COMMIT_HASH_SHORT.unwrap_or("was built without git repository") + crate::build_info::PKG_VERSION, + crate::build_info::GIT_COMMIT_HASH_SHORT.unwrap_or("was built without git repository") ) }); diff --git a/packages/cli/src/main.rs b/packages/cli/src/main.rs index 2a6dc2a7eb..7675dab9d2 100644 --- a/packages/cli/src/main.rs +++ b/packages/cli/src/main.rs @@ -3,22 +3,20 @@ #![doc(html_favicon_url = "https://avatars.githubusercontent.com/u/79236386")] pub mod assets; +pub mod build_info; pub mod builder; pub mod bundle_utils; pub mod bundler; pub mod cli; pub mod config; pub mod dioxus_crate; -pub mod dx_build_info; pub mod error; pub mod fastfs; pub mod metadata; pub mod serve; pub mod settings; pub mod tools; -pub mod tracer; -pub use bundler::AppBundle; pub use cli::*; pub use dioxus_crate::*; pub use error::*; @@ -26,7 +24,6 @@ pub use settings::*; use anyhow::Context; use clap::Parser; - use Commands::*; #[tokio::main] diff --git a/packages/cli/src/serve/output.rs b/packages/cli/src/serve/output.rs index cd7178f024..0e0e4f14d5 100644 --- a/packages/cli/src/serve/output.rs +++ b/packages/cli/src/serve/output.rs @@ -9,7 +9,7 @@ use crossterm::{ ExecutableCommand, }; use dioxus_devtools_types::ClientMsg; -use futures_util::{future::select_all, Future, FutureExt, StreamExt}; +use futures_util::{Future, FutureExt, StreamExt}; use ratatui::{prelude::*, widgets::*, TerminalOptions, Viewport}; use std::{ cell::RefCell, @@ -92,10 +92,10 @@ impl Output { dx_version.push_str(env!("CARGO_PKG_VERSION")); - let is_cli_release = crate::dx_build_info::PROFILE == "release"; + let is_cli_release = crate::build_info::PROFILE == "release"; if !is_cli_release { - if let Some(hash) = crate::dx_build_info::GIT_COMMIT_HASH_SHORT { + if let Some(hash) = crate::build_info::GIT_COMMIT_HASH_SHORT { let hash = &hash.trim_start_matches('g')[..4]; dx_version.push('-'); dx_version.push_str(hash); diff --git a/packages/cli/src/serve/server.rs b/packages/cli/src/serve/server.rs index a5cd02adcf..3ae5dae4aa 100644 --- a/packages/cli/src/serve/server.rs +++ b/packages/cli/src/serve/server.rs @@ -1,8 +1,5 @@ +use crate::dioxus_crate::DioxusCrate; use crate::{builder::Platform, serve::ServeArgs}; -use crate::{ - builder::{AppBundle, BuildRequest}, - dioxus_crate::DioxusCrate, -}; use crate::{config::WebHttpsConfig, serve::update::ServeUpdate}; use crate::{Error, Result}; use axum::extract::{Request, State}; diff --git a/packages/cli/src/tracer.rs b/packages/cli/src/tracer.rs deleted file mode 100644 index 8b13789179..0000000000 --- a/packages/cli/src/tracer.rs +++ /dev/null @@ -1 +0,0 @@ - From 59fe248aafb5feaf7ada96551d95c4556674bbd6 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 5 Sep 2024 00:39:52 -0700 Subject: [PATCH 069/139] change from pub to pub(crate) in cli --- packages/cli/src/assets.rs | 62 ++++--- packages/cli/src/builder/builder.rs | 22 +-- packages/cli/src/builder/cargo.rs | 8 +- packages/cli/src/builder/mod.rs | 10 +- packages/cli/src/builder/platform.rs | 8 +- packages/cli/src/builder/profiles.rs | 6 +- packages/cli/src/builder/progress.rs | 40 ++-- packages/cli/src/builder/request.rs | 22 +-- packages/cli/src/builder/result.rs | 2 +- packages/cli/src/builder/web.rs | 12 +- packages/cli/src/bundle_utils.rs | 2 +- packages/cli/src/bundler.rs | 4 +- packages/cli/src/bundler/app.rs | 20 +- packages/cli/src/bundler/web.rs | 2 +- packages/cli/src/cli/autoformat.rs | 16 +- packages/cli/src/cli/build.rs | 58 +++--- packages/cli/src/cli/bundle.rs | 10 +- packages/cli/src/cli/check.rs | 8 +- packages/cli/src/cli/clean.rs | 4 +- packages/cli/src/cli/config.rs | 8 +- packages/cli/src/cli/create.rs | 8 +- packages/cli/src/cli/httpserver.rs | 4 +- packages/cli/src/cli/init.rs | 4 +- packages/cli/src/cli/link.rs | 12 +- packages/cli/src/cli/mod.rs | 36 ++-- packages/cli/src/cli/serve.rs | 28 +-- packages/cli/src/cli/translate.rs | 16 +- packages/cli/src/config.rs | 12 +- packages/cli/src/config/app.rs | 20 +- packages/cli/src/config/bundle.rs | 128 ++++++------- packages/cli/src/config/desktop.rs | 4 +- packages/cli/src/config/dioxus_config.rs | 10 +- packages/cli/src/config/serve.rs | 8 +- packages/cli/src/config/web.rs | 70 +++---- packages/cli/src/dioxus_crate.rs | 48 ++--- packages/cli/src/error.rs | 4 +- packages/cli/src/main.rs | 38 ++-- packages/cli/src/metadata.rs | 6 +- packages/cli/src/serve/detect.rs | 2 +- .../cli/src/serve/hot_reloading_file_map.rs | 34 ++-- packages/cli/src/serve/mod.rs | 4 +- packages/cli/src/serve/output.rs | 174 +++++++++--------- packages/cli/src/serve/proxy.rs | 4 +- packages/cli/src/serve/runner.rs | 32 ++-- packages/cli/src/serve/server.rs | 50 ++--- packages/cli/src/serve/tracer.rs | 12 +- packages/cli/src/serve/update.rs | 2 +- packages/cli/src/serve/watcher.rs | 12 +- packages/cli/src/settings.rs | 22 ++- packages/cli/src/tools.rs | 36 ++-- 50 files changed, 585 insertions(+), 579 deletions(-) diff --git a/packages/cli/src/assets.rs b/packages/cli/src/assets.rs index 30063844ed..b3e22a4c9b 100644 --- a/packages/cli/src/assets.rs +++ b/packages/cli/src/assets.rs @@ -25,20 +25,20 @@ use crate::link::InterceptedArgs; /// /// This will be filled in primarly by incremental compilation artifacts. #[derive(Debug, PartialEq, Default, Clone)] -pub struct AssetManifest { - pub assets: HashMap, +pub(crate) struct AssetManifest { + pub(crate) assets: HashMap, } impl AssetManifest { /// Create a new asset manifest pre-populated with the assets from the linker intercept - pub fn new_from_linker_intercept(args: InterceptedArgs) -> Self { + pub(crate) fn new_from_linker_intercept(args: InterceptedArgs) -> Self { let mut manifest = Self::default(); manifest.add_from_linker_intercept(args); manifest } /// Fill this manifest from the intercepted rustc args used to link the app together - pub fn add_from_linker_intercept(&mut self, args: InterceptedArgs) { + pub(crate) fn add_from_linker_intercept(&mut self, args: InterceptedArgs) { // Attempt to load the arg as a command file, otherwise just use the args themselves // This is because windows will pass in `@linkerargs.txt` as a source of linker args if let Some(command) = args.args.iter().find(|arg| arg.starts_with('@')).cloned() { @@ -53,7 +53,7 @@ impl AssetManifest { /// Rustc will pass a file as link args to linkers on windows instead of args directly. /// /// We actually need to read that file and then pull out the args directly. - pub fn add_from_command_file(&mut self, args: InterceptedArgs, arg: &str) { + pub(crate) fn add_from_command_file(&mut self, args: InterceptedArgs, arg: &str) { let path = arg.trim().trim_start_matches('@'); let file_binary = std::fs::read(path).unwrap(); @@ -88,7 +88,7 @@ impl AssetManifest { }); } - pub fn add_from_linker_args(&mut self, args: InterceptedArgs) { + pub(crate) fn add_from_linker_args(&mut self, args: InterceptedArgs) { // Parse through linker args for `.o` or `.rlib` files. for item in args.args { if item.ends_with(".o") || item.ends_with(".rlib") { @@ -98,7 +98,7 @@ impl AssetManifest { } /// Fill this manifest with a file object/rlib files, typically extracted from the linker intercepted - pub fn add_from_object_path(&mut self, path: PathBuf) { + pub(crate) fn add_from_object_path(&mut self, path: PathBuf) { let Some(ext) = path.extension() else { return; }; @@ -126,7 +126,7 @@ impl AssetManifest { } /// Fill this manifest from an rlib / ar file that contains many object files and their entryies - pub fn add_from_archive_file(&mut self, archive: &ArchiveFile, data: &[u8]) { + pub(crate) fn add_from_archive_file(&mut self, archive: &ArchiveFile, data: &[u8]) { // Look through each archive member for object files. // Read the archive member's binary data (we know it's an object file) // And parse it with the normal `object::File::parse` to find the manganis string. @@ -144,7 +144,7 @@ impl AssetManifest { } /// Fill this manifest with whatever tables might come from the object file - pub fn add_from_object_file(&mut self, obj: &ObjectFile) -> Option<()> { + pub(crate) fn add_from_object_file(&mut self, obj: &ObjectFile) -> Option<()> { for section in obj.sections() { let Ok(section_name) = section.name() else { continue; @@ -188,7 +188,7 @@ impl AssetManifest { /// The output file is guaranteed to be the destination + the ResourceAsset bundle name /// /// Will not actually copy the asset if the source asset hasn't changed? - pub fn copy_asset_to( + pub(crate) fn copy_asset_to( &self, destination: &Path, target_asset: &Path, @@ -213,7 +213,11 @@ impl AssetManifest { } } -pub fn copy_dir_to(src_dir: PathBuf, dest_dir: PathBuf, pre_compress: bool) -> std::io::Result<()> { +pub(crate) fn copy_dir_to( + src_dir: PathBuf, + dest_dir: PathBuf, + pre_compress: bool, +) -> std::io::Result<()> { let entries = std::fs::read_dir(&src_dir)?; let mut children: Vec>> = Vec::new(); @@ -281,7 +285,7 @@ fn compressed_path(path: &Path) -> Option { } /// pre-compress a file with brotli -pub fn pre_compress_file(path: &Path) -> std::io::Result<()> { +pub(crate) fn pre_compress_file(path: &Path) -> std::io::Result<()> { let Some(compressed_path) = compressed_path(path) else { return Ok(()); }; @@ -296,7 +300,7 @@ pub fn pre_compress_file(path: &Path) -> std::io::Result<()> { } /// pre-compress all files in a folder -pub fn pre_compress_folder(path: &Path, pre_compress: bool) -> std::io::Result<()> { +pub(crate) fn pre_compress_folder(path: &Path, pre_compress: bool) -> std::io::Result<()> { let walk_dir = WalkDir::new(path); for entry in walk_dir.into_iter().filter_map(|e| e.ok()) { let entry_path = entry.path(); @@ -319,12 +323,12 @@ pub fn pre_compress_folder(path: &Path, pre_compress: bool) -> std::io::Result<( // use swc_common::{sync::Lrc, FileName}; // use swc_common::{SourceMap, GLOBALS}; -// pub trait Process { +// pub(crate) trait Process { // fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()>; // } // /// Process a specific file asset -// pub fn process_file(file: &ResourceAsset, output_folder: &Path) -> anyhow::Result<()> { +// pub(crate) fn process_file(file: &ResourceAsset, output_folder: &Path) -> anyhow::Result<()> { // todo!() // // let location = file.location(); // // let source = location.source(); @@ -483,7 +487,7 @@ pub fn pre_compress_folder(path: &Path, pre_compress: bool) -> std::io::Result<( // } // } -// pub fn minify_css(css: &str) -> String { +// pub(crate) fn minify_css(css: &str) -> String { // let mut stylesheet = StyleSheet::parse(css, ParserOptions::default()).unwrap(); // stylesheet.minify(MinifyOptions::default()).unwrap(); // let printer = PrinterOptions { @@ -494,7 +498,7 @@ pub fn pre_compress_folder(path: &Path, pre_compress: bool) -> std::io::Result<( // res.code // } -// pub fn minify_js(source: &ResourceAsset) -> anyhow::Result { +// pub(crate) fn minify_js(source: &ResourceAsset) -> anyhow::Result { // todo!("disabled swc due to semver issues") // // let cm = Arc::::default(); @@ -554,7 +558,7 @@ pub fn pre_compress_folder(path: &Path, pre_compress: bool) -> std::io::Result<( // } // } -// pub fn minify_json(source: &str) -> anyhow::Result { +// pub(crate) fn minify_json(source: &str) -> anyhow::Result { // // First try to parse the json // let json: serde_json::Value = serde_json::from_str(source)?; // // Then print it in a minified format @@ -585,7 +589,7 @@ pub fn pre_compress_folder(path: &Path, pre_compress: bool) -> std::io::Result<( // } // /// Process a folder, optimizing and copying all assets into the output folder -// pub fn process_folder(folder: &FolderAsset, output_folder: &Path) -> anyhow::Result<()> { +// pub(crate) fn process_folder(folder: &FolderAsset, output_folder: &Path) -> anyhow::Result<()> { // // Push the unique name of the folder to the output folder // let output_folder = output_folder.join(folder.unique_name()); @@ -647,12 +651,12 @@ pub fn pre_compress_folder(path: &Path, pre_compress: bool) -> std::io::Result<( // use swc_common::{sync::Lrc, FileName}; // use swc_common::{SourceMap, GLOBALS}; -// pub trait Process { +// pub(crate) trait Process { // fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()>; // } // /// Process a specific file asset -// pub fn process_file(file: &ResourceAsset, output_folder: &Path) -> anyhow::Result<()> { +// pub(crate) fn process_file(file: &ResourceAsset, output_folder: &Path) -> anyhow::Result<()> { // todo!() // // let location = file.location(); // // let source = location.source(); @@ -811,7 +815,7 @@ pub fn pre_compress_folder(path: &Path, pre_compress: bool) -> std::io::Result<( // } // } -// pub fn minify_css(css: &str) -> String { +// pub(crate) fn minify_css(css: &str) -> String { // let mut stylesheet = StyleSheet::parse(css, ParserOptions::default()).unwrap(); // stylesheet.minify(MinifyOptions::default()).unwrap(); // let printer = PrinterOptions { @@ -822,7 +826,7 @@ pub fn pre_compress_folder(path: &Path, pre_compress: bool) -> std::io::Result<( // res.code // } -// pub fn minify_js(source: &ResourceAsset) -> anyhow::Result { +// pub(crate) fn minify_js(source: &ResourceAsset) -> anyhow::Result { // todo!("disabled swc due to semver issues") // // let cm = Arc::::default(); @@ -882,7 +886,7 @@ pub fn pre_compress_folder(path: &Path, pre_compress: bool) -> std::io::Result<( // } // } -// pub fn minify_json(source: &str) -> anyhow::Result { +// pub(crate) fn minify_json(source: &str) -> anyhow::Result { // // First try to parse the json // let json: serde_json::Value = serde_json::from_str(source)?; // // Then print it in a minified format @@ -913,7 +917,7 @@ pub fn pre_compress_folder(path: &Path, pre_compress: bool) -> std::io::Result<( // } // /// Returns the HTML that should be injected into the head of the page -// pub fn head(&self) -> String { +// pub(crate) fn head(&self) -> String { // let mut head = String::new(); // for asset in &self.assets { // if let crate::AssetType::Resource(file) = asset { @@ -953,7 +957,7 @@ pub fn pre_compress_folder(path: &Path, pre_compress: bool) -> std::io::Result<( // } // /// An extension trait CLI support for the asset manifest -// pub trait AssetManifestExt { +// pub(crate) trait AssetManifestExt { // /// Load a manifest from a list of Manganis JSON strings. // /// // /// The asset descriptions are stored inside a manifest file that is produced when the linker is intercepted. @@ -1057,10 +1061,10 @@ pub fn pre_compress_folder(path: &Path, pre_compress: bool) -> std::io::Result<( // } // The temp file name for passing manganis json from linker to current exec. -// pub const MG_JSON_OUT: &str = "mg-out"; +// pub(crate) const MG_JSON_OUT: &str = "mg-out"; // /// Create a head file that contains all of the imports for assets that the user project uses -// pub fn create_assets_head(build: &BuildRequest, manifest: &AssetManifest) -> Result<()> { +// pub(crate) fn create_assets_head(build: &BuildRequest, manifest: &AssetManifest) -> Result<()> { // let out_dir = build.target_out_dir(); // std::fs::create_dir_all(&out_dir)?; // let mut file = File::create(out_dir.join("__assets_head.html"))?; @@ -1071,7 +1075,7 @@ pub fn pre_compress_folder(path: &Path, pre_compress: bool) -> std::io::Result<( // use crate::file::Process; // /// Process a folder, optimizing and copying all assets into the output folder -// pub fn process_folder(folder: &FolderAsset, output_folder: &Path) -> anyhow::Result<()> { +// pub(crate) fn process_folder(folder: &FolderAsset, output_folder: &Path) -> anyhow::Result<()> { // // Push the unique name of the folder to the output folder // let output_folder = output_folder.join(folder.unique_name()); diff --git a/packages/cli/src/builder/builder.rs b/packages/cli/src/builder/builder.rs index e9d293ce7e..e3bb3893e5 100644 --- a/packages/cli/src/builder/builder.rs +++ b/packages/cli/src/builder/builder.rs @@ -7,18 +7,18 @@ use progress::{ProgressRx, ProgressTx, UpdateBuildProgress}; use tokio::task::JoinSet; /// A handle to ongoing builds and then the spawned tasks themselves -pub struct Builder { +pub(crate) struct Builder { /// The application we are building - pub krate: DioxusCrate, + pub(crate) krate: DioxusCrate, /// Ongoing builds - pub building: JoinSet<(Platform, Result)>, + pub(crate) building: JoinSet<(Platform, Result)>, /// Messages from the build engine will be sent to this channel - pub channel: (ProgressTx, ProgressRx), + pub(crate) channel: (ProgressTx, ProgressRx), } -pub enum BuildUpdate { +pub(crate) enum BuildUpdate { Progress(UpdateBuildProgress), BuildReady { @@ -37,7 +37,7 @@ pub enum BuildUpdate { impl Builder { /// Create a new builder that can accept multiple simultaneous builds - pub fn new(krate: &DioxusCrate) -> Self { + pub(crate) fn new(krate: &DioxusCrate) -> Self { Self { channel: futures_channel::mpsc::unbounded(), krate: krate.clone(), @@ -46,14 +46,14 @@ impl Builder { } /// Create a new builder and immediately start a build - pub fn start(krate: &DioxusCrate, args: BuildArgs) -> Result { + pub(crate) fn start(krate: &DioxusCrate, args: BuildArgs) -> Result { let mut builder = Self::new(krate); builder.build(args)?; Ok(builder) } /// Start a new build - killing the current one if it exists - pub fn build(&mut self, args: BuildArgs) -> Result<()> { + pub(crate) fn build(&mut self, args: BuildArgs) -> Result<()> { self.abort_all(); let mut requests = vec![ @@ -89,7 +89,7 @@ impl Builder { } /// Wait for the build to finish - pub async fn wait_for_finish(&mut self) { + pub(crate) async fn wait_for_finish(&mut self) { loop { let next = self.wait().await; if let BuildUpdate::AllFinished = next { @@ -103,7 +103,7 @@ impl Builder { /// Also listen for any input from the app's handle /// /// Returns immediately with `Finished` if there are no more builds to run - don't poll-loop this! - pub async fn wait(&mut self) -> BuildUpdate { + pub(crate) async fn wait(&mut self) -> BuildUpdate { if self.building.is_empty() { return BuildUpdate::AllFinished; } @@ -122,7 +122,7 @@ impl Builder { /// Shutdown the current build process /// /// todo: might want to use a cancellation token here to allow cleaner shutdowns - pub fn abort_all(&mut self) { + pub(crate) fn abort_all(&mut self) { self.building.abort_all(); } diff --git a/packages/cli/src/builder/cargo.rs b/packages/cli/src/builder/cargo.rs index c808aa89ff..def032d965 100644 --- a/packages/cli/src/builder/cargo.rs +++ b/packages/cli/src/builder/cargo.rs @@ -7,7 +7,7 @@ use std::process::Stdio; use tokio::process::Command; impl BuildRequest { - pub async fn build(self) -> Result { + pub(crate) async fn build(self) -> Result { tracing::info!("🚅 Running build command..."); // Install any tooling that might be required for this build. @@ -23,7 +23,7 @@ impl BuildRequest { AppBundle::new(self, assets, executable).await } - pub async fn verify_tooling(&self) -> Result<()> { + pub(crate) async fn verify_tooling(&self) -> Result<()> { match self.platform() { // If this is a web, build make sure we have the web build tooling set up Platform::Web => self.install_web_build_tooling().await?, @@ -48,7 +48,7 @@ impl BuildRequest { /// This will execute `dx` with an env var set to force `dx` to operate as a linker, and then /// traverse the .o and .rlib files rustc passes that new `dx` instance, collecting the link /// tables marked by manganis and parsing them as a ResourceAsset. - pub async fn collect_assets(&self) -> anyhow::Result { + pub(crate) async fn collect_assets(&self) -> anyhow::Result { // If this is the server build, the client build already copied any assets we need if self.platform() == Platform::Server { return Ok(AssetManifest::default()); @@ -97,7 +97,7 @@ impl BuildRequest { } /// Create a list of arguments for cargo builds - pub fn build_arguments(&self) -> Vec { + pub(crate) fn build_arguments(&self) -> Vec { let mut cargo_args = Vec::new(); if self.build.release { diff --git a/packages/cli/src/builder/mod.rs b/packages/cli/src/builder/mod.rs index f4be6631f7..1899ec14e8 100644 --- a/packages/cli/src/builder/mod.rs +++ b/packages/cli/src/builder/mod.rs @@ -14,8 +14,8 @@ mod result; mod tooling; mod web; -pub use builder::*; -pub use platform::*; -pub use progress::*; -pub use request::*; -pub use result::*; +pub(crate) use builder::*; +pub(crate) use platform::*; +pub(crate) use progress::*; +pub(crate) use request::*; +pub(crate) use result::*; diff --git a/packages/cli/src/builder/platform.rs b/packages/cli/src/builder/platform.rs index f3a60fab14..36d48a7ab3 100644 --- a/packages/cli/src/builder/platform.rs +++ b/packages/cli/src/builder/platform.rs @@ -17,7 +17,7 @@ use std::str::FromStr; clap::ValueEnum, )] #[non_exhaustive] -pub enum Platform { +pub(crate) enum Platform { /// Targeting the web platform using WASM #[clap(name = "web")] #[serde(rename = "web")] @@ -59,7 +59,7 @@ pub enum Platform { } /// An error that occurs when a platform is not recognized -pub struct UnknownPlatformError; +pub(crate) struct UnknownPlatformError; impl std::fmt::Display for UnknownPlatformError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -89,7 +89,7 @@ impl Display for Platform { impl Platform { /// All platforms the dioxus CLI supports - pub const ALL: &'static [Self] = &[ + pub(crate) const ALL: &'static [Self] = &[ Platform::Web, Platform::Desktop, Platform::Ios, @@ -99,7 +99,7 @@ impl Platform { ]; /// Get the feature name for the platform in the dioxus crate - pub fn feature_name(&self) -> &str { + pub(crate) fn feature_name(&self) -> &str { match self { Platform::Web => "web", Platform::Desktop => "desktop", diff --git a/packages/cli/src/builder/profiles.rs b/packages/cli/src/builder/profiles.rs index 23a6e40ab1..480e4ad03f 100644 --- a/packages/cli/src/builder/profiles.rs +++ b/packages/cli/src/builder/profiles.rs @@ -2,8 +2,8 @@ use crate::dioxus_crate::DioxusCrate; use std::io::Write; use toml_edit::Item; -pub static CLIENT_PROFILE: &str = "dioxus-client"; -pub static SERVER_PROFILE: &str = "dioxus-server"; +pub(crate) static CLIENT_PROFILE: &str = "dioxus-client"; +pub(crate) static SERVER_PROFILE: &str = "dioxus-server"; // The `opt-level=2` increases build times, but can noticeably decrease time // between saving changes and being able to interact with an app. The "overall" @@ -11,7 +11,7 @@ pub static SERVER_PROFILE: &str = "dioxus-server"; // almost imperceptible (~1 s) but also can be very noticeable (~6 s) — depends // on setup (hardware, OS, browser, idle load). // Find or create the client and server profiles in the .cargo/config.toml file -pub fn initialize_profiles(krate: &DioxusCrate) -> crate::Result<()> { +pub(crate) fn initialize_profiles(krate: &DioxusCrate) -> crate::Result<()> { let config_path = krate.workspace_dir().join(".cargo/config.toml"); let mut config = match std::fs::read_to_string(&config_path) { Ok(config) => config.parse::().map_err(|e| { diff --git a/packages/cli/src/builder/progress.rs b/packages/cli/src/builder/progress.rs index 48a20c93ed..e417667cd7 100644 --- a/packages/cli/src/builder/progress.rs +++ b/packages/cli/src/builder/progress.rs @@ -15,7 +15,7 @@ impl BuildRequest { /// Run `cargo`, returning the location of the final exectuable /// /// todo: add some stats here, like timing reports, crate-graph optimizations, etc - pub async fn build_cargo(&self) -> anyhow::Result { + pub(crate) async fn build_cargo(&self) -> anyhow::Result { // Extract the unit count of the crate graph so build_cargo has more accurate data let crate_count = self.get_unit_count_estimate().await; @@ -176,7 +176,7 @@ impl BuildRequest { /// Get an estimate of the number of units in the crate. If nightly rustc is not available, this will return an estimate of the number of units in the crate based on cargo metadata. /// TODO: always use https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#unit-graph once it is stable - pub async fn get_unit_count_estimate(&self) -> usize { + pub(crate) async fn get_unit_count_estimate(&self) -> usize { // Try to get it from nightly self.get_unit_count().await.unwrap_or_else(|| { // Otherwise, use cargo metadata @@ -191,7 +191,7 @@ impl BuildRequest { }) } - pub fn status_build_finished(&self) { + pub(crate) fn status_build_finished(&self) { tracing::info!("🚩 Build completed: [{}]", self.krate.out_dir().display()); _ = self.progress.unbounded_send(UpdateBuildProgress { @@ -201,7 +201,7 @@ impl BuildRequest { }); } - pub fn status_copying_asset(&self, cur: usize, total: usize, asset: &Path) { + pub(crate) fn status_copying_asset(&self, cur: usize, total: usize, asset: &Path) { // Update the progress // _ = self.progress.unbounded_send(UpdateBuildProgress { // stage: Stage::OptimizingAssets, @@ -217,7 +217,7 @@ impl BuildRequest { // }); } - pub fn status_finished_asset(&self, idx: usize, total: usize, asset: &Path) { + pub(crate) fn status_finished_asset(&self, idx: usize, total: usize, asset: &Path) { // Update the progress // _ = self.progress.unbounded_send(UpdateBuildProgress { // stage: Stage::OptimizingAssets, @@ -227,11 +227,11 @@ impl BuildRequest { } } -pub type ProgressTx = UnboundedSender; -pub type ProgressRx = UnboundedReceiver; +pub(crate) type ProgressTx = UnboundedSender; +pub(crate) type ProgressRx = UnboundedReceiver; #[derive(Default, Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Copy)] -pub enum Stage { +pub(crate) enum Stage { #[default] Initializing = 0, InstallingWasmTooling = 1, @@ -263,14 +263,14 @@ impl std::fmt::Display for Stage { } #[derive(Debug, Clone)] -pub struct UpdateBuildProgress { - pub stage: Stage, - pub update: UpdateStage, - pub platform: Platform, +pub(crate) struct UpdateBuildProgress { + pub(crate) stage: Stage, + pub(crate) update: UpdateStage, + pub(crate) platform: Platform, } impl UpdateBuildProgress { - pub fn to_std_out(&self) { + pub(crate) fn to_std_out(&self) { match &self.update { UpdateStage::Start => println!("--- {} ---", self.stage), UpdateStage::AddMessage(message) => match &message.message { @@ -292,7 +292,7 @@ impl UpdateBuildProgress { } #[derive(Debug, Clone, PartialEq)] -pub enum UpdateStage { +pub(crate) enum UpdateStage { Start, AddMessage(BuildMessage), SetProgress(f64), @@ -300,14 +300,14 @@ pub enum UpdateStage { } #[derive(Debug, Clone, PartialEq)] -pub struct BuildMessage { - pub level: Level, - pub message: MessageType, - pub source: MessageSource, +pub(crate) struct BuildMessage { + pub(crate) level: Level, + pub(crate) message: MessageType, + pub(crate) source: MessageSource, } #[derive(Debug, Clone, PartialEq)] -pub enum MessageType { +pub(crate) enum MessageType { Cargo(Diagnostic), Text(String), } @@ -317,7 +317,7 @@ pub enum MessageType { /// The CLI will render a prefix according to the message type /// but this prefix, [`MessageSource::to_string()`] shouldn't be used if a strict message source is required. #[derive(Debug, Clone, PartialEq)] -pub enum MessageSource { +pub(crate) enum MessageSource { /// Represents any message from the running application. Renders `[app]` App, diff --git a/packages/cli/src/builder/request.rs b/packages/cli/src/builder/request.rs index cca13d53be..639bde8e98 100644 --- a/packages/cli/src/builder/request.rs +++ b/packages/cli/src/builder/request.rs @@ -10,25 +10,25 @@ use std::path::PathBuf; /// As the build progresses, we'll fill in fields like assets, executable, entitlements, etc /// /// If the app needs to be bundled, we'll add the bundle info here too -pub struct BuildRequest { +pub(crate) struct BuildRequest { /// The configuration for the crate we are building - pub krate: DioxusCrate, + pub(crate) krate: DioxusCrate, /// The arguments for the build - pub build: BuildArgs, + pub(crate) build: BuildArgs, /// The rustc flags to pass to the build - pub rust_flags: Vec, + pub(crate) rust_flags: Vec, /// The target directory for the build - pub custom_target_dir: Option, + pub(crate) custom_target_dir: Option, /// Status channel to send our progress updates to - pub progress: ProgressTx, + pub(crate) progress: ProgressTx, } impl BuildRequest { - pub fn new(krate: DioxusCrate, build: BuildArgs, progress: ProgressTx) -> Self { + pub(crate) fn new(krate: DioxusCrate, build: BuildArgs, progress: ProgressTx) -> Self { Self { progress, build, @@ -62,7 +62,7 @@ impl BuildRequest { } } - pub fn new_server(krate: &DioxusCrate, mut build: BuildArgs, progress: ProgressTx) -> Self { + pub(crate) fn new_server(krate: &DioxusCrate, mut build: BuildArgs, progress: ProgressTx) -> Self { if build.profile.is_none() { build.profile = Some(CLIENT_PROFILE.to_string()); } @@ -75,7 +75,7 @@ impl BuildRequest { ) } - pub fn new_client(krate: &DioxusCrate, mut build: BuildArgs, progress: ProgressTx) -> Self { + pub(crate) fn new_client(krate: &DioxusCrate, mut build: BuildArgs, progress: ProgressTx) -> Self { if build.profile.is_none() { build.profile = Some(SERVER_PROFILE.to_string()); } @@ -89,7 +89,7 @@ impl BuildRequest { } /// Get the platform for this build - pub fn platform(&self) -> Platform { + pub(crate) fn platform(&self) -> Platform { self.build .platform .unwrap_or_else(|| self.krate.dioxus_config.application.default_platform) @@ -102,7 +102,7 @@ impl BuildRequest { /// {app_name}-{platform}-{arch} /// /// Does not include the extension - pub fn app_name(&self) -> String { + pub(crate) fn app_name(&self) -> String { match self.platform() { Platform::Web => "web".to_string(), Platform::Desktop => todo!(), diff --git a/packages/cli/src/builder/result.rs b/packages/cli/src/builder/result.rs index 42e4c0a758..8ecc46ec48 100644 --- a/packages/cli/src/builder/result.rs +++ b/packages/cli/src/builder/result.rs @@ -1 +1 @@ -pub use crate::bundler::AppBundle; +pub(crate) use crate::bundler::AppBundle; diff --git a/packages/cli/src/builder/web.rs b/packages/cli/src/builder/web.rs index 5ab8684183..2b2d362f85 100644 --- a/packages/cli/src/builder/web.rs +++ b/packages/cli/src/builder/web.rs @@ -14,7 +14,7 @@ const DEFAULT_HTML: &str = include_str!("../../assets/index.html"); const TOAST_HTML: &str = include_str!("../../assets/toast.html"); impl BuildRequest { - pub async fn run_wasm_bindgen(&self, input_path: &Path, bindgen_outdir: &Path) -> Result<()> { + pub(crate) async fn run_wasm_bindgen(&self, input_path: &Path, bindgen_outdir: &Path) -> Result<()> { tracing::info!("Running wasm-bindgen"); let input_path = input_path.to_path_buf(); @@ -47,7 +47,7 @@ impl BuildRequest { } #[allow(unused)] - pub fn run_wasm_opt(&self, bindgen_outdir: &std::path::PathBuf) -> Result<(), Error> { + pub(crate) fn run_wasm_opt(&self, bindgen_outdir: &std::path::PathBuf) -> Result<(), Error> { if !self.build.release { return Ok(()); }; @@ -94,7 +94,7 @@ impl BuildRequest { } /// Check if the wasm32-unknown-unknown target is installed and try to install it if not - pub async fn install_web_build_tooling(&self) -> Result<()> { + pub(crate) async fn install_web_build_tooling(&self) -> Result<()> { // If the user has rustup, we can check if the wasm32-unknown-unknown target is installed // Otherwise we can just assume it is installed - which is not great... // Eventually we can poke at the errors and let the user know they need to install the target @@ -118,7 +118,7 @@ impl BuildRequest { } // Attempt to automatically recover from a bindgen failure by updating the wasm-bindgen version - pub async fn update_wasm_bindgen_version() -> Result<()> { + pub(crate) async fn update_wasm_bindgen_version() -> Result<()> { let cli_bindgen_version = wasm_bindgen_shared::version(); tracing::info!("Attempting to recover from bindgen failure by setting the wasm-bindgen version to {cli_bindgen_version}..."); @@ -150,7 +150,7 @@ impl BuildRequest { Err(Error::BuildFailed(format!("WASM bindgen build failed!\nThis is probably due to the Bindgen version, dioxus-cli is using `{cli_bindgen_version}` which is not compatible with your crate.\nPlease reinstall the dioxus cli to fix this issue.\nYou can reinstall the dioxus cli by running `cargo install dioxus-cli --force` and then rebuild your project"))) } - pub fn prepare_html(&self) -> Result { + pub(crate) fn prepare_html(&self) -> Result { let mut html = { let crate_root: &Path = &self.krate.crate_dir(); let custom_html_file = crate_root.join("index.html"); @@ -329,7 +329,7 @@ impl BuildRequest { } /// Check if the build is targeting the web platform - pub fn targeting_web(&self) -> bool { + pub(crate) fn targeting_web(&self) -> bool { self.platform() == Platform::Web } } diff --git a/packages/cli/src/bundle_utils.rs b/packages/cli/src/bundle_utils.rs index 34016098cf..e9619db664 100644 --- a/packages/cli/src/bundle_utils.rs +++ b/packages/cli/src/bundle_utils.rs @@ -2,7 +2,7 @@ use crate::config::BundleConfig; -pub fn make_tauri_bundler_settings(bundle_config: BundleConfig) -> tauri_bundler::BundleSettings { +pub(crate) fn make_tauri_bundler_settings(bundle_config: BundleConfig) -> tauri_bundler::BundleSettings { todo!() } diff --git a/packages/cli/src/bundler.rs b/packages/cli/src/bundler.rs index 3b7f695256..4c63fc289a 100644 --- a/packages/cli/src/bundler.rs +++ b/packages/cli/src/bundler.rs @@ -5,12 +5,12 @@ mod web; mod win; mod app; -pub use app::*; +pub(crate) use app::*; use serde::{Deserialize, Serialize}; #[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Debug)] -pub enum BundleFormat { +pub(crate) enum BundleFormat { // Apple Macos, Ios, diff --git a/packages/cli/src/bundler/app.rs b/packages/cli/src/bundler/app.rs index 2c9ea4e1e0..ca0249c23e 100644 --- a/packages/cli/src/bundler/app.rs +++ b/packages/cli/src/bundler/app.rs @@ -5,15 +5,15 @@ use rayon::prelude::{IntoParallelRefIterator, ParallelIterator}; use std::path::PathBuf; use std::sync::atomic::AtomicUsize; -pub struct AppBundle { - pub build: BuildRequest, - pub workdir: PathBuf, - pub executable: PathBuf, - pub assets: AssetManifest, +pub(crate) struct AppBundle { + pub(crate) build: BuildRequest, + pub(crate) workdir: PathBuf, + pub(crate) executable: PathBuf, + pub(crate) assets: AssetManifest, } impl AppBundle { - pub async fn new( + pub(crate) async fn new( build: BuildRequest, assets: AssetManifest, executable: PathBuf, @@ -38,7 +38,7 @@ impl AppBundle { /// /// Perform any finishing steps here: /// - Signing the bundle - pub async fn finish(&self, destination: PathBuf) -> Result { + pub(crate) async fn finish(&self, destination: PathBuf) -> Result { match self.build.platform() { // Nothing special to do - just copy the workdir to the output location Platform::Web => { @@ -163,7 +163,7 @@ impl AppBundle { Ok(()) } - pub fn all_source_assets(&self) -> Vec { + pub(crate) fn all_source_assets(&self) -> Vec { // Merge the legacy asset dir assets with the assets from the manifest // Legacy assets need to retain their name in case they're referenced in the manifest // todo: we should only copy over assets that appear in `img { src: "assets/logo.png" }` to @@ -180,7 +180,7 @@ impl AppBundle { Ok(()) } - pub fn asset_dir(&self) -> PathBuf { + pub(crate) fn asset_dir(&self) -> PathBuf { let dir: PathBuf = match self.build.platform() { Platform::Web => self.workdir.join("assets"), Platform::Desktop => self.workdir.join("Resources"), @@ -198,7 +198,7 @@ impl AppBundle { } /// Run the optimizers, obfuscators, minimizers, etc - pub async fn optimize(&self) -> Result<()> { + pub(crate) async fn optimize(&self) -> Result<()> { match self.build.platform() { Platform::Web => { // Compress the asset dir diff --git a/packages/cli/src/bundler/web.rs b/packages/cli/src/bundler/web.rs index 09059ead95..0012867163 100644 --- a/packages/cli/src/bundler/web.rs +++ b/packages/cli/src/bundler/web.rs @@ -2,7 +2,7 @@ use super::AppBundle; use crate::Result; impl AppBundle { - pub fn prepare_html(&self) -> Result { + pub(crate) fn prepare_html(&self) -> Result { todo!() } } diff --git a/packages/cli/src/cli/autoformat.rs b/packages/cli/src/cli/autoformat.rs index 3d2f5c6827..05549c454a 100644 --- a/packages/cli/src/cli/autoformat.rs +++ b/packages/cli/src/cli/autoformat.rs @@ -10,35 +10,35 @@ use std::{borrow::Cow, fs, path::Path, process::exit}; /// Format some rsx #[derive(Clone, Debug, Parser)] -pub struct Autoformat { +pub(crate) struct Autoformat { /// Format rust code before the formatting the rsx macros #[clap(long)] - pub all_code: bool, + pub(crate) all_code: bool, /// Run in 'check' mode. Exits with 0 if input is formatted correctly. Exits /// with 1 and prints a diff if formatting is required. #[clap(short, long)] - pub check: bool, + pub(crate) check: bool, /// Input rsx (selection) #[clap(short, long)] - pub raw: Option, + pub(crate) raw: Option, /// Input file #[clap(short, long)] - pub file: Option, + pub(crate) file: Option, /// Split attributes in lines or not #[clap(short, long, default_value = "false")] - pub split_line_attributes: bool, + pub(crate) split_line_attributes: bool, /// The package to build #[clap(short, long)] - pub package: Option, + pub(crate) package: Option, } impl Autoformat { - pub fn autoformat(self) -> Result<()> { + pub(crate) fn autoformat(self) -> Result<()> { let Autoformat { check, raw, diff --git a/packages/cli/src/cli/build.rs b/packages/cli/src/cli/build.rs index fa581c6be6..6fab5fe07b 100644 --- a/packages/cli/src/cli/build.rs +++ b/packages/cli/src/cli/build.rs @@ -9,107 +9,107 @@ use std::str::FromStr; /// Produces a final output bundle designed to be run on the target platform. #[derive(Clone, Debug, Default, Deserialize, Parser)] #[clap(name = "build")] -pub struct BuildArgs { +pub(crate) struct BuildArgs { /// Build in release mode [default: false] #[clap(long, short)] #[serde(default)] - pub release: bool, + pub(crate) release: bool, /// This flag only applies to fullstack builds. By default fullstack builds will run with something in between debug and release mode. This flag will force the build to run in debug mode. [default: false] #[clap(long)] #[serde(default)] - pub force_debug: bool, + pub(crate) force_debug: bool, /// This flag only applies to fullstack builds. By default fullstack builds will run the server and client builds in parallel. This flag will force the build to run the server build first, then the client build. [default: false] #[clap(long)] #[serde(default)] - pub force_sequential: bool, + pub(crate) force_sequential: bool, // Use verbose output [default: false] #[clap(long)] #[serde(default)] - pub verbose: bool, + pub(crate) verbose: bool, /// Build with custom profile #[clap(long)] - pub profile: Option, + pub(crate) profile: Option, /// Build platform: support Web & Desktop [default: "default_platform"] #[clap(long, value_enum)] - pub platform: Option, + pub(crate) platform: Option, /// Build the fullstack variant of this app, using that as the fileserver and backend /// /// This defaults to `false` but will be overriden to true if the `fullstack` feature is enabled. #[clap(long)] - pub fullstack: bool, + pub(crate) fullstack: bool, /// Run the ssg config of the app and generate the files #[clap(long)] - pub ssg: bool, + pub(crate) ssg: bool, /// Skip collecting assets from dependencies [default: false] #[clap(long)] #[serde(default)] - pub skip_assets: bool, + pub(crate) skip_assets: bool, /// Extra arguments passed to cargo build #[clap(last = true)] - pub cargo_args: Vec, + pub(crate) cargo_args: Vec, /// Inject scripts to load the wasm and js files for your dioxus app if they are not already present [default: true] #[clap(long, default_value_t = true)] - pub inject_loading_scripts: bool, + pub(crate) inject_loading_scripts: bool, /// Information about the target to build #[clap(flatten)] - pub target_args: TargetArgs, + pub(crate) target_args: TargetArgs, } /// Information about the target to build #[derive(Clone, Debug, Default, Deserialize, Parser)] -pub struct TargetArgs { +pub(crate) struct TargetArgs { /// Build for nightly [default: false] #[clap(long)] - pub nightly: bool, + pub(crate) nightly: bool, /// Build a example [default: ""] #[clap(long)] - pub example: Option, + pub(crate) example: Option, /// Build a binary [default: ""] #[clap(long)] - pub bin: Option, + pub(crate) bin: Option, /// The package to build #[clap(short, long)] - pub package: Option, + pub(crate) package: Option, /// Space separated list of features to activate #[clap(long)] - pub features: Vec, + pub(crate) features: Vec, /// The feature to use for the client in a fullstack app [default: "web"] #[clap(long)] - pub client_feature: Option, + pub(crate) client_feature: Option, /// The feature to use for the server in a fullstack app [default: "server"] #[clap(long)] - pub server_feature: Option, + pub(crate) server_feature: Option, /// The architecture to build for [default: "native"] /// /// Can either be `arm | arm64 | x86 | x86_64 | native` #[clap(long)] - pub arch: Option, + pub(crate) arch: Option, /// Rustc platform triple #[clap(long)] - pub target: Option, + pub(crate) target: Option, } impl BuildArgs { - pub async fn run(mut self) -> anyhow::Result<()> { + pub(crate) async fn run(mut self) -> anyhow::Result<()> { let mut dioxus_crate = DioxusCrate::new(&self.target_args).context("Failed to load Dioxus workspace")?; @@ -118,7 +118,7 @@ impl BuildArgs { Ok(()) } - pub async fn build(&mut self, dioxus_crate: &mut DioxusCrate) -> Result<()> { + pub(crate) async fn build(&mut self, dioxus_crate: &mut DioxusCrate) -> Result<()> { self.resolve(dioxus_crate)?; // todo: probably want to consume the logs from the builder here, instead of just waiting for it to finish @@ -134,7 +134,7 @@ impl BuildArgs { /// /// IE if they've specified "fullstack" as a feature on `dioxus`, then we want to build the /// fullstack variant even if they omitted the `--fullstack` flag. - pub fn resolve(&mut self, dioxus_crate: &mut DioxusCrate) -> Result<()> { + pub(crate) fn resolve(&mut self, dioxus_crate: &mut DioxusCrate) -> Result<()> { // Inherit the platform from the defaults let platform = self .platform @@ -150,7 +150,7 @@ impl BuildArgs { Ok(()) } - pub fn auto_detect_client_platform( + pub(crate) fn auto_detect_client_platform( &self, resolved: &DioxusCrate, ) -> (Option, Platform) { @@ -160,7 +160,7 @@ impl BuildArgs { .unwrap_or_else(|| (Some("web".to_string()), Platform::Web)) } - pub fn auto_detect_server_feature(&self, resolved: &DioxusCrate) -> Option { + pub(crate) fn auto_detect_server_feature(&self, resolved: &DioxusCrate) -> Option { self.find_dioxus_feature(resolved, |platform| matches!(platform, Platform::Server)) .map(|(feature, _)| feature) .unwrap_or_else(|| Some("server".to_string())) @@ -227,7 +227,7 @@ impl BuildArgs { } /// Get the platform from the build arguments - pub fn platform(&self) -> Platform { + pub(crate) fn platform(&self) -> Platform { self.platform.unwrap_or_default() } } diff --git a/packages/cli/src/cli/bundle.rs b/packages/cli/src/cli/bundle.rs index 6b7d3b7d0d..e9862a6a96 100644 --- a/packages/cli/src/cli/bundle.rs +++ b/packages/cli/src/cli/bundle.rs @@ -11,17 +11,17 @@ use super::*; /// Bundle the Rust desktop app and all of its assets #[derive(Clone, Debug, Parser)] #[clap(name = "bundle")] -pub struct Bundle { +pub(crate) struct Bundle { #[clap(long)] - pub package: Option>, + pub(crate) package: Option>, /// The arguments for the dioxus build #[clap(flatten)] - pub build_arguments: BuildArgs, + pub(crate) build_arguments: BuildArgs, } #[derive(Clone, Debug)] -pub enum PackageType { +pub(crate) enum PackageType { MacOsBundle, IosBundle, WindowsMsi, @@ -65,7 +65,7 @@ impl From for tauri_bundler::PackageType { } impl Bundle { - pub async fn bundle(mut self) -> anyhow::Result<()> { + pub(crate) async fn bundle(mut self) -> anyhow::Result<()> { let mut dioxus_crate = DioxusCrate::new(&self.build_arguments.target_args) .context("Failed to load Dioxus workspace")?; diff --git a/packages/cli/src/cli/check.rs b/packages/cli/src/cli/check.rs index 84374184df..8901abe956 100644 --- a/packages/cli/src/cli/check.rs +++ b/packages/cli/src/cli/check.rs @@ -11,19 +11,19 @@ use super::*; /// Check the Rust files in the project for issues. #[derive(Clone, Debug, Parser)] -pub struct Check { +pub(crate) struct Check { /// Input file #[clap(short, long)] - pub file: Option, + pub(crate) file: Option, /// Information about the target to check #[clap(flatten)] - pub target_args: TargetArgs, + pub(crate) target_args: TargetArgs, } impl Check { // Todo: check the entire crate - pub async fn check(self) -> Result<()> { + pub(crate) async fn check(self) -> Result<()> { match self.file { // Default to checking the project None => { diff --git a/packages/cli/src/cli/clean.rs b/packages/cli/src/cli/clean.rs index 2ea627b6c7..25ebfb6d68 100644 --- a/packages/cli/src/cli/clean.rs +++ b/packages/cli/src/cli/clean.rs @@ -7,10 +7,10 @@ use super::*; /// Clean build artifacts. #[derive(Clone, Debug, Parser)] #[clap(name = "clean")] -pub struct Clean {} +pub(crate) struct Clean {} impl Clean { - pub fn clean(self) -> anyhow::Result<()> { + pub(crate) fn clean(self) -> anyhow::Result<()> { let dioxus_crate = DioxusCrate::new(&TargetArgs::default()).context("Failed to load Dioxus workspace")?; diff --git a/packages/cli/src/cli/config.rs b/packages/cli/src/cli/config.rs index 8bf56a528a..b105d1880b 100644 --- a/packages/cli/src/cli/config.rs +++ b/packages/cli/src/cli/config.rs @@ -6,7 +6,7 @@ use super::*; /// Dioxus config file controls #[derive(Clone, Debug, Deserialize, Subcommand)] #[clap(name = "config")] -pub enum Config { +pub(crate) enum Config { /// Init `Dioxus.toml` for project/folder. Init { /// Init project name @@ -32,7 +32,7 @@ pub enum Config { } #[derive(Debug, Clone, Copy, Deserialize, Subcommand)] -pub enum Setting { +pub(crate) enum Setting { /// Set the value of the always-hot-reload setting. AlwaysHotReload { value: BoolValue }, /// Set the value of the always-open-browser setting. @@ -57,7 +57,7 @@ impl Display for Setting { // Clap complains if we use a bool directly and I can't find much info about it. // "Argument 'value` is positional and it must take a value but action is SetTrue" #[derive(Debug, Clone, Copy, Deserialize, clap::ValueEnum)] -pub enum BoolValue { +pub(crate) enum BoolValue { True, False, } @@ -72,7 +72,7 @@ impl From for bool { } impl Config { - pub fn config(self) -> Result<()> { + pub(crate) fn config(self) -> Result<()> { let crate_root = crate_root()?; match self { Config::Init { diff --git a/packages/cli/src/cli/create.rs b/packages/cli/src/cli/create.rs index 57bcfcad36..7e47323d10 100644 --- a/packages/cli/src/cli/create.rs +++ b/packages/cli/src/cli/create.rs @@ -3,11 +3,11 @@ use cargo_generate::{GenerateArgs, TemplatePath}; use cargo_metadata::Metadata; use std::path::Path; -pub static DEFAULT_TEMPLATE: &str = "gh:dioxuslabs/dioxus-template"; +pub(crate) static DEFAULT_TEMPLATE: &str = "gh:dioxuslabs/dioxus-template"; #[derive(Clone, Debug, Default, Deserialize, Parser)] #[clap(name = "new")] -pub struct Create { +pub(crate) struct Create { /// Project name (required when `--yes` is used) name: Option, @@ -38,7 +38,7 @@ pub struct Create { } impl Create { - pub fn create(mut self) -> Result<()> { + pub(crate) fn create(mut self) -> Result<()> { let metadata = cargo_metadata::MetadataCommand::new().exec().ok(); // If we're getting pass a `.` name, that's actually a path @@ -111,7 +111,7 @@ impl Create { /// Post-creation actions for newly setup crates. // Also used by `init`. -pub fn post_create(path: &Path, metadata: Option) -> Result<()> { +pub(crate) fn post_create(path: &Path, metadata: Option) -> Result<()> { // 1. Add the new project to the workspace, if it exists. // This must be executed first in order to run `cargo fmt` on the new project. metadata.and_then(|metadata| { diff --git a/packages/cli/src/cli/httpserver.rs b/packages/cli/src/cli/httpserver.rs index de54f1db00..a488e851f1 100644 --- a/packages/cli/src/cli/httpserver.rs +++ b/packages/cli/src/cli/httpserver.rs @@ -3,10 +3,10 @@ use super::*; /// Translate some source file into Dioxus code #[derive(Clone, Debug, Parser)] #[clap(name = "http-server")] -pub struct Httpserver {} +pub(crate) struct Httpserver {} impl Httpserver { - pub async fn serve(self) -> Result<()> { + pub(crate) async fn serve(self) -> Result<()> { todo!() } } diff --git a/packages/cli/src/cli/init.rs b/packages/cli/src/cli/init.rs index 01e49b69c7..a9231cfa9b 100644 --- a/packages/cli/src/cli/init.rs +++ b/packages/cli/src/cli/init.rs @@ -4,7 +4,7 @@ use cargo_generate::{GenerateArgs, TemplatePath}; #[derive(Clone, Debug, Default, Deserialize, Parser)] #[clap(name = "init")] -pub struct Init { +pub(crate) struct Init { /// Template path #[clap(default_value = DEFAULT_TEMPLATE, short, long)] template: String, @@ -24,7 +24,7 @@ pub struct Init { } impl Init { - pub fn init(self) -> Result<()> { + pub(crate) fn init(self) -> Result<()> { let metadata = cargo_metadata::MetadataCommand::new().exec().ok(); // Get directory name. diff --git a/packages/cli/src/cli/link.rs b/packages/cli/src/cli/link.rs index 0de54a3379..53501d9b96 100644 --- a/packages/cli/src/cli/link.rs +++ b/packages/cli/src/cli/link.rs @@ -3,19 +3,19 @@ use std::{env::current_dir, path::PathBuf}; use serde::{Deserialize, Serialize}; /// The env var that will be set by the linker intercept cmd to indicate that we should act as a linker -pub const LINK_OUTPUT_ENV_VAR: &str = "dx-magic-link-file"; +pub(crate) const LINK_OUTPUT_ENV_VAR: &str = "dx-magic-link-file"; /// Should we act as a linker? /// /// Just check if the magic env var is set -pub fn should_link() -> bool { +pub(crate) fn should_link() -> bool { std::env::var(LINK_OUTPUT_ENV_VAR).is_ok() } #[derive(Serialize, Deserialize)] -pub struct InterceptedArgs { - pub work_dir: PathBuf, - pub args: Vec, +pub(crate) struct InterceptedArgs { + pub(crate) work_dir: PathBuf, + pub(crate) args: Vec, } /// Write the incoming linker args to a file @@ -27,7 +27,7 @@ pub struct InterceptedArgs { /// /// hmmmmmmmm tbh I'd rather just pass the object files back and do the parsing here, but the interface /// is nicer to just bounce back the args and let the host do the parsing/canonicalization -pub fn dump_link_args() -> anyhow::Result<()> { +pub(crate) fn dump_link_args() -> anyhow::Result<()> { let output = std::env::var(LINK_OUTPUT_ENV_VAR).expect("Missing env var with target file"); // get the args and then dump them to the file diff --git a/packages/cli/src/cli/mod.rs b/packages/cli/src/cli/mod.rs index f9215608a2..6196349f56 100644 --- a/packages/cli/src/cli/mod.rs +++ b/packages/cli/src/cli/mod.rs @@ -1,15 +1,15 @@ -pub mod autoformat; -pub mod build; -pub mod bundle; -pub mod check; -pub mod clean; -pub mod config; -pub mod create; -pub mod httpserver; -pub mod init; -pub mod link; -pub mod serve; -pub mod translate; +pub(crate) mod autoformat; +pub(crate) mod build; +pub(crate) mod bundle; +pub(crate) mod check; +pub(crate) mod clean; +pub(crate) mod config; +pub(crate) mod create; +pub(crate) mod httpserver; +pub(crate) mod init; +pub(crate) mod link; +pub(crate) mod serve; +pub(crate) mod translate; use crate::{custom_error, error::Result, Error}; use clap::{Parser, Subcommand}; @@ -24,7 +24,7 @@ use std::{ process::{Command, Stdio}, }; -pub static VERSION: Lazy = Lazy::new(|| { +pub(crate) static VERSION: Lazy = Lazy::new(|| { format!( "{} ({})", crate::build_info::PKG_VERSION, @@ -35,21 +35,21 @@ pub static VERSION: Lazy = Lazy::new(|| { /// Build, Bundle & Ship Dioxus Apps. #[derive(Parser)] #[clap(name = "dioxus", version = VERSION.as_str())] -pub struct Cli { +pub(crate) struct Cli { #[clap(subcommand)] - pub action: Commands, + pub(crate) action: Commands, /// Enable verbose logging. #[clap(short)] - pub v: bool, + pub(crate) v: bool, /// Specify a binary target. #[clap(global = true, long)] - pub bin: Option, + pub(crate) bin: Option, } #[derive(Parser)] -pub enum Commands { +pub(crate) enum Commands { /// Build the Dioxus project and all of its assets. Build(build::BuildArgs), diff --git a/packages/cli/src/cli/serve.rs b/packages/cli/src/cli/serve.rs index bbab882571..be8c6e2222 100644 --- a/packages/cli/src/cli/serve.rs +++ b/packages/cli/src/cli/serve.rs @@ -10,48 +10,48 @@ use crossterm::tty::IsTty; #[derive(Clone, Debug, Default, Parser)] #[command(group = clap::ArgGroup::new("release-incompatible").multiple(true).conflicts_with("release"))] #[clap(name = "serve")] -pub struct ServeArgs { +pub(crate) struct ServeArgs { /// The arguments for the address the server will run on #[clap(flatten)] - pub address: AddressArguments, + pub(crate) address: AddressArguments, /// Open the app in the default browser [default: true - unless cli settings are set] #[arg(long, default_missing_value="true", num_args=0..=1)] - pub open: Option, + pub(crate) open: Option, /// Enable full hot reloading for the app [default: true - unless cli settings are set] #[clap(long, group = "release-incompatible")] - pub hot_reload: Option, + pub(crate) hot_reload: Option, /// Configure always-on-top for desktop apps [default: true - unless cli settings are set] #[clap(long, default_missing_value = "true")] - pub always_on_top: Option, + pub(crate) always_on_top: Option, /// Set cross-origin-policy to same-origin [default: false] #[clap(name = "cross-origin-policy")] #[clap(long)] - pub cross_origin_policy: bool, + pub(crate) cross_origin_policy: bool, /// Additional arguments to pass to the executable #[clap(long)] - pub args: Vec, + pub(crate) args: Vec, /// Sets the interval in seconds that the CLI will poll for file changes on WSL. #[clap(long, default_missing_value = "2")] - pub wsl_file_poll_interval: Option, + pub(crate) wsl_file_poll_interval: Option, /// Run the server in interactive mode #[arg(long, default_missing_value="true", num_args=0..=1, short = 'i')] - pub interactive: Option, + pub(crate) interactive: Option, /// Arguments for the build itself #[clap(flatten)] - pub build_arguments: BuildArgs, + pub(crate) build_arguments: BuildArgs, } impl ServeArgs { /// Start the tui, builder, etc by resolving the arguments and then running the actual top-level serve function - pub async fn serve(mut self) -> Result<()> { + pub(crate) async fn serve(mut self) -> Result<()> { let mut krate = DioxusCrate::new(&self.build_arguments.target_args) .context("Failed to load Dioxus workspace")?; @@ -103,15 +103,15 @@ impl ServeArgs { Ok(()) } - pub fn should_hotreload(&self) -> bool { + pub(crate) fn should_hotreload(&self) -> bool { self.hot_reload.unwrap_or(true) } - pub fn build_args(&self) -> BuildArgs { + pub(crate) fn build_args(&self) -> BuildArgs { self.build_arguments.clone() } - pub fn interactive_tty(&self) -> bool { + pub(crate) fn interactive_tty(&self) -> bool { std::io::stdout().is_tty() && self.interactive.unwrap_or(true) } } diff --git a/packages/cli/src/cli/translate.rs b/packages/cli/src/cli/translate.rs index 4c8b485189..e822cd5eb0 100644 --- a/packages/cli/src/cli/translate.rs +++ b/packages/cli/src/cli/translate.rs @@ -7,27 +7,27 @@ use super::*; /// Translate some source file into Dioxus code #[derive(Clone, Debug, Parser)] #[clap(name = "translate")] -pub struct Translate { +pub(crate) struct Translate { /// Activate debug mode // short and long flags (-d, --debug) will be deduced from the field's name #[clap(short, long)] - pub component: bool, + pub(crate) component: bool, /// Input file #[clap(short, long)] - pub file: Option, + pub(crate) file: Option, /// Input file #[clap(short, long)] - pub raw: Option, + pub(crate) raw: Option, /// Output file, stdout if not present #[arg(short, long)] - pub output: Option, + pub(crate) output: Option, } impl Translate { - pub fn translate(self) -> Result<()> { + pub(crate) fn translate(self) -> Result<()> { // Get the right input for the translation let contents = determine_input(self.file, self.raw)?; @@ -47,7 +47,7 @@ impl Translate { } } -pub fn convert_html_to_formatted_rsx(dom: &Dom, component: bool) -> String { +pub(crate) fn convert_html_to_formatted_rsx(dom: &Dom, component: bool) -> String { let callbody = rsx_rosetta::rsx_from_html(dom); match component { @@ -83,7 +83,7 @@ fn write_svg_section(out: &mut String, svgs: Vec) { for (idx, icon) in svgs.into_iter().enumerate() { let raw = dioxus_autofmt::write_block_out(&CallBody::new(TemplateBody::new(vec![icon]))).unwrap(); - out.push_str("\n\n pub fn icon_"); + out.push_str("\n\n pub(crate) fn icon_"); out.push_str(&idx.to_string()); out.push_str("() -> Element {\n rsx! {"); indent_and_write(&raw, 2, out); diff --git a/packages/cli/src/config.rs b/packages/cli/src/config.rs index c495b45e0d..27b7a52403 100644 --- a/packages/cli/src/config.rs +++ b/packages/cli/src/config.rs @@ -9,9 +9,9 @@ mod web; // mod liveview; // mod static_generation; -pub use app::*; -pub use bundle::*; -pub use desktop::*; -pub use dioxus_config::*; -pub use serve::*; -pub use web::*; +pub(crate) use app::*; +pub(crate) use bundle::*; +pub(crate) use desktop::*; +pub(crate) use dioxus_config::*; +pub(crate) use serve::*; +pub(crate) use web::*; diff --git a/packages/cli/src/config/app.rs b/packages/cli/src/config/app.rs index 9ba16c7d69..4a331767a7 100644 --- a/packages/cli/src/config/app.rs +++ b/packages/cli/src/config/app.rs @@ -3,35 +3,35 @@ use serde::{Deserialize, Serialize}; use std::path::PathBuf; #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ApplicationConfig { +pub(crate) struct ApplicationConfig { #[serde(default = "default_name")] - pub name: String, + pub(crate) name: String, #[serde(default = "default_platform")] - pub default_platform: Platform, + pub(crate) default_platform: Platform, #[serde(default = "out_dir_default")] - pub out_dir: PathBuf, + pub(crate) out_dir: PathBuf, #[serde(default = "asset_dir_default")] - pub asset_dir: PathBuf, + pub(crate) asset_dir: PathBuf, #[serde(default)] - pub sub_package: Option, + pub(crate) sub_package: Option, } -pub fn default_name() -> String { +pub(crate) fn default_name() -> String { "my-cool-project".into() } -pub fn default_platform() -> Platform { +pub(crate) fn default_platform() -> Platform { Platform::Web } -pub fn asset_dir_default() -> PathBuf { +pub(crate) fn asset_dir_default() -> PathBuf { PathBuf::from("public") } -pub fn out_dir_default() -> PathBuf { +pub(crate) fn out_dir_default() -> PathBuf { PathBuf::from("dist") } diff --git a/packages/cli/src/config/bundle.rs b/packages/cli/src/config/bundle.rs index 2c2aea5068..2ac73dac7e 100644 --- a/packages/cli/src/config/bundle.rs +++ b/packages/cli/src/config/bundle.rs @@ -3,94 +3,94 @@ use std::collections::HashMap; use std::path::PathBuf; #[derive(Debug, Clone, Serialize, Deserialize, Default)] -pub struct BundleConfig { - pub identifier: Option, - pub publisher: Option, - pub icon: Option>, - pub resources: Option>, - pub copyright: Option, - pub category: Option, - pub short_description: Option, - pub long_description: Option, - pub external_bin: Option>, - pub deb: Option, - pub macos: Option, - pub windows: Option, +pub(crate) struct BundleConfig { + pub(crate) identifier: Option, + pub(crate) publisher: Option, + pub(crate) icon: Option>, + pub(crate) resources: Option>, + pub(crate) copyright: Option, + pub(crate) category: Option, + pub(crate) short_description: Option, + pub(crate) long_description: Option, + pub(crate) external_bin: Option>, + pub(crate) deb: Option, + pub(crate) macos: Option, + pub(crate) windows: Option, } #[derive(Debug, Clone, Serialize, Deserialize, Default)] -pub struct DebianSettings { - pub depends: Option>, - pub files: HashMap, - pub nsis: Option, +pub(crate) struct DebianSettings { + pub(crate) depends: Option>, + pub(crate) files: HashMap, + pub(crate) nsis: Option, } #[derive(Debug, Clone, Serialize, Deserialize, Default)] -pub struct WixSettings { - pub language: Vec<(String, Option)>, - pub template: Option, - pub fragment_paths: Vec, - pub component_group_refs: Vec, - pub component_refs: Vec, - pub feature_group_refs: Vec, - pub feature_refs: Vec, - pub merge_refs: Vec, - pub skip_webview_install: bool, - pub license: Option, - pub enable_elevated_update_task: bool, - pub banner_path: Option, - pub dialog_image_path: Option, - pub fips_compliant: bool, +pub(crate) struct WixSettings { + pub(crate) language: Vec<(String, Option)>, + pub(crate) template: Option, + pub(crate) fragment_paths: Vec, + pub(crate) component_group_refs: Vec, + pub(crate) component_refs: Vec, + pub(crate) feature_group_refs: Vec, + pub(crate) feature_refs: Vec, + pub(crate) merge_refs: Vec, + pub(crate) skip_webview_install: bool, + pub(crate) license: Option, + pub(crate) enable_elevated_update_task: bool, + pub(crate) banner_path: Option, + pub(crate) dialog_image_path: Option, + pub(crate) fips_compliant: bool, } #[derive(Debug, Clone, Serialize, Deserialize, Default)] -pub struct MacOsSettings { - pub frameworks: Option>, - pub minimum_system_version: Option, - pub license: Option, - pub exception_domain: Option, - pub signing_identity: Option, - pub provider_short_name: Option, - pub entitlements: Option, - pub info_plist_path: Option, +pub(crate) struct MacOsSettings { + pub(crate) frameworks: Option>, + pub(crate) minimum_system_version: Option, + pub(crate) license: Option, + pub(crate) exception_domain: Option, + pub(crate) signing_identity: Option, + pub(crate) provider_short_name: Option, + pub(crate) entitlements: Option, + pub(crate) info_plist_path: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct WindowsSettings { - pub digest_algorithm: Option, - pub certificate_thumbprint: Option, - pub timestamp_url: Option, - pub tsp: bool, - pub wix: Option, - pub icon_path: Option, - pub webview_install_mode: WebviewInstallMode, - pub webview_fixed_runtime_path: Option, - pub allow_downgrades: bool, - pub nsis: Option, +pub(crate) struct WindowsSettings { + pub(crate) digest_algorithm: Option, + pub(crate) certificate_thumbprint: Option, + pub(crate) timestamp_url: Option, + pub(crate) tsp: bool, + pub(crate) wix: Option, + pub(crate) icon_path: Option, + pub(crate) webview_install_mode: WebviewInstallMode, + pub(crate) webview_fixed_runtime_path: Option, + pub(crate) allow_downgrades: bool, + pub(crate) nsis: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct NsisSettings { - pub template: Option, - pub license: Option, - pub header_image: Option, - pub sidebar_image: Option, - pub installer_icon: Option, - pub install_mode: NSISInstallerMode, - pub languages: Option>, - pub custom_language_files: Option>, - pub display_language_selector: bool, +pub(crate) struct NsisSettings { + pub(crate) template: Option, + pub(crate) license: Option, + pub(crate) header_image: Option, + pub(crate) sidebar_image: Option, + pub(crate) installer_icon: Option, + pub(crate) install_mode: NSISInstallerMode, + pub(crate) languages: Option>, + pub(crate) custom_language_files: Option>, + pub(crate) display_language_selector: bool, } #[derive(Debug, Clone, Serialize, Deserialize)] -pub enum NSISInstallerMode { +pub(crate) enum NSISInstallerMode { CurrentUser, PerMachine, Both, } #[derive(Debug, Clone, Serialize, Deserialize)] -pub enum WebviewInstallMode { +pub(crate) enum WebviewInstallMode { Skip, DownloadBootstrapper { silent: bool }, EmbedBootstrapper { silent: bool }, diff --git a/packages/cli/src/config/desktop.rs b/packages/cli/src/config/desktop.rs index ec6cd49365..13fb0918b9 100644 --- a/packages/cli/src/config/desktop.rs +++ b/packages/cli/src/config/desktop.rs @@ -2,10 +2,10 @@ use serde::{Deserialize, Serialize}; /// Represents configuration items for the desktop platform. #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct DesktopConfig { +pub(crate) struct DesktopConfig { /// Describes whether a debug-mode desktop app should be always-on-top. #[serde(default)] - pub always_on_top: bool, + pub(crate) always_on_top: bool, } impl Default for DesktopConfig { diff --git a/packages/cli/src/config/dioxus_config.rs b/packages/cli/src/config/dioxus_config.rs index 3f8fa23d99..ec1bcbc66e 100644 --- a/packages/cli/src/config/dioxus_config.rs +++ b/packages/cli/src/config/dioxus_config.rs @@ -2,17 +2,17 @@ use super::*; use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct DioxusConfig { - pub application: ApplicationConfig, +pub(crate) struct DioxusConfig { + pub(crate) application: ApplicationConfig, #[serde(default)] - pub web: WebConfig, + pub(crate) web: WebConfig, #[serde(default)] - pub desktop: DesktopConfig, + pub(crate) desktop: DesktopConfig, #[serde(default)] - pub bundle: BundleConfig, + pub(crate) bundle: BundleConfig, } impl Default for DioxusConfig { diff --git a/packages/cli/src/config/serve.rs b/packages/cli/src/config/serve.rs index 39d24cb747..8edc6e6c35 100644 --- a/packages/cli/src/config/serve.rs +++ b/packages/cli/src/config/serve.rs @@ -5,15 +5,15 @@ use std::net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4}; /// The arguments for the address the server will run on #[derive(Clone, Debug, Parser)] -pub struct AddressArguments { +pub(crate) struct AddressArguments { /// The port the server will run on #[clap(long)] #[clap(default_value_t = default_port())] - pub port: u16, + pub(crate) port: u16, /// The address the server will run on #[clap(long, default_value_t = default_address())] - pub addr: std::net::IpAddr, + pub(crate) addr: std::net::IpAddr, } impl Default for AddressArguments { @@ -27,7 +27,7 @@ impl Default for AddressArguments { impl AddressArguments { /// Get the address the server should run on - pub fn address(&self) -> SocketAddr { + pub(crate) fn address(&self) -> SocketAddr { SocketAddr::new(self.addr, self.port) } } diff --git a/packages/cli/src/config/web.rs b/packages/cli/src/config/web.rs index 037e72f49f..489bfa07c8 100644 --- a/packages/cli/src/config/web.rs +++ b/packages/cli/src/config/web.rs @@ -2,29 +2,29 @@ use serde::{Deserialize, Serialize}; use std::path::PathBuf; #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct WebConfig { +pub(crate) struct WebConfig { #[serde(default)] - pub app: WebAppConfig, + pub(crate) app: WebAppConfig, #[serde(default)] - pub proxy: Vec, + pub(crate) proxy: Vec, #[serde(default)] - pub watcher: WebWatcherConfig, + pub(crate) watcher: WebWatcherConfig, #[serde(default)] - pub resource: WebResourceConfig, + pub(crate) resource: WebResourceConfig, #[serde(default)] - pub https: WebHttpsConfig, + pub(crate) https: WebHttpsConfig, /// Whether to enable pre-compression of assets and wasm during a web build in release mode #[serde(default = "true_bool")] - pub pre_compress: bool, + pub(crate) pre_compress: bool, /// The wasm-opt configuration #[serde(default)] - pub wasm_opt: WasmOptConfig, + pub(crate) wasm_opt: WasmOptConfig, } impl Default for WebConfig { @@ -43,7 +43,7 @@ impl Default for WebConfig { /// The wasm-opt configuration #[derive(Debug, Clone, Serialize, Deserialize, Default)] -pub struct WasmOptConfig { +pub(crate) struct WasmOptConfig { /// The wasm-opt level to use for release builds [default: s] /// Options: /// - z: optimize aggressively for size @@ -53,16 +53,16 @@ pub struct WasmOptConfig { /// - 3: optimize for even more for speed /// - 4: optimize aggressively for speed #[serde(default)] - pub level: WasmOptLevel, + pub(crate) level: WasmOptLevel, /// Keep debug symbols in the wasm file #[serde(default = "false_bool")] - pub debug: bool, + pub(crate) debug: bool, } /// The wasm-opt level to use for release web builds [default: 4] #[derive(Default, Debug, Copy, Clone, Serialize, Deserialize)] -pub enum WasmOptLevel { +pub(crate) enum WasmOptLevel { /// Optimize aggressively for size #[serde(rename = "z")] Z, @@ -88,15 +88,15 @@ pub enum WasmOptLevel { } #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct WebAppConfig { +pub(crate) struct WebAppConfig { #[serde(default = "default_title")] - pub title: String, - pub base_path: Option, + pub(crate) title: String, + pub(crate) base_path: Option, } impl WebAppConfig { /// Get the normalized base path for the application with `/` trimmed from both ends. If the base path is not set, this will return `.`. - pub fn base_path(&self) -> &str { + pub(crate) fn base_path(&self) -> &str { match &self.base_path { Some(path) => path.trim_matches('/'), None => ".", @@ -114,20 +114,20 @@ impl Default for WebAppConfig { } #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct WebProxyConfig { - pub backend: String, +pub(crate) struct WebProxyConfig { + pub(crate) backend: String, } #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct WebWatcherConfig { +pub(crate) struct WebWatcherConfig { #[serde(default = "watch_path_default")] - pub watch_path: Vec, + pub(crate) watch_path: Vec, #[serde(default)] - pub reload_html: bool, + pub(crate) reload_html: bool, #[serde(default = "true_bool")] - pub index_on_404: bool, + pub(crate) index_on_404: bool, } impl Default for WebWatcherConfig { @@ -145,26 +145,26 @@ fn watch_path_default() -> Vec { } #[derive(Default, Debug, Clone, Serialize, Deserialize)] -pub struct WebResourceConfig { - pub dev: WebDevResourceConfig, - pub style: Option>, - pub script: Option>, +pub(crate) struct WebResourceConfig { + pub(crate) dev: WebDevResourceConfig, + pub(crate) style: Option>, + pub(crate) script: Option>, } #[derive(Default, Debug, Clone, Serialize, Deserialize)] -pub struct WebDevResourceConfig { +pub(crate) struct WebDevResourceConfig { #[serde(default)] - pub style: Vec, + pub(crate) style: Vec, #[serde(default)] - pub script: Vec, + pub(crate) script: Vec, } #[derive(Debug, Default, Clone, Serialize, Deserialize)] -pub struct WebHttpsConfig { - pub enabled: Option, - pub mkcert: Option, - pub key_path: Option, - pub cert_path: Option, +pub(crate) struct WebHttpsConfig { + pub(crate) enabled: Option, + pub(crate) mkcert: Option, + pub(crate) key_path: Option, + pub(crate) cert_path: Option, } fn true_bool() -> bool { @@ -175,6 +175,6 @@ fn false_bool() -> bool { false } -pub fn default_title() -> String { +pub(crate) fn default_title() -> String { "dioxus | ⛺".into() } diff --git a/packages/cli/src/dioxus_crate.rs b/packages/cli/src/dioxus_crate.rs index db01b102f4..9511d4d83d 100644 --- a/packages/cli/src/dioxus_crate.rs +++ b/packages/cli/src/dioxus_crate.rs @@ -13,15 +13,15 @@ use crate::metadata::CargoError; // Contains information about the crate we are currently in and the dioxus config for that crate #[derive(Clone)] -pub struct DioxusCrate { - pub krates: Arc, - pub package: NodeId, - pub dioxus_config: DioxusConfig, - pub target: Target, +pub(crate) struct DioxusCrate { + pub(crate) krates: Arc, + pub(crate) package: NodeId, + pub(crate) dioxus_config: DioxusConfig, + pub(crate) target: Target, } impl DioxusCrate { - pub fn new(target: &TargetArgs) -> Result { + pub(crate) fn new(target: &TargetArgs) -> Result { let mut cmd = Cmd::new(); cmd.features(target.features.clone()); let builder = krates::Builder::new(); @@ -61,13 +61,13 @@ impl DioxusCrate { /// Compose an asset directory. Represents the typical "public" directory /// with publicly available resources (configurable in the `Dioxus.toml`). - pub fn legacy_asset_dir(&self) -> PathBuf { + pub(crate) fn legacy_asset_dir(&self) -> PathBuf { self.crate_dir() .join(&self.dioxus_config.application.asset_dir) } /// Get the list of files in the "legacy" asset directory - pub fn legacy_asset_dir_files(&self) -> Vec { + pub(crate) fn legacy_asset_dir_files(&self) -> Vec { let mut files = vec![]; let Ok(read_dir) = self.legacy_asset_dir().read_dir() else { @@ -86,18 +86,18 @@ impl DioxusCrate { /// Compose an out directory. Represents the typical "dist" directory that /// is "distributed" after building an application (configurable in the /// `Dioxus.toml`). - pub fn out_dir(&self) -> PathBuf { + pub(crate) fn out_dir(&self) -> PathBuf { self.workspace_dir() .join(&self.dioxus_config.application.out_dir) } /// Get the workspace directory for the crate - pub fn workspace_dir(&self) -> PathBuf { + pub(crate) fn workspace_dir(&self) -> PathBuf { self.krates.workspace_root().as_std_path().to_path_buf() } /// Get the directory of the crate - pub fn crate_dir(&self) -> PathBuf { + pub(crate) fn crate_dir(&self) -> PathBuf { self.package() .manifest_path .parent() @@ -107,26 +107,26 @@ impl DioxusCrate { } /// Get the main source file of the target - pub fn main_source_file(&self) -> PathBuf { + pub(crate) fn main_source_file(&self) -> PathBuf { self.target.src_path.as_std_path().to_path_buf() } /// Get the package we are currently in - pub fn package(&self) -> &krates::cm::Package { + pub(crate) fn package(&self) -> &krates::cm::Package { &self.krates[self.package] } /// Get the name of the package we are compiling - pub fn executable_name(&self) -> &str { + pub(crate) fn executable_name(&self) -> &str { &self.target.name } /// Get the type of executable we are compiling - pub fn executable_type(&self) -> krates::cm::TargetKind { + pub(crate) fn executable_type(&self) -> krates::cm::TargetKind { self.target.kind[0].clone() } - pub fn features_for_platform(&mut self, platform: Platform) -> Vec { + pub(crate) fn features_for_platform(&mut self, platform: Platform) -> Vec { let package = self.package(); // Try to find the feature that activates the dioxus feature for the given platform let dioxus_feature = platform.feature_name(); @@ -152,19 +152,19 @@ impl DioxusCrate { /// Check if assets should be pre_compressed. This will only be true in release mode if the user /// has enabled pre_compress in the web config. - pub fn should_pre_compress_web_assets(&self, release: bool) -> bool { + pub(crate) fn should_pre_compress_web_assets(&self, release: bool) -> bool { self.dioxus_config.web.pre_compress && release } } #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Executable { - pub name: String, - pub ty: ExecutableType, +pub(crate) struct Executable { + pub(crate) name: String, + pub(crate) ty: ExecutableType, } #[derive(Debug, Copy, Clone, Serialize, Deserialize)] -pub enum ExecutableType { +pub(crate) enum ExecutableType { Binary, Lib, Example, @@ -172,13 +172,13 @@ pub enum ExecutableType { impl ExecutableType { /// Get the name of the executable if it is a binary or an example. - pub fn executable(&self) -> bool { + pub(crate) fn executable(&self) -> bool { matches!(self, Self::Binary | Self::Example) } } #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct LoadDioxusConfigError { +pub(crate) struct LoadDioxusConfigError { location: String, error: String, } @@ -193,7 +193,7 @@ impl std::error::Error for LoadDioxusConfigError {} #[derive(Debug)] #[non_exhaustive] -pub enum CrateConfigError { +pub(crate) enum CrateConfigError { Cargo(CargoError), Io(std::io::Error), Toml(toml::de::Error), diff --git a/packages/cli/src/error.rs b/packages/cli/src/error.rs index 6d37636eda..04d1e9e2c7 100644 --- a/packages/cli/src/error.rs +++ b/packages/cli/src/error.rs @@ -2,10 +2,10 @@ use thiserror::Error as ThisError; use crate::{metadata::CargoError, CrateConfigError, LoadDioxusConfigError}; -pub type Result = std::result::Result; +pub(crate) type Result = std::result::Result; #[derive(ThisError, Debug)] -pub enum Error { +pub(crate) enum Error { /// Used when errors need to propagate but are too unique to be typed #[error("{0}")] Unique(String), diff --git a/packages/cli/src/main.rs b/packages/cli/src/main.rs index 7675dab9d2..f669c7c777 100644 --- a/packages/cli/src/main.rs +++ b/packages/cli/src/main.rs @@ -2,25 +2,25 @@ #![doc(html_logo_url = "https://avatars.githubusercontent.com/u/79236386")] #![doc(html_favicon_url = "https://avatars.githubusercontent.com/u/79236386")] -pub mod assets; -pub mod build_info; -pub mod builder; -pub mod bundle_utils; -pub mod bundler; -pub mod cli; -pub mod config; -pub mod dioxus_crate; -pub mod error; -pub mod fastfs; -pub mod metadata; -pub mod serve; -pub mod settings; -pub mod tools; - -pub use cli::*; -pub use dioxus_crate::*; -pub use error::*; -pub use settings::*; +pub(crate) mod assets; +pub(crate) mod build_info; +pub(crate) mod builder; +pub(crate) mod bundle_utils; +pub(crate) mod bundler; +pub(crate) mod cli; +pub(crate) mod config; +pub(crate) mod dioxus_crate; +pub(crate) mod error; +pub(crate) mod fastfs; +pub(crate) mod metadata; +pub(crate) mod serve; +pub(crate) mod settings; +pub(crate) mod tools; + +pub(crate) use cli::*; +pub(crate) use dioxus_crate::*; +pub(crate) use error::*; +pub(crate) use settings::*; use anyhow::Context; use clap::Parser; diff --git a/packages/cli/src/metadata.rs b/packages/cli/src/metadata.rs index 3eab9b3afb..f9e0726836 100644 --- a/packages/cli/src/metadata.rs +++ b/packages/cli/src/metadata.rs @@ -8,12 +8,12 @@ use std::{ }; #[derive(Debug, Clone)] -pub struct CargoError { +pub(crate) struct CargoError { msg: String, } impl CargoError { - pub fn new(msg: String) -> Self { + pub(crate) fn new(msg: String) -> Self { Self { msg } } } @@ -32,7 +32,7 @@ const MAX_ANCESTORS: u32 = 10; /// Returns the root of the crate that the command is run from /// /// If the command is run from the workspace root, this will return the top-level Cargo.toml -pub fn crate_root() -> Result { +pub(crate) fn crate_root() -> Result { // From the current directory we work our way up, looking for `Cargo.toml` env::current_dir() .ok() diff --git a/packages/cli/src/serve/detect.rs b/packages/cli/src/serve/detect.rs index 8518d8d847..504731da14 100644 --- a/packages/cli/src/serve/detect.rs +++ b/packages/cli/src/serve/detect.rs @@ -3,7 +3,7 @@ /// We determine this based on whether the keyword `microsoft` or `wsl` is contained within the [`WSL_1`] or [`WSL_2`] files. /// This may fail in the future as it isn't guaranteed by Microsoft. /// See https://github.com/microsoft/WSL/issues/423#issuecomment-221627364 -pub fn is_wsl() -> bool { +pub(crate) fn is_wsl() -> bool { const WSL_1: &str = "/proc/sys/kernel/osrelease"; const WSL_2: &str = "/proc/version"; const WSL_KEYWORDS: [&str; 2] = ["microsoft", "wsl"]; diff --git a/packages/cli/src/serve/hot_reloading_file_map.rs b/packages/cli/src/serve/hot_reloading_file_map.rs index 6ffe1d1279..0659047427 100644 --- a/packages/cli/src/serve/hot_reloading_file_map.rs +++ b/packages/cli/src/serve/hot_reloading_file_map.rs @@ -6,34 +6,34 @@ use dioxus_rsx::{ }; use krates::cm::MetadataCommand; use krates::Cmd; -pub use std::collections::HashMap; +pub(crate) use std::collections::HashMap; use std::{ffi::OsStr, path::PathBuf}; -pub use std::{fs, io, path::Path}; -pub use std::{fs::File, io::Read}; +pub(crate) use std::{fs, io, path::Path}; +pub(crate) use std::{fs::File, io::Read}; use syn::spanned::Spanned; -pub struct FileMap { - pub map: HashMap, +pub(crate) struct FileMap { + pub(crate) map: HashMap, /// Any errors that occurred while building the FileMap that were not fatal - pub errors: Vec, + pub(crate) errors: Vec, - pub in_workspace: HashMap>, + pub(crate) in_workspace: HashMap>, } /// A cached file that has been parsed /// /// We store the templates found in this file -pub struct CachedSynFile { - pub raw: String, - pub templates: HashMap, +pub(crate) struct CachedSynFile { + pub(crate) raw: String, + pub(crate) templates: HashMap, } impl FileMap { /// Create a new FileMap from a crate directory /// /// TODO: this should be created with a gitignore filter - pub fn create(path: PathBuf) -> io::Result { + pub(crate) fn create(path: PathBuf) -> io::Result { Self::create_with_filter::(path, |p| { // skip some stuff we know is large by default p.file_name() == Some(OsStr::new("target")) @@ -45,7 +45,7 @@ impl FileMap { /// /// Takes a filter that when returns true, the file will be filtered out (ie not tracked) /// Note that this is inverted from a typical .filter() method. - pub fn create_with_filter( + pub(crate) fn create_with_filter( crate_dir: PathBuf, mut filter: impl FnMut(&Path) -> bool, ) -> io::Result { @@ -65,7 +65,7 @@ impl FileMap { /// Start watching assets for changes /// /// This just diffs every file against itself and populates the tracked assets as it goes - pub fn load_assets(&mut self, crate_dir: &Path) { + pub(crate) fn load_assets(&mut self, crate_dir: &Path) { let keys = self.map.keys().cloned().collect::>(); for file in keys { _ = self.update_rsx::(file.as_path(), crate_dir); @@ -84,7 +84,7 @@ impl FileMap { } /// Try to update the rsx in a file - pub fn update_rsx( + pub(crate) fn update_rsx( &mut self, file_path: &Path, crate_dir: &Path, @@ -221,7 +221,7 @@ impl FileMap { } } -pub fn template_location(old_start: proc_macro2::LineColumn, file: &Path) -> String { +pub(crate) fn template_location(old_start: proc_macro2::LineColumn, file: &Path) -> String { let line = old_start.line; let column = old_start.column + 1; @@ -235,7 +235,7 @@ pub fn template_location(old_start: proc_macro2::LineColumn, file: &Path) -> Str path + ":" + line.to_string().as_str() + ":" + column.to_string().as_str() } -pub fn format_template_name(name: &str, index: usize) -> String { +pub(crate) fn format_template_name(name: &str, index: usize) -> String { format!("{}:{}", name, index) } @@ -292,7 +292,7 @@ fn find_rs_files(root: PathBuf, filter: &mut impl FnMut(&Path) -> bool) -> FileM } #[derive(Debug)] -pub enum HotreloadError { +pub(crate) enum HotreloadError { Failure(io::Error), Parse, Notreloadable, diff --git a/packages/cli/src/serve/mod.rs b/packages/cli/src/serve/mod.rs index bf97e3521c..be5f6721b2 100644 --- a/packages/cli/src/serve/mod.rs +++ b/packages/cli/src/serve/mod.rs @@ -18,7 +18,7 @@ mod watcher; use output::*; use runner::*; use server::*; -pub use tracer::*; +pub(crate) use tracer::*; use update::*; use watcher::*; @@ -43,7 +43,7 @@ use watcher::*; /// - Handle logs from the build engine separately? /// - I want us to be able to detect a `server_fn` in the project and then upgrade from a static server /// to a dynamic one on the fly. -pub async fn serve_all(args: ServeArgs, krate: DioxusCrate) -> Result<()> { +pub(crate) async fn serve_all(args: ServeArgs, krate: DioxusCrate) -> Result<()> { let mut tracer = tracer::TraceController::start(); // Note that starting the builder will queue up a build immediately diff --git a/packages/cli/src/serve/output.rs b/packages/cli/src/serve/output.rs index 0e0e4f14d5..07bfba9be3 100644 --- a/packages/cli/src/serve/output.rs +++ b/packages/cli/src/serve/output.rs @@ -24,7 +24,7 @@ use tracing::Level; use super::{update::ServeUpdate, AppHandle, Builder, DevServer, Watcher}; -pub struct Output { +pub(crate) struct Output { term: Rc>>, // optional since when there's no tty there's no eventstream to read from - just stdin @@ -34,7 +34,7 @@ pub struct Output { _rustc_nightly: bool, _dx_version: String, interactive: bool, - pub build_progress: BuildProgress, + pub(crate) build_progress: BuildProgress, is_cli_release: bool, platform: Platform, @@ -58,7 +58,7 @@ enum Tab { type TerminalBackend = Terminal>; impl Output { - pub fn start(cfg: &ServeArgs) -> io::Result { + pub(crate) fn start(cfg: &ServeArgs) -> io::Result { let interactive = cfg.interactive_tty(); let mut events = None; @@ -125,7 +125,7 @@ impl Output { } /// Add a message from stderr to the logs - pub fn push_stderr(&mut self, platform: Platform, stderr: String) { + pub(crate) fn push_stderr(&mut self, platform: Platform, stderr: String) { // self.set_tab(Tab::BuildLog); @@ -150,7 +150,7 @@ impl Output { } /// Add a message from stdout to the logs - pub fn push_stdout(&mut self, platform: Platform, stdout: String) { + pub(crate) fn push_stdout(&mut self, platform: Platform, stdout: String) { // self.running_apps // .get_mut(&platform) @@ -177,7 +177,7 @@ impl Output { /// Why is the ctrl_c handler here? /// /// Also tick animations every few ms - pub async fn wait(&mut self) -> ServeUpdate { + pub(crate) async fn wait(&mut self) -> ServeUpdate { let event = tokio::select! { Some(Ok(event)) = self.events.as_mut().unwrap().next(), if self.events.is_some() => event }; @@ -185,7 +185,7 @@ impl Output { ServeUpdate::TuiInput { event } } - pub fn shutdown(&mut self) -> io::Result<()> { + pub(crate) fn shutdown(&mut self) -> io::Result<()> { // if we're a tty then we need to disable the raw mode if self.interactive { disable_raw_mode()?; @@ -196,47 +196,8 @@ impl Output { Ok(()) } - /// Emit the build logs as println! statements such that the terminal has the same output as cargo - /// - /// This is used when the terminal is shutdown and we want the build logs in the terminal. Old - /// versions of the cli would just eat build logs making debugging issues harder than they needed - /// to be. - fn drain_print_logs(&mut self) { - fn log_build_message(platform: &LogSource, message: &BuildMessage) { - match &message.message { - MessageType::Text(text) => { - for line in text.lines() { - println!("{platform}: {line}"); - } - } - MessageType::Cargo(diagnostic) => { - println!("{platform}: {diagnostic}"); - } - } - } - - // todo: print the build info here for the most recent build, and then the logs of the most recent build - for (platform, build) in self.build_progress.build_logs.iter_mut() { - if build.messages.is_empty() { - continue; - } - - let messages = build.messages.drain(0..); - - for message in messages { - log_build_message(&LogSource::Target(*platform), &message); - } - } - - // Log the internal logs - let messaegs = self.build_progress.internal_logs.drain(..); - for message in messaegs { - log_build_message(&LogSource::Internal, &message); - } - } - /// Handle an input event, returning `true` if the event should cause the program to restart. - pub fn handle_input(&mut self, input: Event) -> io::Result { + pub(crate) fn handle_input(&mut self, input: Event) -> io::Result { // let mut events = vec![event]; // // Collect all the events within the next 10ms in one stream @@ -334,7 +295,11 @@ impl Output { Ok(false) } - pub fn new_ws_message(&mut self, platform: Platform, message: axum::extract::ws::Message) { + pub(crate) fn new_ws_message( + &mut self, + platform: Platform, + message: axum::extract::ws::Message, + ) { if let axum::extract::ws::Message::Text(text) = message { let msg = serde_json::from_str::(text.as_str()); match msg { @@ -372,21 +337,11 @@ impl Output { } } - // todo: re-enable - #[allow(unused)] - fn is_snapped(&self, _platform: LogSource) -> bool { - true - // let prev_scrol = self - // .num_lines_with_wrapping - // .saturating_sub(self.term_height); - // prev_scrol == self.scroll - } - - pub fn scroll_to_bottom(&mut self) { + pub(crate) fn scroll_to_bottom(&mut self) { self.scroll = (self.num_lines_with_wrapping).saturating_sub(self.term_height); } - pub fn push_inner_log(&mut self, msg: String) { + pub(crate) fn push_inner_log(&mut self, msg: String) { self.push_log( LogSource::Internal, crate::builder::BuildMessage { @@ -397,27 +352,7 @@ impl Output { ); } - pub fn push_log(&mut self, platform: impl Into, message: BuildMessage) { - let source = platform.into(); - let snapped = self.is_snapped(source); - - match source { - LogSource::Internal => self.build_progress.internal_logs.push(message), - LogSource::Target(platform) => self - .build_progress - .build_logs - .entry(platform) - .or_default() - .stdout_logs - .push(message), - } - - if snapped { - self.scroll_to_bottom(); - } - } - - pub fn new_build_logs(&mut self, platform: Platform, update: UpdateBuildProgress) { + pub(crate) fn new_build_logs(&mut self, platform: Platform, update: UpdateBuildProgress) { let snapped = self.is_snapped(LogSource::Target(platform)); // when the build is finished, switch to the console @@ -436,7 +371,7 @@ impl Output { } } - pub fn new_ready_app(&mut self, handle: &AppHandle) { + pub(crate) fn new_ready_app(&mut self, handle: &AppHandle) { // for result in results { // let out = build_engine // .finished @@ -474,7 +409,7 @@ impl Output { // } } - pub fn render( + pub(crate) fn render( &mut self, args: &ServeArgs, krate: &DioxusCrate, @@ -752,10 +687,75 @@ impl Output { self.tab = tab; self.scroll = 0; } + + fn push_log(&mut self, platform: impl Into, message: BuildMessage) { + let source = platform.into(); + let snapped = self.is_snapped(source); + + match source { + LogSource::Internal => self.build_progress.internal_logs.push(message), + LogSource::Target(platform) => self + .build_progress + .build_logs + .entry(platform) + .or_default() + .stdout_logs + .push(message), + } + + if snapped { + self.scroll_to_bottom(); + } + } + + /// Emit the build logs as println! statements such that the terminal has the same output as cargo + /// + /// This is used when the terminal is shutdown and we want the build logs in the terminal. Old + /// versions of the cli would just eat build logs making debugging issues harder than they needed + /// to be. + fn drain_print_logs(&mut self) { + fn log_build_message(platform: &LogSource, message: &BuildMessage) { + match &message.message { + MessageType::Text(text) => { + for line in text.lines() { + println!("{platform}: {line}"); + } + } + MessageType::Cargo(diagnostic) => { + println!("{platform}: {diagnostic}"); + } + } + } + + // todo: print the build info here for the most recent build, and then the logs of the most recent build + for (platform, build) in self.build_progress.build_logs.iter_mut() { + if build.messages.is_empty() { + continue; + } + + let messages = build.messages.drain(0..); + + for message in messages { + log_build_message(&LogSource::Target(*platform), &message); + } + } + + // Log the internal logs + let messaegs = self.build_progress.internal_logs.drain(..); + for message in messaegs { + log_build_message(&LogSource::Internal, &message); + } + } + + // todo: re-enable + #[allow(unused)] + fn is_snapped(&self, _platform: LogSource) -> bool { + true + } } #[derive(Default, Debug, PartialEq)] -pub struct ActiveBuild { +pub(crate) struct ActiveBuild { stage: Stage, messages: Vec, stdout_logs: Vec, @@ -859,7 +859,7 @@ async fn rustc_version() -> String { } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub enum LogSource { +pub(crate) enum LogSource { Internal, Target(Platform), } @@ -880,13 +880,13 @@ impl From for LogSource { } #[derive(Default)] -pub struct BuildProgress { +pub(crate) struct BuildProgress { internal_logs: Vec, build_logs: HashMap, } impl BuildProgress { - pub fn progress(&self) -> f64 { + pub(crate) fn progress(&self) -> f64 { self.build_logs .values() .min_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)) diff --git a/packages/cli/src/serve/proxy.rs b/packages/cli/src/serve/proxy.rs index 9939525603..3b94eaf30e 100644 --- a/packages/cli/src/serve/proxy.rs +++ b/packages/cli/src/serve/proxy.rs @@ -55,7 +55,7 @@ impl ProxyClient { /// - the exact path of the proxy config's backend URL, e.g. /api /// - the exact path with a trailing slash, e.g. /api/ /// - any subpath of the backend URL, e.g. /api/foo/bar -pub fn add_proxy(mut router: Router, proxy: &WebProxyConfig) -> Result { +pub(crate) fn add_proxy(mut router: Router, proxy: &WebProxyConfig) -> Result { let url: Uri = proxy.backend.parse()?; let path = url.path().to_string(); let trimmed_path = path.trim_start_matches('/'); @@ -91,7 +91,7 @@ pub fn add_proxy(mut router: Router, proxy: &WebProxyConfig) -> Result { Ok(router) } -pub fn proxy_to( +pub(crate) fn proxy_to( url: Uri, nocache: bool, handle_error: fn(Error) -> Response, diff --git a/packages/cli/src/serve/runner.rs b/packages/cli/src/serve/runner.rs index fe5e755e23..d41cb53655 100644 --- a/packages/cli/src/serve/runner.rs +++ b/packages/cli/src/serve/runner.rs @@ -14,23 +14,23 @@ use tokio::{ use tokio_stream::StreamExt; use uuid::Uuid; -pub struct AppRunner { +pub(crate) struct AppRunner { /// Ongoing apps running in place /// /// They might be actively being being, running, or have exited. /// /// When a new full rebuild occurs, we will keep these requests here - pub running: HashMap, + pub(crate) running: HashMap, } impl AppRunner { - pub fn start(serve: &ServeArgs, config: &DioxusCrate) -> Self { + pub(crate) fn start(serve: &ServeArgs, config: &DioxusCrate) -> Self { Self { running: Default::default(), } } - pub async fn shutdown(&mut self) { + pub(crate) async fn shutdown(&mut self) { for (_, mut handle) in self.running.drain() { if let Some(mut child) = handle.child.take() { let _ = child.kill().await; @@ -38,7 +38,7 @@ impl AppRunner { } } - pub async fn wait(&mut self) -> ServeUpdate { + pub(crate) async fn wait(&mut self) -> ServeUpdate { // If there are no running apps, we can just return pending to avoid deadlocking if self.running.is_empty() { return futures_util::future::pending().await; @@ -69,7 +69,7 @@ impl AppRunner { } /// Finally "bundle" this app and return a handle to it - pub async fn open( + pub(crate) async fn open( &mut self, app: AppBundle, devserver_ip: SocketAddr, @@ -197,15 +197,15 @@ impl AppRunner { } /// A handle to a running app -pub struct AppHandle { - pub id: Uuid, - pub app: AppBundle, - pub executable: PathBuf, - pub child: Option, - pub stdout: Option>>, - pub stderr: Option>>, - // pub stdout_line: String, - // pub stderr_line: String, +pub(crate) struct AppHandle { + pub(crate) id: Uuid, + pub(crate) app: AppBundle, + pub(crate) executable: PathBuf, + pub(crate) child: Option, + pub(crate) stdout: Option>>, + pub(crate) stderr: Option>>, + // pub(crate) stdout_line: String, + // pub(crate) stderr_line: String, } impl AppHandle { @@ -214,7 +214,7 @@ impl AppHandle { /// Might need to upload the asset to the simulator or overwrite it within the bundle /// /// Returns the name of the asset in the bundle if it exists - pub fn hotreload_asset(&self, path: &PathBuf) -> Option { + pub(crate) fn hotreload_asset(&self, path: &PathBuf) -> Option { let resource = self.app.assets.assets.get(path).cloned()?; self.app diff --git a/packages/cli/src/serve/server.rs b/packages/cli/src/serve/server.rs index 3ae5dae4aa..3917bc2cea 100644 --- a/packages/cli/src/serve/server.rs +++ b/packages/cli/src/serve/server.rs @@ -78,17 +78,17 @@ impl SharedStatus { } } -pub struct DevServer { - pub serve: ServeArgs, - pub hot_reload_sockets: Vec, - pub build_status_sockets: Vec, - pub ip: SocketAddr, - pub new_hot_reload_sockets: UnboundedReceiver, - pub new_build_status_sockets: UnboundedReceiver, +pub(crate) struct DevServer { + pub(crate) serve: ServeArgs, + pub(crate) hot_reload_sockets: Vec, + pub(crate) build_status_sockets: Vec, + pub(crate) ip: SocketAddr, + pub(crate) new_hot_reload_sockets: UnboundedReceiver, + pub(crate) new_build_status_sockets: UnboundedReceiver, _server_task: JoinHandle>, /// We proxy (not hot reloading) fullstack requests to this port - pub fullstack_port: Option, + pub(crate) fullstack_port: Option, build_status: SharedStatus, application_name: String, @@ -96,7 +96,7 @@ pub struct DevServer { } impl DevServer { - pub fn start(args: &ServeArgs, cfg: &DioxusCrate) -> Self { + pub(crate) fn start(args: &ServeArgs, cfg: &DioxusCrate) -> Self { let (hot_reload_sockets_tx, hot_reload_sockets_rx) = futures_channel::mpsc::unbounded(); let (build_status_sockets_tx, build_status_sockets_rx) = futures_channel::mpsc::unbounded(); @@ -198,7 +198,7 @@ impl DevServer { } /// Sends a start build message to all clients. - pub async fn start_build(&mut self) { + pub(crate) async fn start_build(&mut self) { self.build_status.set(Status::Building { progress: 0.0, build_message: "Starting the build...".to_string(), @@ -207,7 +207,7 @@ impl DevServer { } /// Sends an updated build status to all clients. - pub async fn update_build_status(&mut self, progress: f64, build_message: String) { + pub(crate) async fn update_build_status(&mut self, progress: f64, build_message: String) { if !matches!(self.build_status.get(), Status::Building { .. }) { return; } @@ -219,7 +219,7 @@ impl DevServer { } /// Sends hot reloadable changes to all clients. - pub async fn send_hotreload(&mut self, reload: HotReloadMsg) { + pub(crate) async fn send_hotreload(&mut self, reload: HotReloadMsg) { if !reload.assets.is_empty() { tracing::debug!("Hot reloading assets {:?}", reload.assets); } @@ -240,7 +240,7 @@ impl DevServer { } /// Wait for new clients to be connected and then save them - pub async fn wait(&mut self) -> ServeUpdate { + pub(crate) async fn wait(&mut self) -> ServeUpdate { let mut new_hot_reload_socket = self.new_hot_reload_sockets.next(); let mut new_build_status_socket = self.new_build_status_sockets.next(); let mut new_message = self @@ -290,7 +290,7 @@ impl DevServer { } /// Converts a `cargo` error to HTML and sends it to clients. - pub async fn send_build_error(&mut self, error: Error) { + pub(crate) async fn send_build_error(&mut self, error: Error) { let error = error.to_string(); self.build_status.set(Status::BuildError { error: ansi_to_html::convert(&error).unwrap_or(error), @@ -299,19 +299,19 @@ impl DevServer { } /// Tells all clients that a full rebuild has started. - pub async fn send_reload_start(&mut self) { + pub(crate) async fn send_reload_start(&mut self) { self.send_devserver_message(DevserverMsg::FullReloadStart) .await; } /// Tells all clients that a full rebuild has failed. - pub async fn send_reload_failed(&mut self) { + pub(crate) async fn send_reload_failed(&mut self) { self.send_devserver_message(DevserverMsg::FullReloadFailed) .await; } /// Tells all clients to reload if possible for new changes. - pub async fn send_reload_command(&mut self) { + pub(crate) async fn send_reload_command(&mut self) { self.build_status.set(Status::Ready); self.send_build_status().await; self.send_devserver_message(DevserverMsg::FullReloadCommand) @@ -319,7 +319,7 @@ impl DevServer { } /// Send a shutdown message to all connected clients. - pub async fn send_shutdown(&mut self) { + pub(crate) async fn send_shutdown(&mut self) { self.send_devserver_message(DevserverMsg::Shutdown).await; } @@ -332,7 +332,7 @@ impl DevServer { } } - pub async fn shutdown(&mut self) { + pub(crate) async fn shutdown(&mut self) { self.send_shutdown().await; for socket in self.hot_reload_sockets.drain(..) { _ = socket.close().await; @@ -340,7 +340,7 @@ impl DevServer { } /// Get the address the fullstack server should run on if we're serving a fullstack app - pub fn fullstack_address(&self) -> Option { + pub(crate) fn fullstack_address(&self) -> Option { self.fullstack_port .map(|port| SocketAddr::new(self.ip.ip(), port)) } @@ -514,14 +514,14 @@ fn no_cache( response } -pub fn insert_no_cache_headers(headers: &mut HeaderMap) { +pub(crate) fn insert_no_cache_headers(headers: &mut HeaderMap) { headers.insert(CACHE_CONTROL, HeaderValue::from_static("no-cache")); headers.insert(PRAGMA, HeaderValue::from_static("no-cache")); headers.insert(EXPIRES, HeaderValue::from_static("0")); } /// Returns an enum of rustls config -pub async fn get_rustls(web_config: &WebHttpsConfig) -> Result> { +pub(crate) async fn get_rustls(web_config: &WebHttpsConfig) -> Result> { if web_config.enabled != Some(true) { return Ok(None); } @@ -536,7 +536,7 @@ pub async fn get_rustls(web_config: &WebHttpsConfig) -> Result Result<(String, String)> { +pub(crate) async fn get_rustls_with_mkcert(web_config: &WebHttpsConfig) -> Result<(String, String)> { const DEFAULT_KEY_PATH: &str = "ssl/key.pem"; const DEFAULT_CERT_PATH: &str = "ssl/cert.pem"; @@ -585,7 +585,7 @@ pub async fn get_rustls_with_mkcert(web_config: &WebHttpsConfig) -> Result<(Stri Ok((cert_path, key_path)) } -pub fn get_rustls_without_mkcert(web_config: &WebHttpsConfig) -> Result<(String, String)> { +pub(crate) fn get_rustls_without_mkcert(web_config: &WebHttpsConfig) -> Result<(String, String)> { // get paths to cert & key if let (Some(key), Some(cert)) = (web_config.key_path.clone(), web_config.cert_path.clone()) { Ok((cert, key)) @@ -596,7 +596,7 @@ pub fn get_rustls_without_mkcert(web_config: &WebHttpsConfig) -> Result<(String, } /// Open the browser to the address -pub fn open_browser(base_path: Option, address: SocketAddr, https: bool) { +pub(crate) fn open_browser(base_path: Option, address: SocketAddr, https: bool) { let protocol = if https { "https" } else { "http" }; let base_path = match base_path.as_deref() { Some(base_path) => format!("/{}", base_path.trim_matches('/')), diff --git a/packages/cli/src/serve/tracer.rs b/packages/cli/src/serve/tracer.rs index 4c962f2028..e1ebfe27dc 100644 --- a/packages/cli/src/serve/tracer.rs +++ b/packages/cli/src/serve/tracer.rs @@ -16,12 +16,12 @@ use super::ServeUpdate; static TUI_ENABLED: AtomicBool = AtomicBool::new(false); static TUI_TX: OnceCell> = OnceCell::new(); -pub struct TraceController { - pub tui_rx: UnboundedReceiver, +pub(crate) struct TraceController { + pub(crate) tui_rx: UnboundedReceiver, } impl TraceController { - pub fn initialize() { + pub(crate) fn initialize() { // Start a tracing instance just for serving. // This ensures that any tracing we do while serving doesn't break the TUI itself, and instead is // redirected to the serve process. @@ -50,7 +50,7 @@ impl TraceController { sub.init(); } - pub fn start() -> Self { + pub(crate) fn start() -> Self { // Create writer controller and custom writer. let (tui_tx, tui_rx) = unbounded(); TUI_TX.set(tui_tx.clone()).unwrap(); @@ -60,13 +60,13 @@ impl TraceController { } /// Wait for the internal logger to send a message - pub async fn wait(&mut self) -> ServeUpdate { + pub(crate) async fn wait(&mut self) -> ServeUpdate { ServeUpdate::TracingLog { log: self.tui_rx.next().await.expect("tracer should never die"), } } - pub fn shutdown(&self) { + pub(crate) fn shutdown(&self) { TUI_ENABLED.store(false, Ordering::SeqCst); } } diff --git a/packages/cli/src/serve/update.rs b/packages/cli/src/serve/update.rs index 9d48b79f19..a677897521 100644 --- a/packages/cli/src/serve/update.rs +++ b/packages/cli/src/serve/update.rs @@ -7,7 +7,7 @@ use super::LogSource; /// One fat enum to rule them all.... /// /// Thanks to libraries like winit for the inspiration -pub enum ServeUpdate { +pub(crate) enum ServeUpdate { NewConnection, WsMessage(WsMessage), diff --git a/packages/cli/src/serve/watcher.rs b/packages/cli/src/serve/watcher.rs index 327c48702c..badc3b1e42 100644 --- a/packages/cli/src/serve/watcher.rs +++ b/packages/cli/src/serve/watcher.rs @@ -18,7 +18,7 @@ use std::{path::PathBuf, time::Duration}; /// /// This is where we do workspace discovery and recursively listen for changes in Rust files and asset /// directories. -pub struct Watcher { +pub(crate) struct Watcher { rx: UnboundedReceiver, krate: DioxusCrate, file_map: FileMap, @@ -30,7 +30,7 @@ pub struct Watcher { } impl Watcher { - pub fn start(serve: &ServeArgs, krate: &DioxusCrate) -> Self { + pub(crate) fn start(serve: &ServeArgs, krate: &DioxusCrate) -> Self { let (tx, rx) = futures_channel::mpsc::unbounded(); // Extend the watch path to include: @@ -139,7 +139,7 @@ impl Watcher { } /// Wait for changed files to be detected - pub async fn wait(&mut self) -> ServeUpdate { + pub(crate) async fn wait(&mut self) -> ServeUpdate { // Wait for the next file to change let mut changes: Vec<_> = self.rx.next().await.into_iter().collect(); @@ -199,7 +199,7 @@ impl Watcher { ServeUpdate::FilesChanged { files } } - pub fn attempt_hot_reload( + pub(crate) fn attempt_hot_reload( &mut self, modified_files: Vec, runner: &AppRunner, @@ -265,12 +265,12 @@ impl Watcher { } /// Get any hot reload changes that have been applied since the last full rebuild - pub fn applied_hot_reload_changes(&mut self) -> Option { + pub(crate) fn applied_hot_reload_changes(&mut self) -> Option { self.applied_hot_reload_message.clone() } /// Clear the hot reload changes. This should be called any time a new build is starting - pub fn clear_hot_reload_changes(&mut self) { + pub(crate) fn clear_hot_reload_changes(&mut self) { self.applied_hot_reload_message.take(); } diff --git a/packages/cli/src/settings.rs b/packages/cli/src/settings.rs index 6ec592984f..88faa325d1 100644 --- a/packages/cli/src/settings.rs +++ b/packages/cli/src/settings.rs @@ -18,26 +18,26 @@ const GLOBAL_SETTINGS_FILE_NAME: &str = "dioxus/settings.toml"; /// /// This allows users to control the cli settings with ease. #[derive(Debug, Clone, Serialize, Deserialize, Default)] -pub struct CliSettings { +pub(crate) struct CliSettings { /// Describes whether hot reload should always be on. - pub always_hot_reload: Option, + pub(crate) always_hot_reload: Option, /// Describes whether the CLI should always open the browser for Web targets. - pub always_open_browser: Option, + pub(crate) always_open_browser: Option, /// Describes whether desktop apps in development will be pinned always-on-top. - pub always_on_top: Option, + pub(crate) always_on_top: Option, /// Describes the interval in seconds that the CLI should poll for file changes on WSL. #[serde(default = "default_wsl_file_poll_interval")] - pub wsl_file_poll_interval: Option, + pub(crate) wsl_file_poll_interval: Option, } impl CliSettings { /// Load the settings from the local, global, or default config in that order - pub fn load() -> Self { + pub(crate) fn load() -> Self { Self::from_global().unwrap_or_default() } /// Get the current settings structure from global. - pub fn from_global() -> Option { + pub(crate) fn from_global() -> Option { let Some(path) = dirs::data_local_dir() else { warn!("failed to get local data directory, some config keys may be missing"); return None; @@ -63,7 +63,7 @@ impl CliSettings { /// Save the current structure to the global settings toml. /// This does not save to project-level settings. - pub fn save(self) -> Result { + pub(crate) fn save(self) -> Result { let path = Self::get_settings_path().ok_or_else(|| { error!("failed to get settings path"); CrateConfigError::Io(Error::new( @@ -99,7 +99,7 @@ impl CliSettings { } /// Get the path to the settings toml file. - pub fn get_settings_path() -> Option { + pub(crate) fn get_settings_path() -> Option { let Some(path) = dirs::data_local_dir() else { warn!("failed to get local data directory, some config keys may be missing"); return None; @@ -109,7 +109,9 @@ impl CliSettings { } /// Modify the settings toml file - pub fn modify_settings(with: impl FnOnce(&mut CliSettings)) -> Result<(), CrateConfigError> { + pub(crate) fn modify_settings( + with: impl FnOnce(&mut CliSettings), + ) -> Result<(), CrateConfigError> { let mut settings = Self::load(); with(&mut settings); settings.save()?; diff --git a/packages/cli/src/tools.rs b/packages/cli/src/tools.rs index a5983e75be..78112423af 100644 --- a/packages/cli/src/tools.rs +++ b/packages/cli/src/tools.rs @@ -12,12 +12,12 @@ use tar::Archive; use tokio::io::AsyncWriteExt; #[derive(Debug, PartialEq, Eq)] -pub enum Tool { +pub(crate) enum Tool { Sass, Tailwind, } -pub fn app_path() -> PathBuf { +pub(crate) fn app_path() -> PathBuf { let data_local = dirs::data_local_dir().unwrap(); let dioxus_dir = data_local.join("dioxus"); if !dioxus_dir.is_dir() { @@ -26,7 +26,7 @@ pub fn app_path() -> PathBuf { dioxus_dir } -pub fn temp_path() -> PathBuf { +pub(crate) fn temp_path() -> PathBuf { let app_path = app_path(); let temp_path = app_path.join("temp"); if !temp_path.is_dir() { @@ -35,7 +35,7 @@ pub fn temp_path() -> PathBuf { temp_path } -pub fn clone_repo(dir: &Path, url: &str) -> anyhow::Result<()> { +pub(crate) fn clone_repo(dir: &Path, url: &str) -> anyhow::Result<()> { let target_dir = dir.parent().unwrap(); let dir_name = dir.file_name().unwrap(); @@ -51,7 +51,7 @@ pub fn clone_repo(dir: &Path, url: &str) -> anyhow::Result<()> { Ok(()) } -pub fn tools_path() -> PathBuf { +pub(crate) fn tools_path() -> PathBuf { let app_path = app_path(); let temp_path = app_path.join("tools"); if !temp_path.is_dir() { @@ -63,7 +63,7 @@ pub fn tools_path() -> PathBuf { #[allow(clippy::should_implement_trait)] impl Tool { /// from str to tool enum - pub fn from_str(name: &str) -> Option { + pub(crate) fn from_str(name: &str) -> Option { match name { "sass" => Some(Self::Sass), "tailwindcss" => Some(Self::Tailwind), @@ -72,7 +72,7 @@ impl Tool { } /// get current tool name str - pub fn name(&self) -> &str { + pub(crate) fn name(&self) -> &str { match self { Self::Sass => "sass", Self::Tailwind => "tailwindcss", @@ -80,7 +80,7 @@ impl Tool { } /// get tool bin dir path - pub fn bin_path(&self) -> &str { + pub(crate) fn bin_path(&self) -> &str { match self { Self::Sass => ".", Self::Tailwind => ".", @@ -88,7 +88,7 @@ impl Tool { } /// get target platform - pub fn target_platform(&self) -> &str { + pub(crate) fn target_platform(&self) -> &str { match self { Self::Sass => { if cfg!(target_os = "windows") { @@ -116,7 +116,7 @@ impl Tool { } /// get tool version - pub fn tool_version(&self) -> &str { + pub(crate) fn tool_version(&self) -> &str { match self { Self::Sass => "1.51.0", Self::Tailwind => "v3.1.6", @@ -124,7 +124,7 @@ impl Tool { } /// get tool package download url - pub fn download_url(&self) -> String { + pub(crate) fn download_url(&self) -> String { match self { Self::Sass => { format!( @@ -150,7 +150,7 @@ impl Tool { } /// get package extension name - pub fn extension(&self) -> &str { + pub(crate) fn extension(&self) -> &str { match self { Self::Sass => { if cfg!(target_os = "windows") { @@ -164,17 +164,17 @@ impl Tool { } /// check tool state - pub fn is_installed(&self) -> bool { + pub(crate) fn is_installed(&self) -> bool { tools_path().join(self.name()).is_dir() } /// get download temp path - pub fn temp_out_path(&self) -> PathBuf { + pub(crate) fn temp_out_path(&self) -> PathBuf { temp_path().join(format!("{}-tool.tmp", self.name())) } /// start to download package - pub async fn download_package(&self) -> anyhow::Result { + pub(crate) async fn download_package(&self) -> anyhow::Result { let download_url = self.download_url(); let temp_out = self.temp_out_path(); let mut file = tokio::fs::File::create(&temp_out) @@ -193,7 +193,7 @@ impl Tool { } /// start to install package - pub async fn install_package(&self) -> anyhow::Result<()> { + pub(crate) async fn install_package(&self) -> anyhow::Result<()> { let temp_path = self.temp_out_path(); let tool_path = tools_path(); @@ -248,7 +248,7 @@ impl Tool { Ok(()) } - pub fn call(&self, command: &str, args: Vec<&str>) -> anyhow::Result> { + pub(crate) fn call(&self, command: &str, args: Vec<&str>) -> anyhow::Result> { let bin_path = tools_path().join(self.name()).join(self.bin_path()); let command_file = match self { @@ -283,7 +283,7 @@ impl Tool { } } -pub fn extract_zip(file: &Path, target: &Path) -> anyhow::Result<()> { +pub(crate) fn extract_zip(file: &Path, target: &Path) -> anyhow::Result<()> { let zip_file = std::fs::File::open(file)?; let mut zip = zip::ZipArchive::new(zip_file)?; From f872d7e8d9cddb65af38caa7ecdd7d841c13a863 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 5 Sep 2024 00:43:02 -0700 Subject: [PATCH 070/139] remove tools --- packages/cli/src/main.rs | 1 - packages/cli/src/tools.rs | 369 ---------------------- packages/cli/tests/fixtures/dangerous.zip | Bin 127 -> 0 bytes packages/cli/tests/fixtures/test.zip | Bin 315 -> 0 bytes 4 files changed, 370 deletions(-) delete mode 100644 packages/cli/src/tools.rs delete mode 100644 packages/cli/tests/fixtures/dangerous.zip delete mode 100644 packages/cli/tests/fixtures/test.zip diff --git a/packages/cli/src/main.rs b/packages/cli/src/main.rs index f669c7c777..8ca89ef65e 100644 --- a/packages/cli/src/main.rs +++ b/packages/cli/src/main.rs @@ -15,7 +15,6 @@ pub(crate) mod fastfs; pub(crate) mod metadata; pub(crate) mod serve; pub(crate) mod settings; -pub(crate) mod tools; pub(crate) use cli::*; pub(crate) use dioxus_crate::*; diff --git a/packages/cli/src/tools.rs b/packages/cli/src/tools.rs deleted file mode 100644 index 78112423af..0000000000 --- a/packages/cli/src/tools.rs +++ /dev/null @@ -1,369 +0,0 @@ -use std::{ - fs::{create_dir_all, File}, - io::{ErrorKind, Read, Write}, - path::{Path, PathBuf}, - process::Command, -}; - -use anyhow::Context; -use flate2::read::GzDecoder; -use futures_util::StreamExt; -use tar::Archive; -use tokio::io::AsyncWriteExt; - -#[derive(Debug, PartialEq, Eq)] -pub(crate) enum Tool { - Sass, - Tailwind, -} - -pub(crate) fn app_path() -> PathBuf { - let data_local = dirs::data_local_dir().unwrap(); - let dioxus_dir = data_local.join("dioxus"); - if !dioxus_dir.is_dir() { - create_dir_all(&dioxus_dir).unwrap(); - } - dioxus_dir -} - -pub(crate) fn temp_path() -> PathBuf { - let app_path = app_path(); - let temp_path = app_path.join("temp"); - if !temp_path.is_dir() { - create_dir_all(&temp_path).unwrap(); - } - temp_path -} - -pub(crate) fn clone_repo(dir: &Path, url: &str) -> anyhow::Result<()> { - let target_dir = dir.parent().unwrap(); - let dir_name = dir.file_name().unwrap(); - - let mut cmd = Command::new("git"); - let cmd = cmd.current_dir(target_dir); - let res = cmd.arg("clone").arg(url).arg(dir_name).output(); - if let Err(err) = res { - if ErrorKind::NotFound == err.kind() { - tracing::warn!("Git program not found. Hint: Install git or check $PATH."); - return Err(err.into()); - } - } - Ok(()) -} - -pub(crate) fn tools_path() -> PathBuf { - let app_path = app_path(); - let temp_path = app_path.join("tools"); - if !temp_path.is_dir() { - create_dir_all(&temp_path).unwrap(); - } - temp_path -} - -#[allow(clippy::should_implement_trait)] -impl Tool { - /// from str to tool enum - pub(crate) fn from_str(name: &str) -> Option { - match name { - "sass" => Some(Self::Sass), - "tailwindcss" => Some(Self::Tailwind), - _ => None, - } - } - - /// get current tool name str - pub(crate) fn name(&self) -> &str { - match self { - Self::Sass => "sass", - Self::Tailwind => "tailwindcss", - } - } - - /// get tool bin dir path - pub(crate) fn bin_path(&self) -> &str { - match self { - Self::Sass => ".", - Self::Tailwind => ".", - } - } - - /// get target platform - pub(crate) fn target_platform(&self) -> &str { - match self { - Self::Sass => { - if cfg!(target_os = "windows") { - "windows" - } else if cfg!(target_os = "macos") { - "macos" - } else if cfg!(target_os = "linux") { - "linux" - } else { - panic!("unsupported platformm"); - } - } - Self::Tailwind => { - if cfg!(target_os = "windows") { - "windows" - } else if cfg!(target_os = "macos") { - "macos" - } else if cfg!(target_os = "linux") { - "linux" - } else { - panic!("unsupported platformm"); - } - } - } - } - - /// get tool version - pub(crate) fn tool_version(&self) -> &str { - match self { - Self::Sass => "1.51.0", - Self::Tailwind => "v3.1.6", - } - } - - /// get tool package download url - pub(crate) fn download_url(&self) -> String { - match self { - Self::Sass => { - format!( - "https://github.com/sass/dart-sass/releases/download/{version}/dart-sass-{version}-{target}-x64.{extension}", - version = self.tool_version(), - target = self.target_platform(), - extension = self.extension() - ) - } - Self::Tailwind => { - let windows_extension = match self.target_platform() { - "windows" => ".exe", - _ => "", - }; - format!( - "https://github.com/tailwindlabs/tailwindcss/releases/download/{version}/tailwindcss-{target}-x64{optional_ext}", - version = self.tool_version(), - target = self.target_platform(), - optional_ext = windows_extension - ) - } - } - } - - /// get package extension name - pub(crate) fn extension(&self) -> &str { - match self { - Self::Sass => { - if cfg!(target_os = "windows") { - "zip" - } else { - "tar.gz" - } - } - Self::Tailwind => "bin", - } - } - - /// check tool state - pub(crate) fn is_installed(&self) -> bool { - tools_path().join(self.name()).is_dir() - } - - /// get download temp path - pub(crate) fn temp_out_path(&self) -> PathBuf { - temp_path().join(format!("{}-tool.tmp", self.name())) - } - - /// start to download package - pub(crate) async fn download_package(&self) -> anyhow::Result { - let download_url = self.download_url(); - let temp_out = self.temp_out_path(); - let mut file = tokio::fs::File::create(&temp_out) - .await - .context("failed creating temporary output file")?; - - let resp = reqwest::get(download_url).await.unwrap(); - - let mut res_bytes = resp.bytes_stream(); - while let Some(chunk_res) = res_bytes.next().await { - let chunk = chunk_res.context("error reading chunk from download")?; - let _ = file.write(chunk.as_ref()).await; - } - // tracing::info!("temp file path: {:?}", temp_out); - Ok(temp_out) - } - - /// start to install package - pub(crate) async fn install_package(&self) -> anyhow::Result<()> { - let temp_path = self.temp_out_path(); - let tool_path = tools_path(); - - let dir_name = match self { - Self::Sass => "dart-sass".to_string(), - Self::Tailwind => self.name().to_string(), - }; - - if self.extension() == "tar.gz" { - let tar_gz = File::open(temp_path)?; - let tar = GzDecoder::new(tar_gz); - let mut archive = Archive::new(tar); - archive.unpack(&tool_path)?; - std::fs::rename(tool_path.join(dir_name), tool_path.join(self.name()))?; - } else if self.extension() == "zip" { - // decompress the `zip` file - extract_zip(&temp_path, &tool_path)?; - std::fs::rename(tool_path.join(dir_name), tool_path.join(self.name()))?; - } else if self.extension() == "bin" { - let bin_path = match self.target_platform() { - "windows" => tool_path.join(&dir_name).join(self.name()).join(".exe"), - _ => tool_path.join(&dir_name).join(self.name()), - }; - // Manually creating tool directory because we directly download the binary via Github - std::fs::create_dir(tool_path.join(dir_name))?; - - let mut final_file = std::fs::File::create(&bin_path)?; - let mut temp_file = File::open(&temp_path)?; - let mut content = Vec::new(); - - temp_file.read_to_end(&mut content)?; - final_file.write_all(&content)?; - - if self.target_platform() == "linux" { - // This code does not update permissions idk why - // let mut perms = final_file.metadata()?.permissions(); - // perms.set_mode(0o744); - - // Adding to the binary execution rights with "chmod" - let mut command = Command::new("chmod"); - - let _ = command - .args(vec!["+x", bin_path.to_str().unwrap()]) - .stdout(std::process::Stdio::inherit()) - .stderr(std::process::Stdio::inherit()) - .output()?; - } - - std::fs::remove_file(&temp_path)?; - } - - Ok(()) - } - - pub(crate) fn call(&self, command: &str, args: Vec<&str>) -> anyhow::Result> { - let bin_path = tools_path().join(self.name()).join(self.bin_path()); - - let command_file = match self { - Tool::Sass => { - if cfg!(target_os = "windows") { - format!("{}.bat", command) - } else { - command.to_string() - } - } - Tool::Tailwind => { - if cfg!(target_os = "windows") { - format!("{}.exe", command) - } else { - command.to_string() - } - } - }; - - if !bin_path.join(&command_file).is_file() { - return Err(anyhow::anyhow!("Command file not found.")); - } - - let mut command = Command::new(bin_path.join(&command_file).to_str().unwrap()); - - let output = command - .args(&args[..]) - .stdout(std::process::Stdio::inherit()) - .stderr(std::process::Stdio::inherit()) - .output()?; - Ok(output.stdout) - } -} - -pub(crate) fn extract_zip(file: &Path, target: &Path) -> anyhow::Result<()> { - let zip_file = std::fs::File::open(file)?; - let mut zip = zip::ZipArchive::new(zip_file)?; - - if !target.exists() { - std::fs::create_dir_all(target)?; - } - - for i in 0..zip.len() { - let mut zip_entry = zip.by_index(i)?; - - // check for dangerous paths - // see https://docs.rs/zip/latest/zip/read/struct.ZipFile.html#warnings - let Some(enclosed_name) = zip_entry.enclosed_name() else { - return Err(anyhow::anyhow!( - "Refusing to unpack zip entry with potentially dangerous path: zip={} entry={:?}", - file.display(), - zip_entry.name() - )); - }; - - let output_path = target.join(enclosed_name); - if zip_entry.is_dir() { - std::fs::create_dir_all(output_path)?; - } else { - // create parent dirs if needed - if let Some(parent) = output_path.parent() { - std::fs::create_dir_all(parent)?; - } - - // extract file - let mut target_file = if !output_path.exists() { - std::fs::File::create(output_path)? - } else { - std::fs::File::open(output_path)? - }; - let _num = std::io::copy(&mut zip_entry, &mut target_file)?; - } - } - - Ok(()) -} - -#[cfg(test)] -mod test { - use super::*; - use tempfile::tempdir; - - #[test] - fn test_extract_zip() -> anyhow::Result<()> { - let path = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()) - .join("tests/fixtures/test.zip"); - let temp_dir = tempdir()?; - let temp_path = temp_dir.path(); - - extract_zip(path.as_path(), temp_path)?; - - let expected_files = vec!["file1.txt", "file2.txt", "dir/file3.txt"]; - for file in expected_files { - let path = temp_path.join(file); - assert!(path.exists(), "File not found: {:?}", path); - } - - Ok(()) - } - - #[test] - fn test_extract_zip_dangerous_path() -> anyhow::Result<()> { - let path = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()) - .join("tests/fixtures/dangerous.zip"); - let temp_dir = tempdir()?; - let temp_path = temp_dir.path(); - - let result = extract_zip(path.as_path(), temp_path); - - let err = result.unwrap_err(); - assert!(err - .to_string() - .contains("Refusing to unpack zip entry with potentially dangerous path: zip=")); - assert!(err.to_string().contains("entry=\"/etc/passwd\"")); - - Ok(()) - } -} diff --git a/packages/cli/tests/fixtures/dangerous.zip b/packages/cli/tests/fixtures/dangerous.zip deleted file mode 100644 index 09ff13d6357a4fc55c07488c5c5ed11c7d7bbc16..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 127 zcmWIWW@Zs#0D-cVEa43KoA$8-*&xgf#QLcv$@&F}#l__*DT#UMsYQwb-i%Bl%(!&` cHGx3`BZxxMAK=Z(1`=WfLQ5cR0OBwJ0N|Sx-~a#s diff --git a/packages/cli/tests/fixtures/test.zip b/packages/cli/tests/fixtures/test.zip deleted file mode 100644 index bb90b81addaf5ca48b42ee9f7cbdfae67fddbfcc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 315 zcmWIWW@Zs#0DUj5El|ol%1X5>& zP?ri+H@ojrE=(OS(7cq)B7KmV#vrxE0p5&EBFwmL2WkL=29Oa5w;*f7<`{@328ISk k4X8G-(~z}ca~nci0Fa5|(g1H(Hjp$k5UvE$H6RWH07iW_^Z)<= From b9ea5cddded691706337a4afb1c0c0265c7c5d16 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 5 Sep 2024 01:06:34 -0700 Subject: [PATCH 071/139] bit more polish to cli --- packages/cli/src/builder/builder.rs | 4 +- packages/cli/src/builder/progress.rs | 18 +-- packages/cli/src/builder/web.rs | 6 +- packages/cli/src/serve/handle.rs | 159 +++++++++++++++++++++++++++ packages/cli/src/serve/mod.rs | 8 +- packages/cli/src/serve/output.rs | 152 +++++++------------------ packages/cli/src/serve/runner.rs | 155 +------------------------- packages/cli/src/serve/server.rs | 100 ++++++++--------- packages/cli/src/serve/update.rs | 5 +- 9 files changed, 273 insertions(+), 334 deletions(-) create mode 100644 packages/cli/src/serve/handle.rs diff --git a/packages/cli/src/builder/builder.rs b/packages/cli/src/builder/builder.rs index e3bb3893e5..80c870b3c0 100644 --- a/packages/cli/src/builder/builder.rs +++ b/packages/cli/src/builder/builder.rs @@ -3,7 +3,7 @@ use crate::builder::*; use crate::dioxus_crate::DioxusCrate; use crate::Result; use futures_util::StreamExt; -use progress::{ProgressRx, ProgressTx, UpdateBuildProgress}; +use progress::{BuildUpdateProgress, ProgressRx, ProgressTx}; use tokio::task::JoinSet; /// A handle to ongoing builds and then the spawned tasks themselves @@ -19,7 +19,7 @@ pub(crate) struct Builder { } pub(crate) enum BuildUpdate { - Progress(UpdateBuildProgress), + Progress(BuildUpdateProgress), BuildReady { target: Platform, diff --git a/packages/cli/src/builder/progress.rs b/packages/cli/src/builder/progress.rs index e417667cd7..68cc5050a7 100644 --- a/packages/cli/src/builder/progress.rs +++ b/packages/cli/src/builder/progress.rs @@ -19,7 +19,7 @@ impl BuildRequest { // Extract the unit count of the crate graph so build_cargo has more accurate data let crate_count = self.get_unit_count_estimate().await; - _ = self.progress.unbounded_send(UpdateBuildProgress { + _ = self.progress.unbounded_send(BuildUpdateProgress { stage: Stage::Compiling, update: UpdateStage::Start, platform: self.platform(), @@ -76,7 +76,7 @@ impl BuildRequest { match message { Message::CompilerMessage(msg) => { let message = msg.message; - _ = self.progress.unbounded_send(UpdateBuildProgress { + _ = self.progress.unbounded_send(BuildUpdateProgress { stage: Stage::Compiling, update: UpdateStage::AddMessage(message.clone().into()), platform: self.platform(), @@ -109,7 +109,7 @@ impl BuildRequest { output_location = Some(executable.into()); } else { let build_progress = units_compiled as f64 / crate_count as f64; - _ = self.progress.unbounded_send(UpdateBuildProgress { + _ = self.progress.unbounded_send(BuildUpdateProgress { platform: self.platform(), stage: Stage::Compiling, update: UpdateStage::SetProgress((build_progress).clamp(0.0, 1.00)), @@ -125,7 +125,7 @@ impl BuildRequest { } } Message::TextLine(line) => { - _ = self.progress.unbounded_send(UpdateBuildProgress { + _ = self.progress.unbounded_send(BuildUpdateProgress { platform: self.platform(), stage: Stage::Compiling, update: UpdateStage::AddMessage(BuildMessage { @@ -194,7 +194,7 @@ impl BuildRequest { pub(crate) fn status_build_finished(&self) { tracing::info!("🚩 Build completed: [{}]", self.krate.out_dir().display()); - _ = self.progress.unbounded_send(UpdateBuildProgress { + _ = self.progress.unbounded_send(BuildUpdateProgress { platform: self.platform(), stage: Stage::Finished, update: UpdateStage::Start, @@ -227,8 +227,8 @@ impl BuildRequest { } } -pub(crate) type ProgressTx = UnboundedSender; -pub(crate) type ProgressRx = UnboundedReceiver; +pub(crate) type ProgressTx = UnboundedSender; +pub(crate) type ProgressRx = UnboundedReceiver; #[derive(Default, Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Copy)] pub(crate) enum Stage { @@ -263,13 +263,13 @@ impl std::fmt::Display for Stage { } #[derive(Debug, Clone)] -pub(crate) struct UpdateBuildProgress { +pub(crate) struct BuildUpdateProgress { pub(crate) stage: Stage, pub(crate) update: UpdateStage, pub(crate) platform: Platform, } -impl UpdateBuildProgress { +impl BuildUpdateProgress { pub(crate) fn to_std_out(&self) { match &self.update { UpdateStage::Start => println!("--- {} ---", self.stage), diff --git a/packages/cli/src/builder/web.rs b/packages/cli/src/builder/web.rs index 2b2d362f85..b550585e16 100644 --- a/packages/cli/src/builder/web.rs +++ b/packages/cli/src/builder/web.rs @@ -1,6 +1,6 @@ use super::{BuildRequest, Platform}; use crate::builder::progress::{ - BuildMessage, MessageSource, MessageType, Stage, UpdateBuildProgress, UpdateStage, + BuildMessage, MessageSource, MessageType, Stage, BuildUpdateProgress, UpdateStage, }; use crate::error::{Error, Result}; use anyhow::Context; @@ -101,7 +101,7 @@ impl BuildRequest { if let Ok(wasm_check_command) = Command::new("rustup").args(["show"]).output().await { let wasm_check_output = String::from_utf8(wasm_check_command.stdout).unwrap(); if !wasm_check_output.contains("wasm32-unknown-unknown") { - _ = self.progress.unbounded_send(UpdateBuildProgress { + _ = self.progress.unbounded_send(BuildUpdateProgress { stage: Stage::InstallingWasmTooling, update: UpdateStage::Start, platform: self.platform(), @@ -317,7 +317,7 @@ impl BuildRequest { "{RESOURCE_DEPRECATION_MESSAGE}\nTo migrate to head components, remove `{section_name}` and include the following rsx in your root component:\n```rust\n{replacement_components}\n```" ); - _ = self.progress.unbounded_send(UpdateBuildProgress { + _ = self.progress.unbounded_send(BuildUpdateProgress { platform: self.platform(), stage: Stage::OptimizingWasm, update: UpdateStage::AddMessage(BuildMessage { diff --git a/packages/cli/src/serve/handle.rs b/packages/cli/src/serve/handle.rs new file mode 100644 index 0000000000..ea05b6605e --- /dev/null +++ b/packages/cli/src/serve/handle.rs @@ -0,0 +1,159 @@ +use crate::builder::{AppBundle, Platform}; +use crate::Result; +use std::{net::SocketAddr, path::PathBuf, process::Stdio}; +use tokio::{ + io::AsyncBufReadExt, + process::{Child, Command}, +}; +use tokio::{ + io::{BufReader, Lines}, + process::{ChildStderr, ChildStdout}, +}; +use uuid::Uuid; + +/// A handle to a running app +pub(crate) struct AppHandle { + pub(crate) _id: Uuid, + pub(crate) app: AppBundle, + pub(crate) executable: PathBuf, + pub(crate) child: Option, + pub(crate) stdout: Option>>, + pub(crate) stderr: Option>>, +} + +impl AppHandle { + pub async fn start( + app: AppBundle, + devserver_ip: SocketAddr, + fullstack_address: Option, + ) -> Result { + let platform = app.build.platform(); + let ip = devserver_ip.to_string(); + + if platform == Platform::Server { + tracing::trace!( + "Proxying fullstack server from port {:?}", + fullstack_address + ); + } + + let work_dir = std::env::temp_dir(); + let executable = app.finish(work_dir).await?; + + let mut handle = AppHandle { + app, + executable, + _id: Uuid::new_v4(), + child: None, + stderr: None, + stdout: None, + }; + + // open the exe with some arguments/envvars/etc + // we're going to try and configure this binary from the environment, if we can + // + // web can't be configured like this, so instead, we'll need to plumb a meta tag into the + // index.html during dev + match handle.app.build.platform() { + Platform::Web => {} + Platform::Desktop => { + let mut cmd = Command::new(handle.executable.clone()); + cmd.env( + dioxus_runtime_config::FULLSTACK_ADDRESS_ENV, + fullstack_address + .as_ref() + .map(|addr| addr.to_string()) + .unwrap_or_else(|| "127.0.0.1:8080".to_string()), + ) + .env( + dioxus_runtime_config::IOS_DEVSERVER_ADDR_ENV, + format!("ws://{}/_dioxus", ip), + ) + .env( + dioxus_runtime_config::DEVSERVER_RAW_ADDR_ENV, + format!("ws://{}/_dioxus", ip), + ) + .env("CARGO_MANIFEST_DIR", handle.app.build.krate.crate_dir()) + .env( + "SIMCTL_CHILD_CARGO_MANIFEST_DIR", + handle.app.build.krate.crate_dir(), + ) + .stderr(Stdio::piped()) + .stdout(Stdio::piped()) + .kill_on_drop(true); + + let mut child = cmd.spawn()?; + let stdout = BufReader::new(child.stdout.take().unwrap()); + let stderr = BufReader::new(child.stderr.take().unwrap()); + handle.stdout = Some(stdout.lines()); + handle.stderr = Some(stderr.lines()); + handle.child = Some(child); + } + Platform::Ios => {} + Platform::Android => {} + Platform::Server => {} + Platform::Liveview => {} + } + + Ok(handle) + } + /// Update an asset in the running apps + /// + /// Might need to upload the asset to the simulator or overwrite it within the bundle + /// + /// Returns the name of the asset in the bundle if it exists + pub(crate) fn hotreload_asset(&self, path: &PathBuf) -> Option { + let resource = self.app.assets.assets.get(path).cloned()?; + + self.app + .assets + .copy_asset_to(&self.app.asset_dir(), path, false, false); + + Some(resource.bundled.into()) + } + + #[allow(unused)] + fn open_bundled_ios_app(&self, build: &AppBundle) -> std::io::Result> { + // command = "xcrun" + // args = [ + // "simctl", + // "install", + // "booted", + // "target/aarch64-apple-ios-sim/debug/bundle/ios/DioxusApp.app", + // ] + + // [tasks.run_ios_sim] + // args = ["simctl", "launch", "--console", "booted", "com.dioxuslabs"] + // command = "xcrun" + // dependencies = ["build_ios_sim", "install_ios_sim"] + + // [tasks.serve-sim] + // dependencies = ["build_ios_sim", "install_ios_sim", "run_ios_sim"] + + // APP_PATH="target/aarch64-apple-ios/debug/bundle/ios/DioxusApp.app" + + // # get the device id by jq-ing the json of the device list + // xcrun devicectl list devices --json-output target/deviceid.json + // DEVICE_UUID=$(jq -r '.result.devices[0].identifier' target/deviceid.json) + + // xcrun devicectl device install app --device "${DEVICE_UUID}" "${APP_PATH}" --json-output target/xcrun.json + + // # get the installation url by jq-ing the json of the device install + // INSTALLATION_URL=$(jq -r '.result.installedApplications[0].installationURL' target/xcrun.json) + + // # launch the app + // # todo: we can just background it immediately and then pick it up for loading its logs + // xcrun devicectl device process launch --device "${DEVICE_UUID}" "${INSTALLATION_URL}" + + // # # launch the app and put it in background + // # xcrun devicectl device process launch --no-activate --verbose --device "${DEVICE_UUID}" "${INSTALLATION_URL}" --json-output "${XCRUN_DEVICE_PROCESS_LAUNCH_LOG_DIR}" + + // # # Extract background PID of status app + // # STATUS_PID=$(jq -r '.result.process.processIdentifier' "${XCRUN_DEVICE_PROCESS_LAUNCH_LOG_DIR}") + // # "${GIT_ROOT}/scripts/wait-for-metro-port.sh" 2>&1 + + // # # now that metro is ready, resume the app from background + // # xcrun devicectl device process resume --device "${DEVICE_UUID}" --pid "${STATUS_PID}" > "${XCRUN_DEVICE_PROCESS_RESUME_LOG_DIR}" 2>&1 + todo!("Open mobile apps") + } +} diff --git a/packages/cli/src/serve/mod.rs b/packages/cli/src/serve/mod.rs index be5f6721b2..1ac9150ba2 100644 --- a/packages/cli/src/serve/mod.rs +++ b/packages/cli/src/serve/mod.rs @@ -1,10 +1,11 @@ -use crate::builder::{BuildUpdate, Builder, Platform, Stage, UpdateBuildProgress, UpdateStage}; +use crate::builder::{BuildUpdate, BuildUpdateProgress, Builder, Platform, Stage, UpdateStage}; use crate::cli::serve::ServeArgs; use crate::DioxusCrate; use crate::Result; use std::ops::ControlFlow; mod detect; +mod handle; mod hot_reloading_file_map; mod logs_tab; mod output; @@ -15,6 +16,7 @@ mod tracer; mod update; mod watcher; +use handle::*; use output::*; use runner::*; use server::*; @@ -161,14 +163,14 @@ async fn handle_msg( match update { // Send rebuild start message. - UpdateBuildProgress { + BuildUpdateProgress { stage: Stage::Compiling, update: UpdateStage::Start, platform: _, } => devserver.send_reload_start().await, // Send rebuild failed message. - UpdateBuildProgress { + BuildUpdateProgress { stage: Stage::Finished, update: UpdateStage::Failed(_), platform: _, diff --git a/packages/cli/src/serve/output.rs b/packages/cli/src/serve/output.rs index 07bfba9be3..c38ba3fcfa 100644 --- a/packages/cli/src/serve/output.rs +++ b/packages/cli/src/serve/output.rs @@ -9,15 +9,15 @@ use crossterm::{ ExecutableCommand, }; use dioxus_devtools_types::ClientMsg; -use futures_util::{Future, FutureExt, StreamExt}; +use futures_util::StreamExt; use ratatui::{prelude::*, widgets::*, TerminalOptions, Viewport}; use std::{ cell::RefCell, - collections::{HashMap, HashSet}, + collections::HashMap, fmt::Display, io::{self, stdout}, rc::Rc, - time::{Duration, Instant}, + time::Instant, }; use tracing::Level; @@ -126,50 +126,31 @@ impl Output { /// Add a message from stderr to the logs pub(crate) fn push_stderr(&mut self, platform: Platform, stderr: String) { - - // self.set_tab(Tab::BuildLog); - - // self.running_apps - // .get_mut(&platform) - // .unwrap() - // .output - // .as_mut() - // .unwrap() - // .stderr_line - // .push_str(&stderr); - // self.build_progress - // .build_logs - // .get_mut(&platform) - // .unwrap() - // .messages - // .push(BuildMessage { - // level: Level::ERROR, - // message: MessageType::Text(stderr), - // source: MessageSource::App, - // }); + self.set_tab(Tab::BuildLog); + self.build_progress + .build_logs + .get_mut(&platform) + .unwrap() + .messages + .push(BuildMessage { + level: Level::ERROR, + message: MessageType::Text(stderr), + source: MessageSource::App, + }); } /// Add a message from stdout to the logs pub(crate) fn push_stdout(&mut self, platform: Platform, stdout: String) { - - // self.running_apps - // .get_mut(&platform) - // .unwrap() - // .output - // .as_mut() - // .unwrap() - // .stdout_line - // .push_str(&stdout); - // self.build_progress - // .build_logs - // .get_mut(&platform) - // .unwrap() - // .messages - // .push(BuildMessage { - // level: Level::INFO, - // message: MessageType::Text(stdout), - // source: MessageSource::App, - // }); + self.build_progress + .build_logs + .get_mut(&platform) + .unwrap() + .messages + .push(BuildMessage { + level: Level::INFO, + message: MessageType::Text(stdout), + source: MessageSource::App, + }); } /// Wait for either the ctrl_c handler or the next event @@ -198,36 +179,6 @@ impl Output { /// Handle an input event, returning `true` if the event should cause the program to restart. pub(crate) fn handle_input(&mut self, input: Event) -> io::Result { - // let mut events = vec![event]; - - // // Collect all the events within the next 10ms in one stream - // let collect_events = async { - // loop { - // let Some(Ok(next)) = self.events.as_mut().unwrap().next().await else { - // break; - // }; - // events.push(next); - // } - // }; - // tokio::select! { - // _ = collect_events => {}, - // _ = tokio::time::sleep(Duration::from_millis(10)) => {} - // } - - // // Debounce events within the same frame - // let mut handled = HashSet::new(); - // for event in events { - // if !handled.contains(&event) { - // if self.handle_input(event.clone())? { - // // Restart the running app. - // return Ok(true); - // } - // handled.insert(event); - // } - // } - - // Ok(false) - // handle ctrlc if let Event::Key(key) = input { if let KeyCode::Char('c') = key.code { @@ -352,7 +303,7 @@ impl Output { ); } - pub(crate) fn new_build_logs(&mut self, platform: Platform, update: UpdateBuildProgress) { + pub(crate) fn new_build_logs(&mut self, platform: Platform, update: BuildUpdateProgress) { let snapped = self.is_snapped(LogSource::Target(platform)); // when the build is finished, switch to the console @@ -372,50 +323,23 @@ impl Output { } pub(crate) fn new_ready_app(&mut self, handle: &AppHandle) { - // for result in results { - // let out = build_engine - // .finished - // .iter_mut() - // .find_map(|(platform, child)| { - // if platform == &result.target_platform { - // let stdout = child.stdout.take().unwrap(); - // let stderr = child.stderr.take().unwrap(); - // Some((stdout, stderr)) - // } else { - // None - // } - // }); - - // let platform = result.target_platform; - - // let stdout = out.map(|(stdout, stderr)| RunningAppOutput { - // stdout: BufReader::new(stdout).lines(), - // stderr: BufReader::new(stderr).lines(), - // stdout_line: String::new(), - // stderr_line: String::new(), - // }); - - // let app = RunningApp { - // result, - // output: stdout, - // }; - - // self.running_apps.insert(platform, app); - - // // Finish the build progress for the platform that just finished building - // if let Some(build) = self.build_progress.build_logs.get_mut(&platform) { - // build.stage = Stage::Finished; - // } - // } + // Finish the build progress for the platform that just finished building + if let Some(build) = self + .build_progress + .build_logs + .get_mut(&handle.app.build.platform()) + { + build.stage = Stage::Finished; + } } pub(crate) fn render( &mut self, - args: &ServeArgs, - krate: &DioxusCrate, - builder: &Builder, + _args: &ServeArgs, + _krate: &DioxusCrate, + _builder: &Builder, server: &DevServer, - watcher: &Watcher, + _watcher: &Watcher, ) { // just drain the build logs if !self.interactive { @@ -764,7 +688,7 @@ pub(crate) struct ActiveBuild { } impl ActiveBuild { - fn update(&mut self, update: UpdateBuildProgress) { + fn update(&mut self, update: BuildUpdateProgress) { match update.update { UpdateStage::Start => { // If we are already past the stage, don't roll back, but allow a fresh build to update. diff --git a/packages/cli/src/serve/runner.rs b/packages/cli/src/serve/runner.rs index d41cb53655..6a0350c375 100644 --- a/packages/cli/src/serve/runner.rs +++ b/packages/cli/src/serve/runner.rs @@ -1,18 +1,12 @@ -use super::ServeUpdate; +use super::{handle::AppHandle, ServeUpdate}; use crate::{ builder::{AppBundle, Platform}, cli::serve::ServeArgs, DioxusCrate, Result, }; use futures_util::stream::FuturesUnordered; -use std::{collections::HashMap, net::SocketAddr, path::PathBuf, process::Stdio}; -use tokio::process::Child; -use tokio::{ - io::{AsyncBufReadExt, BufReader, Lines}, - process::{ChildStderr, ChildStdout, Command}, -}; +use std::{collections::HashMap, net::SocketAddr}; use tokio_stream::StreamExt; -use uuid::Uuid; pub(crate) struct AppRunner { /// Ongoing apps running in place @@ -24,7 +18,7 @@ pub(crate) struct AppRunner { } impl AppRunner { - pub(crate) fn start(serve: &ServeArgs, config: &DioxusCrate) -> Self { + pub(crate) fn start(_args: &ServeArgs, _krate: &DioxusCrate) -> Self { Self { running: Default::default(), } @@ -75,73 +69,8 @@ impl AppRunner { devserver_ip: SocketAddr, fullstack_address: Option, ) -> Result<&AppHandle> { - let platform = app.build.platform(); - let ip = devserver_ip.to_string(); - - if platform == Platform::Server { - tracing::trace!( - "Proxying fullstack server from port {:?}", - fullstack_address - ); - } - - let work_dir = std::env::temp_dir(); - let executable = app.finish(work_dir).await?; - - let mut handle = AppHandle { - app, - executable, - child: None, - id: Uuid::new_v4(), - stderr: None, - stdout: None, - }; - - // open the exe with some arguments/envvars/etc - // we're going to try and configure this binary from the environment, if we can - // - // web can't be configured like this, so instead, we'll need to plumb a meta tag into the - // index.html during dev - match handle.app.build.platform() { - Platform::Web => {} - Platform::Desktop => { - let mut cmd = Command::new(handle.executable.clone()); - cmd.env( - dioxus_runtime_config::FULLSTACK_ADDRESS_ENV, - fullstack_address - .as_ref() - .map(|addr| addr.to_string()) - .unwrap_or_else(|| "127.0.0.1:8080".to_string()), - ) - .env( - dioxus_runtime_config::IOS_DEVSERVER_ADDR_ENV, - format!("ws://{}/_dioxus", ip), - ) - .env( - dioxus_runtime_config::DEVSERVER_RAW_ADDR_ENV, - format!("ws://{}/_dioxus", ip), - ) - .env("CARGO_MANIFEST_DIR", handle.app.build.krate.crate_dir()) - .env( - "SIMCTL_CHILD_CARGO_MANIFEST_DIR", - handle.app.build.krate.crate_dir(), - ) - .stderr(Stdio::piped()) - .stdout(Stdio::piped()) - .kill_on_drop(true); - - let mut child = cmd.spawn()?; - let stdout = BufReader::new(child.stdout.take().unwrap()); - let stderr = BufReader::new(child.stderr.take().unwrap()); - handle.stdout = Some(stdout.lines()); - handle.stderr = Some(stderr.lines()); - handle.child = Some(child); - } - Platform::Ios => {} - Platform::Android => {} - Platform::Server => {} - Platform::Liveview => {} - } + let handle = AppHandle::start(app, devserver_ip, fullstack_address).await?; + let platform = handle.app.build.platform(); if let Some(_previous) = self.running.insert(platform, handle) { // close the old app, gracefully, hopefully @@ -149,78 +78,4 @@ impl AppRunner { Ok(self.running.get(&platform).unwrap()) } - - #[allow(unused)] - fn open_bundled_ios_app(&self, build: &AppBundle) -> std::io::Result> { - // command = "xcrun" - // args = [ - // "simctl", - // "install", - // "booted", - // "target/aarch64-apple-ios-sim/debug/bundle/ios/DioxusApp.app", - // ] - - // [tasks.run_ios_sim] - // args = ["simctl", "launch", "--console", "booted", "com.dioxuslabs"] - // command = "xcrun" - // dependencies = ["build_ios_sim", "install_ios_sim"] - - // [tasks.serve-sim] - // dependencies = ["build_ios_sim", "install_ios_sim", "run_ios_sim"] - - // APP_PATH="target/aarch64-apple-ios/debug/bundle/ios/DioxusApp.app" - - // # get the device id by jq-ing the json of the device list - // xcrun devicectl list devices --json-output target/deviceid.json - // DEVICE_UUID=$(jq -r '.result.devices[0].identifier' target/deviceid.json) - - // xcrun devicectl device install app --device "${DEVICE_UUID}" "${APP_PATH}" --json-output target/xcrun.json - - // # get the installation url by jq-ing the json of the device install - // INSTALLATION_URL=$(jq -r '.result.installedApplications[0].installationURL' target/xcrun.json) - - // # launch the app - // # todo: we can just background it immediately and then pick it up for loading its logs - // xcrun devicectl device process launch --device "${DEVICE_UUID}" "${INSTALLATION_URL}" - - // # # launch the app and put it in background - // # xcrun devicectl device process launch --no-activate --verbose --device "${DEVICE_UUID}" "${INSTALLATION_URL}" --json-output "${XCRUN_DEVICE_PROCESS_LAUNCH_LOG_DIR}" - - // # # Extract background PID of status app - // # STATUS_PID=$(jq -r '.result.process.processIdentifier' "${XCRUN_DEVICE_PROCESS_LAUNCH_LOG_DIR}") - // # "${GIT_ROOT}/scripts/wait-for-metro-port.sh" 2>&1 - - // # # now that metro is ready, resume the app from background - // # xcrun devicectl device process resume --device "${DEVICE_UUID}" --pid "${STATUS_PID}" > "${XCRUN_DEVICE_PROCESS_RESUME_LOG_DIR}" 2>&1 - todo!("Open mobile apps") - } -} - -/// A handle to a running app -pub(crate) struct AppHandle { - pub(crate) id: Uuid, - pub(crate) app: AppBundle, - pub(crate) executable: PathBuf, - pub(crate) child: Option, - pub(crate) stdout: Option>>, - pub(crate) stderr: Option>>, - // pub(crate) stdout_line: String, - // pub(crate) stderr_line: String, -} - -impl AppHandle { - /// Update an asset in the running apps - /// - /// Might need to upload the asset to the simulator or overwrite it within the bundle - /// - /// Returns the name of the asset in the bundle if it exists - pub(crate) fn hotreload_asset(&self, path: &PathBuf) -> Option { - let resource = self.app.assets.assets.get(path).cloned()?; - - self.app - .assets - .copy_asset_to(&self.app.asset_dir(), path, false, false); - - Some(resource.bundled.into()) - } } diff --git a/packages/cli/src/serve/server.rs b/packages/cli/src/serve/server.rs index 3917bc2cea..905a6990f1 100644 --- a/packages/cli/src/serve/server.rs +++ b/packages/cli/src/serve/server.rs @@ -2,18 +2,18 @@ use crate::dioxus_crate::DioxusCrate; use crate::{builder::Platform, serve::ServeArgs}; use crate::{config::WebHttpsConfig, serve::update::ServeUpdate}; use crate::{Error, Result}; -use axum::extract::{Request, State}; -use axum::middleware::{self, Next}; use axum::{ body::Body, extract::{ ws::{Message, WebSocket}, Extension, WebSocketUpgrade, }, + extract::{Request, State}, http::{ header::{HeaderName, HeaderValue, CACHE_CONTROL, EXPIRES, PRAGMA}, Method, Response, StatusCode, }, + middleware::{self, Next}, response::IntoResponse, routing::{get, get_service}, Router, @@ -21,21 +21,21 @@ use axum::{ use axum_server::tls_rustls::RustlsConfig; use dioxus_devtools_types::{DevserverMsg, HotReloadMsg}; use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender}; -use futures_util::{future, stream}; -use futures_util::{stream::FuturesUnordered, StreamExt}; -use hyper::header::ACCEPT; -use hyper::HeaderMap; +use futures_util::{ + future, + stream::{self, FuturesUnordered}, + StreamExt, +}; +use hyper::{header::ACCEPT, HeaderMap}; use serde::{Deserialize, Serialize}; -use std::net::TcpListener; -use std::sync::Arc; -use std::sync::RwLock; use std::{ convert::Infallible, fs, io, - net::{IpAddr, SocketAddr}, + net::{IpAddr, SocketAddr, TcpListener}, + sync::RwLock, }; -use std::{path::Path, process::Stdio}; -use tokio::process::{Child, Command}; +use std::{path::Path, sync::Arc}; +use tokio::process::Command; use tokio::task::JoinHandle; use tower::ServiceBuilder; use tower_http::{ @@ -44,42 +44,8 @@ use tower_http::{ ServiceBuilderExt, }; -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(tag = "type", content = "data")] -enum Status { - ClientInit { - application_name: String, - platform: String, - }, - Building { - progress: f64, - build_message: String, - }, - BuildError { - error: String, - }, - Ready, -} - -#[derive(Debug, Clone)] -struct SharedStatus(Arc>); - -impl SharedStatus { - fn new(status: Status) -> Self { - Self(Arc::new(RwLock::new(status))) - } - - fn set(&self, status: Status) { - *self.0.write().unwrap() = status; - } - - fn get(&self) -> Status { - self.0.read().unwrap().clone() - } -} - pub(crate) struct DevServer { - pub(crate) serve: ServeArgs, + pub(crate) _args: ServeArgs, pub(crate) hot_reload_sockets: Vec, pub(crate) build_status_sockets: Vec, pub(crate) ip: SocketAddr, @@ -166,7 +132,7 @@ impl DevServer { }); Self { - serve: args.clone(), + _args: args.clone(), hot_reload_sockets: Default::default(), build_status_sockets: Default::default(), new_hot_reload_sockets: hot_reload_sockets_rx, @@ -536,7 +502,9 @@ pub(crate) async fn get_rustls(web_config: &WebHttpsConfig) -> Result Result<(String, String)> { +pub(crate) async fn get_rustls_with_mkcert( + web_config: &WebHttpsConfig, +) -> Result<(String, String)> { const DEFAULT_KEY_PATH: &str = "ssl/key.pem"; const DEFAULT_CERT_PATH: &str = "ssl/cert.pem"; @@ -649,3 +617,37 @@ async fn send_build_status_to( let msg = serde_json::to_string(&build_status.get()).unwrap(); socket.send(Message::Text(msg)).await } + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(tag = "type", content = "data")] +enum Status { + ClientInit { + application_name: String, + platform: String, + }, + Building { + progress: f64, + build_message: String, + }, + BuildError { + error: String, + }, + Ready, +} + +#[derive(Debug, Clone)] +struct SharedStatus(Arc>); + +impl SharedStatus { + fn new(status: Status) -> Self { + Self(Arc::new(RwLock::new(status))) + } + + fn set(&self, status: Status) { + *self.0.write().unwrap() = status; + } + + fn get(&self) -> Status { + self.0.read().unwrap().clone() + } +} diff --git a/packages/cli/src/serve/update.rs b/packages/cli/src/serve/update.rs index a677897521..f32e786070 100644 --- a/packages/cli/src/serve/update.rs +++ b/packages/cli/src/serve/update.rs @@ -1,9 +1,7 @@ -use crate::builder::{AppBundle, BuildRequest, BuildUpdate, Platform, UpdateBuildProgress}; +use crate::builder::{BuildUpdate, Platform}; use axum::extract::ws::Message as WsMessage; use std::{path::PathBuf, process::ExitStatus}; -use super::LogSource; - /// One fat enum to rule them all.... /// /// Thanks to libraries like winit for the inspiration @@ -46,7 +44,6 @@ pub(crate) enum ServeUpdate { }, TracingLog { - // source: LogSource, log: String, }, } From 32ff7c4030c1cfd2aab07115b5bb3a4e3afbaaab Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 5 Sep 2024 01:20:13 -0700 Subject: [PATCH 072/139] fix issue with join --- packages/cli/src/serve/output.rs | 4 +-- packages/cli/src/serve/runner.rs | 45 +++++++++++++++++--------------- 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/packages/cli/src/serve/output.rs b/packages/cli/src/serve/output.rs index c38ba3fcfa..f356e94fa9 100644 --- a/packages/cli/src/serve/output.rs +++ b/packages/cli/src/serve/output.rs @@ -9,7 +9,7 @@ use crossterm::{ ExecutableCommand, }; use dioxus_devtools_types::ClientMsg; -use futures_util::StreamExt; +use futures_util::{future::OptionFuture, StreamExt}; use ratatui::{prelude::*, widgets::*, TerminalOptions, Viewport}; use std::{ cell::RefCell, @@ -160,7 +160,7 @@ impl Output { /// Also tick animations every few ms pub(crate) async fn wait(&mut self) -> ServeUpdate { let event = tokio::select! { - Some(Ok(event)) = self.events.as_mut().unwrap().next(), if self.events.is_some() => event + Some(Some(Ok(event))) = OptionFuture::from(self.events.as_mut().map(|f| f.next())) => event }; ServeUpdate::TuiInput { event } diff --git a/packages/cli/src/serve/runner.rs b/packages/cli/src/serve/runner.rs index 6a0350c375..7e8c3dd6ec 100644 --- a/packages/cli/src/serve/runner.rs +++ b/packages/cli/src/serve/runner.rs @@ -4,7 +4,7 @@ use crate::{ cli::serve::ServeArgs, DioxusCrate, Result, }; -use futures_util::stream::FuturesUnordered; +use futures_util::{future::OptionFuture, stream::FuturesUnordered}; use std::{collections::HashMap, net::SocketAddr}; use tokio_stream::StreamExt; @@ -38,28 +38,31 @@ impl AppRunner { return futures_util::future::pending().await; } - self.running.iter_mut().map(|(platform, handle)| async { - let platform = *platform; - tokio::select! { - Ok(Some(msg)) = handle.stdout.as_mut().unwrap().next_line(), if handle.stdout.is_some() => { - ServeUpdate::StdoutReceived { platform, msg } - }, - Ok(Some(msg)) = handle.stderr.as_mut().unwrap().next_line(), if handle.stderr.is_some() => { - ServeUpdate::StderrReceived { platform, msg } - }, - status = handle.child.as_mut().unwrap().wait(), if handle.child.is_some() => { - tracing::info!("Child process exited with status: {status:?}"); - match status { - Ok(status) => ServeUpdate::ProcessExited { status, platform }, - Err(_err) => todo!("handle error in process joining?"), + self.running + .iter_mut() + .map(|(platform, handle)| async { + use ServeUpdate::*; + let platform = *platform; + tokio::select! { + Some(Ok(Some(msg))) = OptionFuture::from(handle.stdout.as_mut().map(|f| f.next_line())) => { + StdoutReceived { platform, msg } + }, + Some(Ok(Some(msg))) = OptionFuture::from(handle.stderr.as_mut().map(|f| f.next_line())) => { + StderrReceived { platform, msg } + }, + Some(status) = OptionFuture::from(handle.child.as_mut().map(|f| f.wait())) => { + tracing::info!("Child process exited with status: {status:?}"); + match status { + Ok(status) => ProcessExited { status, platform }, + Err(_err) => todo!("handle error in process joining?"), + } } } - } - }) - .collect::>() - .next() - .await - .expect("Stream to pending if not empty") + }) + .collect::>() + .next() + .await + .expect("Stream to pending if not empty") } /// Finally "bundle" this app and return a handle to it From 8f94523a65b1a7284289712d7540edb32fcc3c43 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 5 Sep 2024 01:50:25 -0700 Subject: [PATCH 073/139] fix fullstack --- packages/cli/src/assets.rs | 11 +++- packages/cli/src/builder/builder.rs | 33 ++++++++--- packages/cli/src/builder/cargo.rs | 4 +- packages/cli/src/builder/mod.rs | 3 - packages/cli/src/builder/request.rs | 70 ++++++++++++----------- packages/cli/src/builder/result.rs | 1 - packages/cli/src/builder/tooling.rs | 1 - packages/cli/src/bundler/app.rs | 2 +- packages/cli/src/config/app.rs | 4 +- packages/cli/src/serve/handle.rs | 2 +- packages/cli/src/serve/output.rs | 4 +- packages/cli/src/serve/runner.rs | 7 +-- packages/fullstack/src/document/mod.rs | 2 + packages/fullstack/src/document/server.rs | 23 +++++--- packages/fullstack/src/lib.rs | 2 +- packages/fullstack/src/render.rs | 51 +++++++---------- packages/fullstack/src/serve_config.rs | 6 +- 17 files changed, 123 insertions(+), 103 deletions(-) delete mode 100644 packages/cli/src/builder/result.rs delete mode 100644 packages/cli/src/builder/tooling.rs diff --git a/packages/cli/src/assets.rs b/packages/cli/src/assets.rs index b3e22a4c9b..19d305a198 100644 --- a/packages/cli/src/assets.rs +++ b/packages/cli/src/assets.rs @@ -194,8 +194,11 @@ impl AssetManifest { target_asset: &Path, optimize: bool, _pre_compress: bool, - ) { - let src = self.assets.get(target_asset).unwrap(); + ) -> anyhow::Result<()> { + let src = self + .assets + .get(target_asset) + .with_context(|| format!("Failed to find asset {target_asset:?}"))?; let local = src.absolute.clone(); @@ -206,10 +209,12 @@ impl AssetManifest { // If there's no optimizaton while copying this asset, we simply std::fs::copy and call it a day if !optimize { std::fs::copy(local, destination.join(&src.bundled)).expect("Failed to copy asset"); - return; + return Ok(()); } // Otherwise, let's attempt to optimize the the asset we're copying + + Ok(()) } } diff --git a/packages/cli/src/builder/builder.rs b/packages/cli/src/builder/builder.rs index 80c870b3c0..830981b488 100644 --- a/packages/cli/src/builder/builder.rs +++ b/packages/cli/src/builder/builder.rs @@ -1,7 +1,7 @@ -use crate::build::BuildArgs; use crate::builder::*; use crate::dioxus_crate::DioxusCrate; use crate::Result; +use crate::{build::BuildArgs, bundler::AppBundle}; use futures_util::StreamExt; use progress::{BuildUpdateProgress, ProgressRx, ProgressTx}; use tokio::task::JoinSet; @@ -9,13 +9,13 @@ use tokio::task::JoinSet; /// A handle to ongoing builds and then the spawned tasks themselves pub(crate) struct Builder { /// The application we are building - pub(crate) krate: DioxusCrate, + krate: DioxusCrate, /// Ongoing builds - pub(crate) building: JoinSet<(Platform, Result)>, + building: JoinSet<(Platform, Result)>, /// Messages from the build engine will be sent to this channel - pub(crate) channel: (ProgressTx, ProgressRx), + channel: (ProgressTx, ProgressRx), } pub(crate) enum BuildUpdate { @@ -58,7 +58,7 @@ impl Builder { let mut requests = vec![ // At least one request for the target app - BuildRequest::new(self.krate.clone(), args.clone(), self.channel.0.clone()), + BuildRequest::new_client(&self.krate, args.clone(), self.channel.0.clone()), ]; // And then the fullstack app if we're building a fullstack app @@ -68,7 +68,7 @@ impl Builder { requests.push(server); } - // Queue the builds on the joinset + // Queue the builds on the joinset, being careful to not panic, so we can unwrap for build_request in requests { let platform = build_request.platform(); self.building.spawn(async move { @@ -89,11 +89,26 @@ impl Builder { } /// Wait for the build to finish - pub(crate) async fn wait_for_finish(&mut self) { + pub(crate) async fn wait_for_finish(&mut self) -> Result> { + let mut results = vec![]; + loop { let next = self.wait().await; - if let BuildUpdate::AllFinished = next { - return; + + match next { + BuildUpdate::Progress(_) => {} + BuildUpdate::BuildReady { target, result } => { + results.push(result); + tracing::info!("Build ready for target {target:?}"); + } + BuildUpdate::BuildFailed { target, err } => { + tracing::error!("Build failed for target {target:?}: {err}"); + return Err(err); + } + BuildUpdate::AllFinished => { + tracing::info!("All builds finished!"); + return Ok(results); + } } } } diff --git a/packages/cli/src/builder/cargo.rs b/packages/cli/src/builder/cargo.rs index def032d965..898c60248b 100644 --- a/packages/cli/src/builder/cargo.rs +++ b/packages/cli/src/builder/cargo.rs @@ -1,6 +1,6 @@ -use super::{AppBundle, BuildRequest}; -use crate::builder::Platform; +use super::BuildRequest; use crate::{assets::AssetManifest, link::LINK_OUTPUT_ENV_VAR}; +use crate::{builder::Platform, bundler::AppBundle}; use crate::{link::InterceptedArgs, Result}; use anyhow::Context; use std::process::Stdio; diff --git a/packages/cli/src/builder/mod.rs b/packages/cli/src/builder/mod.rs index 1899ec14e8..e80c031b1e 100644 --- a/packages/cli/src/builder/mod.rs +++ b/packages/cli/src/builder/mod.rs @@ -10,12 +10,9 @@ mod platform; mod profiles; mod progress; mod request; -mod result; -mod tooling; mod web; pub(crate) use builder::*; pub(crate) use platform::*; pub(crate) use progress::*; pub(crate) use request::*; -pub(crate) use result::*; diff --git a/packages/cli/src/builder/request.rs b/packages/cli/src/builder/request.rs index 639bde8e98..7e4485f786 100644 --- a/packages/cli/src/builder/request.rs +++ b/packages/cli/src/builder/request.rs @@ -1,5 +1,5 @@ -use super::profiles::*; use super::progress::ProgressTx; +use super::{platform, profiles::*}; use crate::build::BuildArgs; use crate::builder::Platform; use crate::dioxus_crate::DioxusCrate; @@ -28,14 +28,44 @@ pub(crate) struct BuildRequest { } impl BuildRequest { - pub(crate) fn new(krate: DioxusCrate, build: BuildArgs, progress: ProgressTx) -> Self { - Self { - progress, - build, + pub(crate) fn new_server( + krate: &DioxusCrate, + mut build: BuildArgs, + progress: ProgressTx, + ) -> Self { + if build.profile.is_none() { + build.profile = Some(CLIENT_PROFILE.to_string()); + } + + let client_feature = build.auto_detect_server_feature(krate); + let mut build = Self::new_with_target_directory_rust_flags_and_features( krate, - custom_target_dir: Default::default(), - rust_flags: Default::default(), + &build, + build.target_args.server_feature.clone().or(client_feature), + progress, + ); + build.build.platform = Some(platform::Platform::Server); + build + } + + pub(crate) fn new_client( + krate: &DioxusCrate, + mut build: BuildArgs, + progress: ProgressTx, + ) -> Self { + if build.profile.is_none() { + build.profile = Some(SERVER_PROFILE.to_string()); } + let (client_feature, client_platform) = build.auto_detect_client_platform(krate); + let mut build = Self::new_with_target_directory_rust_flags_and_features( + krate, + &build, + build.target_args.client_feature.clone().or(client_feature), + progress, + ); + + build.build.platform = Some(client_platform); + build } fn new_with_target_directory_rust_flags_and_features( @@ -62,32 +92,6 @@ impl BuildRequest { } } - pub(crate) fn new_server(krate: &DioxusCrate, mut build: BuildArgs, progress: ProgressTx) -> Self { - if build.profile.is_none() { - build.profile = Some(CLIENT_PROFILE.to_string()); - } - let client_feature = build.auto_detect_server_feature(krate); - Self::new_with_target_directory_rust_flags_and_features( - krate, - &build, - build.target_args.server_feature.clone().or(client_feature), - progress, - ) - } - - pub(crate) fn new_client(krate: &DioxusCrate, mut build: BuildArgs, progress: ProgressTx) -> Self { - if build.profile.is_none() { - build.profile = Some(SERVER_PROFILE.to_string()); - } - let (client_feature, client_platform) = build.auto_detect_client_platform(krate); - Self::new_with_target_directory_rust_flags_and_features( - krate, - &build, - build.target_args.client_feature.clone().or(client_feature), - progress, - ) - } - /// Get the platform for this build pub(crate) fn platform(&self) -> Platform { self.build diff --git a/packages/cli/src/builder/result.rs b/packages/cli/src/builder/result.rs deleted file mode 100644 index 8ecc46ec48..0000000000 --- a/packages/cli/src/builder/result.rs +++ /dev/null @@ -1 +0,0 @@ -pub(crate) use crate::bundler::AppBundle; diff --git a/packages/cli/src/builder/tooling.rs b/packages/cli/src/builder/tooling.rs deleted file mode 100644 index 8b13789179..0000000000 --- a/packages/cli/src/builder/tooling.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/packages/cli/src/bundler/app.rs b/packages/cli/src/bundler/app.rs index ca0249c23e..ad1e86e6ff 100644 --- a/packages/cli/src/bundler/app.rs +++ b/packages/cli/src/bundler/app.rs @@ -149,7 +149,7 @@ impl AppBundle { ); self.assets - .copy_asset_to(&asset_dir, asset, optimize, pre_compress); + .copy_asset_to(&asset_dir, asset, optimize, pre_compress)?; self.build.status_finished_asset( assets_finished.fetch_add(1, std::sync::atomic::Ordering::SeqCst), diff --git a/packages/cli/src/config/app.rs b/packages/cli/src/config/app.rs index 4a331767a7..ba52f3ce3f 100644 --- a/packages/cli/src/config/app.rs +++ b/packages/cli/src/config/app.rs @@ -21,7 +21,7 @@ pub(crate) struct ApplicationConfig { } pub(crate) fn default_name() -> String { - "my-cool-project".into() + "dioxus-app".into() } pub(crate) fn default_platform() -> Platform { @@ -29,7 +29,7 @@ pub(crate) fn default_platform() -> Platform { } pub(crate) fn asset_dir_default() -> PathBuf { - PathBuf::from("public") + PathBuf::from("assets") } pub(crate) fn out_dir_default() -> PathBuf { diff --git a/packages/cli/src/serve/handle.rs b/packages/cli/src/serve/handle.rs index ea05b6605e..2a55ca15b1 100644 --- a/packages/cli/src/serve/handle.rs +++ b/packages/cli/src/serve/handle.rs @@ -1,5 +1,5 @@ -use crate::builder::{AppBundle, Platform}; use crate::Result; +use crate::{builder::Platform, bundler::AppBundle}; use std::{net::SocketAddr, path::PathBuf, process::Stdio}; use tokio::{ io::AsyncBufReadExt, diff --git a/packages/cli/src/serve/output.rs b/packages/cli/src/serve/output.rs index f356e94fa9..717accfd03 100644 --- a/packages/cli/src/serve/output.rs +++ b/packages/cli/src/serve/output.rs @@ -2,7 +2,6 @@ use crate::builder::*; use crate::config::AddressArguments; use crate::serve::ServeArgs; use crate::{builder::Platform, dioxus_crate::DioxusCrate}; -use core::panic; use crossterm::{ event::{Event, EventStream, KeyCode, KeyModifiers, MouseEventKind}, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, @@ -160,7 +159,8 @@ impl Output { /// Also tick animations every few ms pub(crate) async fn wait(&mut self) -> ServeUpdate { let event = tokio::select! { - Some(Some(Ok(event))) = OptionFuture::from(self.events.as_mut().map(|f| f.next())) => event + Some(Some(Ok(event))) = OptionFuture::from(self.events.as_mut().map(|f| f.next())) => event, + else => futures_util::future::pending().await }; ServeUpdate::TuiInput { event } diff --git a/packages/cli/src/serve/runner.rs b/packages/cli/src/serve/runner.rs index 7e8c3dd6ec..947375433d 100644 --- a/packages/cli/src/serve/runner.rs +++ b/packages/cli/src/serve/runner.rs @@ -1,9 +1,5 @@ use super::{handle::AppHandle, ServeUpdate}; -use crate::{ - builder::{AppBundle, Platform}, - cli::serve::ServeArgs, - DioxusCrate, Result, -}; +use crate::{builder::Platform, bundler::AppBundle, cli::serve::ServeArgs, DioxusCrate, Result}; use futures_util::{future::OptionFuture, stream::FuturesUnordered}; use std::{collections::HashMap, net::SocketAddr}; use tokio_stream::StreamExt; @@ -57,6 +53,7 @@ impl AppRunner { Err(_err) => todo!("handle error in process joining?"), } } + else => futures_util::future::pending().await } }) .collect::>() diff --git a/packages/fullstack/src/document/mod.rs b/packages/fullstack/src/document/mod.rs index 0c380994f6..49b332628a 100644 --- a/packages/fullstack/src/document/mod.rs +++ b/packages/fullstack/src/document/mod.rs @@ -2,7 +2,9 @@ #[cfg(feature = "server")] pub(crate) mod server; + #[cfg(feature = "server")] pub use server::ServerDocument; + #[cfg(all(feature = "web", feature = "document"))] pub(crate) mod web; diff --git a/packages/fullstack/src/document/server.rs b/packages/fullstack/src/document/server.rs index 0c560a89a1..8c478b592c 100644 --- a/packages/fullstack/src/document/server.rs +++ b/packages/fullstack/src/document/server.rs @@ -4,9 +4,9 @@ use std::cell::RefCell; -use dioxus_lib::{html::document::*, prelude::*}; +use dioxus_document::*; +use dioxus_lib::prelude::*; use dioxus_ssr::Renderer; -use generational_box::GenerationalBox; use once_cell::sync::Lazy; use parking_lot::RwLock; @@ -71,10 +71,6 @@ impl ServerDocument { } impl Document for ServerDocument { - fn new_evaluator(&self, js: String) -> GenerationalBox> { - NoOpDocument.new_evaluator(js) - } - fn set_title(&self, title: String) { self.warn_if_streaming(); self.serialize_for_hydration(); @@ -115,7 +111,7 @@ impl Document for ServerDocument { }); } - fn create_link(&self, props: document::LinkProps) { + fn create_link(&self, props: LinkProps) { self.warn_if_streaming(); self.serialize_for_hydration(); self.0.borrow_mut().link.push(rsx! { @@ -141,4 +137,17 @@ impl Document for ServerDocument { fn as_any(&self) -> &dyn std::any::Any { self } + + fn eval(&self, js: String) -> Eval { + todo!("server evalator") + } + + fn create_head_element( + &self, + name: &str, + attributes: Vec<(&str, String)>, + contents: Option, + ) { + todo!() + } } diff --git a/packages/fullstack/src/lib.rs b/packages/fullstack/src/lib.rs index c7794ac4ad..7e32eb1141 100644 --- a/packages/fullstack/src/lib.rs +++ b/packages/fullstack/src/lib.rs @@ -62,7 +62,7 @@ pub mod prelude { #[cfg(feature = "server")] #[cfg_attr(docsrs, doc(cfg(feature = "server")))] - pub use dioxus_ssr::incremental::{IncrementalRenderer, IncrementalRendererConfig}; + pub use dioxus_isrg::{IncrementalRenderer, IncrementalRendererConfig}; pub use dioxus_server_macro::*; pub use server_fn::{self, ServerFn as _, ServerFnError}; diff --git a/packages/fullstack/src/render.rs b/packages/fullstack/src/render.rs index c0f5c010a6..9f4cdbb709 100644 --- a/packages/fullstack/src/render.rs +++ b/packages/fullstack/src/render.rs @@ -1,10 +1,9 @@ //! A shared pool of renderers for efficient server side rendering. use crate::streaming::{Mount, StreamingRenderer}; +use dioxus_document::Document; use dioxus_interpreter_js::INITIALIZE_STREAMING_JS; -use dioxus_ssr::{ - incremental::{CachedRender, RenderFreshness}, - Renderer, -}; +use dioxus_isrg::{CachedRender, RenderFreshness}; +use dioxus_ssr::Renderer; use futures_channel::mpsc::Sender; use futures_util::{Stream, StreamExt}; use std::sync::Arc; @@ -48,13 +47,13 @@ where struct SsrRendererPool { renderers: RwLock>, - incremental_cache: Option>, + incremental_cache: Option>, } impl SsrRendererPool { fn new( initial_size: usize, - incremental: Option, + incremental: Option, ) -> Self { let renderers = RwLock::new((0..initial_size).map(|_| pre_renderer()).collect()); Self { @@ -67,7 +66,7 @@ impl SsrRendererPool { fn check_cached_route( &self, route: &str, - render_into: &mut Sender>, + render_into: &mut Sender>, ) -> Option { if let Some(incremental) = &self.incremental_cache { if let Ok(mut incremental) = incremental.write() { @@ -79,11 +78,7 @@ impl SsrRendererPool { .. } = cached_render; _ = render_into.start_send(String::from_utf8(response.to_vec()).map_err( - |err| { - dioxus_ssr::incremental::IncrementalRendererError::Other(Box::new( - err, - )) - }, + |err| dioxus_isrg::IncrementalRendererError::Other(Box::new(err)), )); return Some(freshness); } @@ -110,19 +105,19 @@ impl SsrRendererPool { ) -> Result< ( RenderFreshness, - impl Stream>, + impl Stream>, ), - dioxus_ssr::incremental::IncrementalRendererError, + dioxus_isrg::IncrementalRendererError, > { struct ReceiverWithDrop { receiver: futures_channel::mpsc::Receiver< - Result, + Result, >, cancel_task: Option>, } impl Stream for ReceiverWithDrop { - type Item = Result; + type Item = Result; fn poll_next( mut self: std::pin::Pin<&mut Self>, @@ -142,7 +137,7 @@ impl SsrRendererPool { } let (mut into, rx) = futures_channel::mpsc::channel::< - Result, + Result, >(1000); // before we even spawn anything, we can check synchronously if we have the route cached @@ -302,9 +297,7 @@ impl SsrRendererPool { resolved_data, &mut resolved_chunk, ) { - throw_error!( - dioxus_ssr::incremental::IncrementalRendererError::RenderError(err) - ); + throw_error!(dioxus_isrg::IncrementalRendererError::RenderError(err)); } stream.render(resolved_chunk); @@ -406,9 +399,9 @@ impl SSRState { ) -> Result< ( RenderFreshness, - impl Stream>, + impl Stream>, ), - dioxus_ssr::incremental::IncrementalRendererError, + dioxus_isrg::IncrementalRendererError, > { self.renderers .clone() @@ -435,11 +428,11 @@ impl FullstackHTMLTemplate { &self, to: &mut R, virtual_dom: &VirtualDom, - ) -> Result<(), dioxus_ssr::incremental::IncrementalRendererError> { + ) -> Result<(), dioxus_isrg::IncrementalRendererError> { let ServeConfig { index, .. } = &self.cfg; let title = { - let document: Option> = + let document: Option> = virtual_dom.in_runtime(|| ScopeId::ROOT.consume_context()); let document: Option<&crate::document::server::ServerDocument> = document .as_ref() @@ -456,7 +449,7 @@ impl FullstackHTMLTemplate { } to.write_str(&index.head_after_title)?; - let document: Option> = + let document: Option> = virtual_dom.in_runtime(|| ScopeId::ROOT.consume_context()); let document: Option<&crate::document::server::ServerDocument> = document .as_ref() @@ -478,7 +471,7 @@ impl FullstackHTMLTemplate { fn render_before_body( &self, to: &mut R, - ) -> Result<(), dioxus_ssr::incremental::IncrementalRendererError> { + ) -> Result<(), dioxus_isrg::IncrementalRendererError> { let ServeConfig { index, .. } = &self.cfg; to.write_str(&index.close_head)?; @@ -493,7 +486,7 @@ impl FullstackHTMLTemplate { &self, to: &mut R, virtual_dom: &VirtualDom, - ) -> Result<(), dioxus_ssr::incremental::IncrementalRendererError> { + ) -> Result<(), dioxus_isrg::IncrementalRendererError> { let ServeConfig { index, .. } = &self.cfg; // Collect the initial server data from the root node. For most apps, no use_server_futures will be resolved initially, so this will be full on `None`s. @@ -512,7 +505,7 @@ impl FullstackHTMLTemplate { pub fn render_after_body( &self, to: &mut R, - ) -> Result<(), dioxus_ssr::incremental::IncrementalRendererError> { + ) -> Result<(), dioxus_isrg::IncrementalRendererError> { let ServeConfig { index, .. } = &self.cfg; to.write_str(&index.after_closing_body_tag)?; @@ -526,7 +519,7 @@ impl FullstackHTMLTemplate { to: &mut R, virtual_dom: &VirtualDom, body: impl std::fmt::Display, - ) -> Result<(), dioxus_ssr::incremental::IncrementalRendererError> { + ) -> Result<(), dioxus_isrg::IncrementalRendererError> { self.render_head(to, virtual_dom)?; write!(to, "{body}")?; self.render_after_main(to, virtual_dom)?; diff --git a/packages/fullstack/src/serve_config.rs b/packages/fullstack/src/serve_config.rs index 0d30136f70..3f1482b95f 100644 --- a/packages/fullstack/src/serve_config.rs +++ b/packages/fullstack/src/serve_config.rs @@ -11,7 +11,7 @@ pub struct ServeConfigBuilder { pub(crate) root_id: Option<&'static str>, pub(crate) index_html: Option, pub(crate) index_path: Option, - pub(crate) incremental: Option, + pub(crate) incremental: Option, } impl ServeConfigBuilder { @@ -26,7 +26,7 @@ impl ServeConfigBuilder { } /// Enable incremental static generation - pub fn incremental(mut self, cfg: dioxus_ssr::incremental::IncrementalRendererConfig) -> Self { + pub fn incremental(mut self, cfg: dioxus_isrg::IncrementalRendererConfig) -> Self { self.incremental = Some(cfg); self } @@ -166,7 +166,7 @@ pub(crate) struct IndexHtml { #[derive(Clone)] pub struct ServeConfig { pub(crate) index: IndexHtml, - pub(crate) incremental: Option, + pub(crate) incremental: Option, } impl ServeConfig { From fc07bf550db74a741bb15e151337f2b70e45456a Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 5 Sep 2024 03:08:26 -0700 Subject: [PATCH 074/139] gracefully handle fullstack without a server --- packages/cli/src/assets.rs | 118 ++--------------- packages/cli/src/builder/builder.rs | 4 +- packages/cli/src/builder/cargo.rs | 2 +- packages/cli/src/builder/request.rs | 38 ++++-- packages/cli/src/bundler/app.rs | 11 +- .../cli/src/{bundler.rs => bundler/format.rs} | 9 -- packages/cli/src/bundler/mod.rs | 9 ++ packages/cli/src/cli/build.rs | 9 +- packages/cli/src/cli/serve.rs | 2 +- packages/cli/src/dioxus_crate.rs | 7 +- packages/cli/src/fastfs.rs | 122 ++++++++++++++++++ packages/cli/src/serve/handle.rs | 9 +- packages/cli/src/serve/tracer.rs | 2 +- packages/fullstack/src/document/web.rs | 17 ++- packages/fullstack/src/launch.rs | 9 +- packages/web/ric_raf/README.md | 3 - packages/web/ric_raf/ric_raf.rs | 80 ------------ packages/web/ric_raf/ricpolyfill.js | 28 ---- packages/web/src/hydration/deserialize.rs | 6 +- packages/web/src/hydration/hydrate.rs | 5 +- packages/web/src/launch.rs | 10 +- packages/web/src/lib.rs | 31 +++-- 22 files changed, 241 insertions(+), 290 deletions(-) rename packages/cli/src/{bundler.rs => bundler/format.rs} (79%) create mode 100644 packages/cli/src/bundler/mod.rs delete mode 100644 packages/web/ric_raf/README.md delete mode 100644 packages/web/ric_raf/ric_raf.rs delete mode 100644 packages/web/ric_raf/ricpolyfill.js diff --git a/packages/cli/src/assets.rs b/packages/cli/src/assets.rs index 19d305a198..abde03c6ca 100644 --- a/packages/cli/src/assets.rs +++ b/packages/cli/src/assets.rs @@ -195,10 +195,14 @@ impl AssetManifest { optimize: bool, _pre_compress: bool, ) -> anyhow::Result<()> { - let src = self - .assets - .get(target_asset) - .with_context(|| format!("Failed to find asset {target_asset:?}"))?; + let Some(src) = self.assets.get(target_asset) else { + crate::fastfs::copy_asset( + target_asset, + &destination.join(target_asset.file_name().unwrap()), + )?; + + return Ok(()); + }; let local = src.absolute.clone(); @@ -218,112 +222,6 @@ impl AssetManifest { } } -pub(crate) fn copy_dir_to( - src_dir: PathBuf, - dest_dir: PathBuf, - pre_compress: bool, -) -> std::io::Result<()> { - let entries = std::fs::read_dir(&src_dir)?; - let mut children: Vec>> = Vec::new(); - - for entry in entries.flatten() { - let entry_path = entry.path(); - let path_relative_to_src = entry_path.strip_prefix(&src_dir).unwrap(); - let output_file_location = dest_dir.join(path_relative_to_src); - children.push(std::thread::spawn(move || { - if entry.file_type()?.is_dir() { - // If the file is a directory, recursively copy it into the output directory - if let Err(err) = - copy_dir_to(entry_path.clone(), output_file_location, pre_compress) - { - tracing::error!( - "Failed to pre-compress directory {}: {}", - entry_path.display(), - err - ); - } - } else { - // Make sure the directory exists - std::fs::create_dir_all(output_file_location.parent().unwrap())?; - // Copy the file to the output directory - std::fs::copy(&entry_path, &output_file_location)?; - - // Then pre-compress the file if needed - if pre_compress { - if let Err(err) = pre_compress_file(&output_file_location) { - tracing::error!( - "Failed to pre-compress static assets {}: {}", - output_file_location.display(), - err - ); - } - // If pre-compression isn't enabled, we should remove the old compressed file if it exists - } else if let Some(compressed_path) = compressed_path(&output_file_location) { - _ = std::fs::remove_file(compressed_path); - } - } - - Ok(()) - })); - } - for child in children { - child.join().unwrap()?; - } - Ok(()) -} - -/// Get the path to the compressed version of a file -fn compressed_path(path: &Path) -> Option { - let new_extension = match path.extension() { - Some(ext) => { - if ext.to_string_lossy().to_lowercase().ends_with("br") { - return None; - } - let mut ext = ext.to_os_string(); - ext.push(".br"); - ext - } - None => OsString::from("br"), - }; - - Some(path.with_extension(new_extension)) -} - -/// pre-compress a file with brotli -pub(crate) fn pre_compress_file(path: &Path) -> std::io::Result<()> { - let Some(compressed_path) = compressed_path(path) else { - return Ok(()); - }; - - let file = std::fs::File::open(path)?; - let mut stream = std::io::BufReader::new(file); - let mut buffer = std::fs::File::create(compressed_path)?; - let params = BrotliEncoderParams::default(); - brotli::BrotliCompress(&mut stream, &mut buffer, ¶ms)?; - - Ok(()) -} - -/// pre-compress all files in a folder -pub(crate) fn pre_compress_folder(path: &Path, pre_compress: bool) -> std::io::Result<()> { - let walk_dir = WalkDir::new(path); - for entry in walk_dir.into_iter().filter_map(|e| e.ok()) { - let entry_path = entry.path(); - if entry_path.is_file() { - if pre_compress { - if let Err(err) = pre_compress_file(entry_path) { - tracing::error!("Failed to pre-compress file {entry_path:?}: {err}"); - } - } - // If pre-compression isn't enabled, we should remove the old compressed file if it exists - else if let Some(compressed_path) = compressed_path(entry_path) { - _ = std::fs::remove_file(compressed_path); - } - } - } - Ok(()) -} - // use swc::{config::JsMinifyOptions, try_with_handler, BoolOrDataConfig}; // use swc_common::{sync::Lrc, FileName}; // use swc_common::{SourceMap, GLOBALS}; diff --git a/packages/cli/src/builder/builder.rs b/packages/cli/src/builder/builder.rs index 830981b488..28b092650c 100644 --- a/packages/cli/src/builder/builder.rs +++ b/packages/cli/src/builder/builder.rs @@ -56,6 +56,8 @@ impl Builder { pub(crate) fn build(&mut self, args: BuildArgs) -> Result<()> { self.abort_all(); + super::profiles::initialize_profiles(&self.krate)?; + let mut requests = vec![ // At least one request for the target app BuildRequest::new_client(&self.krate, args.clone(), self.channel.0.clone()), @@ -63,7 +65,6 @@ impl Builder { // And then the fullstack app if we're building a fullstack app if args.fullstack { - super::profiles::initialize_profiles(&self.krate)?; let server = BuildRequest::new_server(&self.krate, args.clone(), self.tx()); requests.push(server); } @@ -71,6 +72,7 @@ impl Builder { // Queue the builds on the joinset, being careful to not panic, so we can unwrap for build_request in requests { let platform = build_request.platform(); + tracing::info!("Spawning build request for {platform:?}"); self.building.spawn(async move { // Run the build, but in a protected spawn, ensuring we can't produce panics and thus, joinerrors let res = tokio::spawn(build_request.build()) diff --git a/packages/cli/src/builder/cargo.rs b/packages/cli/src/builder/cargo.rs index 898c60248b..e7edbf86b3 100644 --- a/packages/cli/src/builder/cargo.rs +++ b/packages/cli/src/builder/cargo.rs @@ -70,7 +70,7 @@ impl BuildRequest { // Pass in the tmp_file as the env var itself // // NOTE: that -Csave-temps=y is needed to prevent rustc from deleting the incremental cache... - // This might not be a "stable" way of keeping artifacts around, but it's in stable rustc + // This might not be a "stable" way of keeping artifacts around, but it's in stable rustc, so we use it Command::new("cargo") .arg("rustc") .args(self.build_arguments()) diff --git a/packages/cli/src/builder/request.rs b/packages/cli/src/builder/request.rs index 7e4485f786..85205808a7 100644 --- a/packages/cli/src/builder/request.rs +++ b/packages/cli/src/builder/request.rs @@ -28,7 +28,7 @@ pub(crate) struct BuildRequest { } impl BuildRequest { - pub(crate) fn new_server( + pub(crate) fn new_client( krate: &DioxusCrate, mut build: BuildArgs, progress: ProgressTx, @@ -37,18 +37,28 @@ impl BuildRequest { build.profile = Some(CLIENT_PROFILE.to_string()); } - let client_feature = build.auto_detect_server_feature(krate); + let (client_feature, client_platform) = build.auto_detect_client_platform(krate); + + let client_feature = match build.platform { + Some(platform::Platform::Ios) => Some("mobile".to_string()), + Some(platform::Platform::Android) => Some("android".to_string()), + Some(plat) => Some(plat.to_string()), + None => client_feature, + }; + + let features = build.target_args.client_feature.clone().or(client_feature); + + tracing::info!("Client feature: {features:?}"); + let mut build = Self::new_with_target_directory_rust_flags_and_features( - krate, - &build, - build.target_args.server_feature.clone().or(client_feature), - progress, + krate, &build, features, progress, ); - build.build.platform = Some(platform::Platform::Server); + + build.build.platform = build.build.platform.or(Some(client_platform)); build } - pub(crate) fn new_client( + pub(crate) fn new_server( krate: &DioxusCrate, mut build: BuildArgs, progress: ProgressTx, @@ -56,15 +66,15 @@ impl BuildRequest { if build.profile.is_none() { build.profile = Some(SERVER_PROFILE.to_string()); } - let (client_feature, client_platform) = build.auto_detect_client_platform(krate); + + let client_feature = build.auto_detect_server_feature(krate); + let features = build.target_args.server_feature.clone().or(client_feature); + tracing::info!("Server feature: {features:?}"); let mut build = Self::new_with_target_directory_rust_flags_and_features( - krate, - &build, - build.target_args.client_feature.clone().or(client_feature), - progress, + krate, &build, features, progress, ); - build.build.platform = Some(client_platform); + build.build.platform = Some(platform::Platform::Server); build } diff --git a/packages/cli/src/bundler/app.rs b/packages/cli/src/bundler/app.rs index ad1e86e6ff..3b231f22b3 100644 --- a/packages/cli/src/bundler/app.rs +++ b/packages/cli/src/bundler/app.rs @@ -66,7 +66,7 @@ impl AppBundle { // Create the workdir and then clean its contents, in case it already exists fn prepare_workdir(&self) -> Result<()> { - _ = std::fs::remove_dir_all(&self.workdir); + // _ = std::fs::remove_dir_all(&self.workdir); _ = std::fs::create_dir_all(&self.workdir); Ok(()) } @@ -148,8 +148,13 @@ impl AppBundle { asset, ); - self.assets - .copy_asset_to(&asset_dir, asset, optimize, pre_compress)?; + let res = self + .assets + .copy_asset_to(&asset_dir, asset, optimize, pre_compress); + + if let Err(err) = res { + tracing::error!("Failed to copy asset {asset:?}: {err}"); + } self.build.status_finished_asset( assets_finished.fetch_add(1, std::sync::atomic::Ordering::SeqCst), diff --git a/packages/cli/src/bundler.rs b/packages/cli/src/bundler/format.rs similarity index 79% rename from packages/cli/src/bundler.rs rename to packages/cli/src/bundler/format.rs index 4c63fc289a..2026edb60d 100644 --- a/packages/cli/src/bundler.rs +++ b/packages/cli/src/bundler/format.rs @@ -1,12 +1,3 @@ -mod android; -mod ios; -mod mac; -mod web; -mod win; - -mod app; -pub(crate) use app::*; - use serde::{Deserialize, Serialize}; #[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Debug)] diff --git a/packages/cli/src/bundler/mod.rs b/packages/cli/src/bundler/mod.rs new file mode 100644 index 0000000000..6a3f98a4dc --- /dev/null +++ b/packages/cli/src/bundler/mod.rs @@ -0,0 +1,9 @@ +mod android; +mod format; +mod ios; +mod mac; +mod web; +mod win; + +mod app; +pub(crate) use app::*; diff --git a/packages/cli/src/cli/build.rs b/packages/cli/src/cli/build.rs index 6fab5fe07b..d00bee1b15 100644 --- a/packages/cli/src/cli/build.rs +++ b/packages/cli/src/cli/build.rs @@ -122,9 +122,14 @@ impl BuildArgs { self.resolve(dioxus_crate)?; // todo: probably want to consume the logs from the builder here, instead of just waiting for it to finish - Builder::start(dioxus_crate, self.clone())? + let bundles = Builder::start(dioxus_crate, self.clone())? .wait_for_finish() - .await; + .await?; + + for bundle in bundles { + let destination = dioxus_crate.out_dir(); + bundle.finish(destination).await?; + } Ok(()) } diff --git a/packages/cli/src/cli/serve.rs b/packages/cli/src/cli/serve.rs index be8c6e2222..b11b9bd7b6 100644 --- a/packages/cli/src/cli/serve.rs +++ b/packages/cli/src/cli/serve.rs @@ -91,7 +91,7 @@ impl ServeArgs { self.build_arguments.resolve(crate_config)?; // Since this is a serve, adjust the outdir to be target/dx-dist/ - let mut dist_dir = crate_config.workspace_dir().join("target").join("dx-dist"); + let mut dist_dir = crate_config.out_dir(); if crate_config.target.is_example() { dist_dir = dist_dir.join("examples"); diff --git a/packages/cli/src/dioxus_crate.rs b/packages/cli/src/dioxus_crate.rs index 9511d4d83d..c4e07c293f 100644 --- a/packages/cli/src/dioxus_crate.rs +++ b/packages/cli/src/dioxus_crate.rs @@ -83,12 +83,15 @@ impl DioxusCrate { files } + pub(crate) fn bundle_out_dir(&self) -> PathBuf { + todo!("bundle out dir") + } + /// Compose an out directory. Represents the typical "dist" directory that /// is "distributed" after building an application (configurable in the /// `Dioxus.toml`). pub(crate) fn out_dir(&self) -> PathBuf { - self.workspace_dir() - .join(&self.dioxus_config.application.out_dir) + self.workspace_dir().join("target").join("dx-dist") } /// Get the workspace directory for the crate diff --git a/packages/cli/src/fastfs.rs b/packages/cli/src/fastfs.rs index bbf836ef02..a20a2981aa 100644 --- a/packages/cli/src/fastfs.rs +++ b/packages/cli/src/fastfs.rs @@ -2,3 +2,125 @@ //! Uses stuff like rayon, caching, and other optimizations //! //! Allows configuration in case you want to do some work while copying and allows you to track progress + +use std::{ + ffi::OsString, + path::{Path, PathBuf}, +}; + +use brotli::enc::BrotliEncoderParams; +use walkdir::WalkDir; + +pub fn copy_asset(src: &Path, dest: &Path) -> std::io::Result<()> { + if src.is_dir() { + copy_dir_to(src, dest, false)?; + } else { + std::fs::copy(src, dest)?; + } + + Ok(()) +} + +pub(crate) fn copy_dir_to( + src_dir: &Path, + dest_dir: &Path, + pre_compress: bool, +) -> std::io::Result<()> { + let entries = std::fs::read_dir(&src_dir)?; + let mut children: Vec>> = Vec::new(); + + for entry in entries.flatten() { + let entry_path = entry.path(); + let path_relative_to_src = entry_path.strip_prefix(&src_dir).unwrap(); + let output_file_location = dest_dir.join(path_relative_to_src); + children.push(std::thread::spawn(move || { + if entry.file_type()?.is_dir() { + // If the file is a directory, recursively copy it into the output directory + if let Err(err) = copy_dir_to(&entry_path, &output_file_location, pre_compress) { + tracing::error!( + "Failed to pre-compress directory {}: {}", + entry_path.display(), + err + ); + } + } else { + // Make sure the directory exists + std::fs::create_dir_all(output_file_location.parent().unwrap())?; + // Copy the file to the output directory + std::fs::copy(&entry_path, &output_file_location)?; + + // Then pre-compress the file if needed + if pre_compress { + if let Err(err) = pre_compress_file(&output_file_location) { + tracing::error!( + "Failed to pre-compress static assets {}: {}", + output_file_location.display(), + err + ); + } + // If pre-compression isn't enabled, we should remove the old compressed file if it exists + } else if let Some(compressed_path) = compressed_path(&output_file_location) { + _ = std::fs::remove_file(compressed_path); + } + } + + Ok(()) + })); + } + for child in children { + child.join().unwrap()?; + } + Ok(()) +} + +/// Get the path to the compressed version of a file +fn compressed_path(path: &Path) -> Option { + let new_extension = match path.extension() { + Some(ext) => { + if ext.to_string_lossy().to_lowercase().ends_with("br") { + return None; + } + let mut ext = ext.to_os_string(); + ext.push(".br"); + ext + } + None => OsString::from("br"), + }; + + Some(path.with_extension(new_extension)) +} + +/// pre-compress a file with brotli +pub(crate) fn pre_compress_file(path: &Path) -> std::io::Result<()> { + let Some(compressed_path) = compressed_path(path) else { + return Ok(()); + }; + + let file = std::fs::File::open(path)?; + let mut stream = std::io::BufReader::new(file); + let mut buffer = std::fs::File::create(compressed_path)?; + let params = BrotliEncoderParams::default(); + brotli::BrotliCompress(&mut stream, &mut buffer, ¶ms)?; + + Ok(()) +} + +/// pre-compress all files in a folder +pub(crate) fn pre_compress_folder(path: &Path, pre_compress: bool) -> std::io::Result<()> { + let walk_dir = WalkDir::new(path); + for entry in walk_dir.into_iter().filter_map(|e| e.ok()) { + let entry_path = entry.path(); + if entry_path.is_file() { + if pre_compress { + if let Err(err) = pre_compress_file(entry_path) { + tracing::error!("Failed to pre-compress file {entry_path:?}: {err}"); + } + } + // If pre-compression isn't enabled, we should remove the old compressed file if it exists + else if let Some(compressed_path) = compressed_path(entry_path) { + _ = std::fs::remove_file(compressed_path); + } + } + } + Ok(()) +} diff --git a/packages/cli/src/serve/handle.rs b/packages/cli/src/serve/handle.rs index 2a55ca15b1..1d295fbd74 100644 --- a/packages/cli/src/serve/handle.rs +++ b/packages/cli/src/serve/handle.rs @@ -55,8 +55,7 @@ impl AppHandle { // web can't be configured like this, so instead, we'll need to plumb a meta tag into the // index.html during dev match handle.app.build.platform() { - Platform::Web => {} - Platform::Desktop => { + Platform::Desktop | Platform::Server | Platform::Liveview => { let mut cmd = Command::new(handle.executable.clone()); cmd.env( dioxus_runtime_config::FULLSTACK_ADDRESS_ENV, @@ -89,10 +88,9 @@ impl AppHandle { handle.stderr = Some(stderr.lines()); handle.child = Some(child); } + Platform::Web => {} Platform::Ios => {} Platform::Android => {} - Platform::Server => {} - Platform::Liveview => {} } Ok(handle) @@ -105,7 +103,8 @@ impl AppHandle { pub(crate) fn hotreload_asset(&self, path: &PathBuf) -> Option { let resource = self.app.assets.assets.get(path).cloned()?; - self.app + _ = self + .app .assets .copy_asset_to(&self.app.asset_dir(), path, false, false); diff --git a/packages/cli/src/serve/tracer.rs b/packages/cli/src/serve/tracer.rs index e1ebfe27dc..5b7d8fe26a 100644 --- a/packages/cli/src/serve/tracer.rs +++ b/packages/cli/src/serve/tracer.rs @@ -27,7 +27,7 @@ impl TraceController { // redirected to the serve process. // If {LOG_ENV} is set, default to env, otherwise filter to cli // and manganis warnings and errors from other crates - let mut filter = EnvFilter::new("error,dx=info,dioxus-cli=info"); + let mut filter = EnvFilter::new("error,dx=info,devdx=info,dioxus-cli=info"); if env::var(LOG_ENV).is_ok() { filter = EnvFilter::from_env(LOG_ENV); diff --git a/packages/fullstack/src/document/web.rs b/packages/fullstack/src/document/web.rs index ab46d9ded1..336e1e48af 100644 --- a/packages/fullstack/src/document/web.rs +++ b/packages/fullstack/src/document/web.rs @@ -12,7 +12,13 @@ fn head_element_written_on_server() -> bool { } pub(crate) struct FullstackWebDocument { - document: WebDocument, + // document: WebDocument, +} + +impl FullstackWebDocument { + pub(crate) fn new() -> Self { + Self {} + } } impl Document for FullstackWebDocument { @@ -26,8 +32,8 @@ impl Document for FullstackWebDocument { return; } - self.document - .create_head_element(name, attributes, contents); + // self.document + // .create_head_element(name, attributes, contents); } fn set_title(&self, title: String) { @@ -35,11 +41,12 @@ impl Document for FullstackWebDocument { return; } - self.document.set_title(title); + // self.document.set_title(title); } fn eval(&self, js: String) -> dioxus_document::Eval { - self.document.eval(js) + todo!() + // self.document.eval(js) } fn as_any(&self) -> &dyn std::any::Any { diff --git a/packages/fullstack/src/launch.rs b/packages/fullstack/src/launch.rs index ec518f6f8a..53b092956d 100644 --- a/packages/fullstack/src/launch.rs +++ b/packages/fullstack/src/launch.rs @@ -62,10 +62,9 @@ pub fn launch( #[cfg(feature = "document")] let factory = move || { let mut vdom = factory(); - todo!("Fullstack web document???"); - // let document = std::rc::Rc::new(crate::document::web::FullstackWebDocument) - // as std::rc::Rc; - // vdom.provide_root_context(document); + let document = std::rc::Rc::new(crate::document::web::FullstackWebDocument::new()) + as std::rc::Rc; + vdom.provide_root_context(document); vdom }; @@ -162,7 +161,7 @@ async fn launch_server( } let router = router.into_make_service(); - let listener = tokio::net::TcpListener::bind(address).await.unwrap(); + let listener = tokio::net::TcpListener::bind(serve_address).await.unwrap(); axum::serve(listener, router).await.unwrap(); } diff --git a/packages/web/ric_raf/README.md b/packages/web/ric_raf/README.md deleted file mode 100644 index b101360f7d..0000000000 --- a/packages/web/ric_raf/README.md +++ /dev/null @@ -1,3 +0,0 @@ -requestIdleCallback and requestAnimationFrame implementation - -These currently actually slow down our DOM patching and thus are temporarily removed. Technically we can schedule around rIC and rAF but choose not to. diff --git a/packages/web/ric_raf/ric_raf.rs b/packages/web/ric_raf/ric_raf.rs deleted file mode 100644 index 89819fff09..0000000000 --- a/packages/web/ric_raf/ric_raf.rs +++ /dev/null @@ -1,80 +0,0 @@ -//! This module provides some utilities around scheduling tasks on the main thread of the browser. -//! -//! The ultimate goal here is to not block the main thread during animation frames, so our animations don't result in "jank". -//! -//! Hence, this module provides Dioxus "Jank Free Rendering" on the web. -//! -//! Because RIC doesn't work on Safari, we polyfill using the "ricpolyfill.js" file and use some basic detection to see -//! if RIC is available. - -use futures_util::StreamExt; -use gloo_timers::future::TimeoutFuture; -use js_sys::Function; -use wasm_bindgen::{prelude::Closure, JsCast, JsValue}; -use web_sys::{window, Window}; - -pub(crate) struct RafLoop { - window: Window, - ric_receiver: futures_channel::mpsc::UnboundedReceiver, - raf_receiver: futures_channel::mpsc::UnboundedReceiver<()>, - ric_closure: Closure, - raf_closure: Closure, -} - -impl RafLoop { - pub fn new() -> Self { - let (raf_sender, raf_receiver) = futures_channel::mpsc::unbounded(); - - let raf_closure: Closure = Closure::wrap(Box::new(move |_v: JsValue| { - raf_sender.unbounded_send(()).unwrap() - })); - - let (ric_sender, ric_receiver) = futures_channel::mpsc::unbounded(); - - let has_idle_callback = { - let bo = window().unwrap().dyn_into::().unwrap(); - bo.has_own_property(&JsValue::from_str("requestIdleCallback")) - }; - let ric_closure: Closure = Closure::wrap(Box::new(move |v: JsValue| { - let time_remaining = if has_idle_callback { - if let Ok(deadline) = v.dyn_into::() { - deadline.time_remaining() as u32 - } else { - 10 - } - } else { - 10 - }; - - ric_sender.unbounded_send(time_remaining).unwrap() - })); - - // execute the polyfill for safari - Function::new_no_args(include_str!("./ricpolyfill.js")) - .call0(&JsValue::NULL) - .unwrap(); - - let window = web_sys::window().unwrap(); - - Self { - window, - raf_receiver, - raf_closure, - ric_receiver, - ric_closure, - } - } - /// waits for some idle time and returns a timeout future that expires after the idle time has passed - pub async fn wait_for_idle_time(&mut self) -> TimeoutFuture { - let ric_fn = self.ric_closure.as_ref().dyn_ref::().unwrap(); - let _cb_id: u32 = self.window.request_idle_callback(ric_fn).unwrap(); - let deadline = self.ric_receiver.next().await.unwrap(); - TimeoutFuture::new(deadline) - } - - pub async fn wait_for_raf(&mut self) { - let raf_fn = self.raf_closure.as_ref().dyn_ref::().unwrap(); - let _id: i32 = self.window.request_animation_frame(raf_fn).unwrap(); - self.raf_receiver.next().await.unwrap(); - } -} diff --git a/packages/web/ric_raf/ricpolyfill.js b/packages/web/ric_raf/ricpolyfill.js deleted file mode 100644 index cd114f4caf..0000000000 --- a/packages/web/ric_raf/ricpolyfill.js +++ /dev/null @@ -1,28 +0,0 @@ -const requestIdleCallback = - (typeof self !== 'undefined' && - self.requestIdleCallback && - self.requestIdleCallback.bind(window)) || - function (cb) { - const start = Date.now(); - return setTimeout(() => { - cb({ - didTimeout: false, - timeRemaining: function () { - return Math.max(0, 50 - (Date.now() - start)); - }, - }); - }, 1); - }; - -const cancelIdleCallback = - (typeof self !== 'undefined' && - self.cancelIdleCallback && - self.cancelIdleCallback.bind(window)) || - function (id) { - return clearTimeout(id); - }; - -if (typeof window !== 'undefined') { - window.requestIdleCallback = requestIdleCallback; - window.cancelIdleCallback = cancelIdleCallback; -} diff --git a/packages/web/src/hydration/deserialize.rs b/packages/web/src/hydration/deserialize.rs index f97677e51b..00fadb0a8e 100644 --- a/packages/web/src/hydration/deserialize.rs +++ b/packages/web/src/hydration/deserialize.rs @@ -44,9 +44,9 @@ pub(crate) struct HTMLDataCursor { } impl HTMLDataCursor { - pub(crate) fn from_serialized(data: &[u8]) -> Self { - let deserialized = ciborium::from_reader(Cursor::new(data)).unwrap(); - Self::new(deserialized) + pub(crate) fn from_serialized(data: &[u8]) -> Option { + let deserialized: Vec>> = ciborium::from_reader(Cursor::new(data)).ok()?; + Some(Self::new(deserialized)) } /// Get the error if there is one diff --git a/packages/web/src/hydration/hydrate.rs b/packages/web/src/hydration/hydrate.rs index a362eddef5..a10f97c384 100644 --- a/packages/web/src/hydration/hydrate.rs +++ b/packages/web/src/hydration/hydrate.rs @@ -25,6 +25,8 @@ pub(crate) enum RehydrationError { SuspenseHydrationIdNotFound, /// The client tried to rehydrate a dom id that was not found on the server ElementNotFound, + /// Could not deserialize the data, might not be an issue? + MissingData, } #[derive(Debug)] @@ -138,7 +140,8 @@ impl WebsysDom { self.interpreter.base().push_root(node); } - let server_data = HTMLDataCursor::from_serialized(&data); + let server_data = + HTMLDataCursor::from_serialized(&data).ok_or_else(|| RehydrationError::MissingData)?; // If the server serialized an error into the suspense boundary, throw it on the client so that it bubbles up to the nearest error boundary if let Some(error) = server_data.error() { dom.in_runtime(|| id.throw_error(error)); diff --git a/packages/web/src/launch.rs b/packages/web/src/launch.rs index 4cc421e9d8..0c5059a92a 100644 --- a/packages/web/src/launch.rs +++ b/packages/web/src/launch.rs @@ -19,6 +19,11 @@ pub fn launch( launch_virtual_dom(vdom, platform_config) } +/// Launch the web application with the given root component and config +pub fn launch_cfg(root: fn() -> Element, platform_config: Config) { + launch(root, Vec::new(), platform_config); +} + /// Launch the web application with a prebuild virtual dom /// /// For a builder API, see `LaunchBuilder` defined in the `dioxus` crate. @@ -27,8 +32,3 @@ pub fn launch_virtual_dom(vdom: VirtualDom, platform_config: Config) { crate::run(vdom, platform_config).await; }); } - -/// Launch the web application with the given root component and config -pub fn launch_cfg(root: fn() -> Element, platform_config: Config) { - launch(root, Vec::new(), platform_config); -} diff --git a/packages/web/src/lib.rs b/packages/web/src/lib.rs index 8fc3b6c971..1683163c29 100644 --- a/packages/web/src/lib.rs +++ b/packages/web/src/lib.rs @@ -87,6 +87,10 @@ pub async fn run(mut virtual_dom: VirtualDom, web_config: Config) -> ! { // Get the initial hydration data from the client #[wasm_bindgen::prelude::wasm_bindgen(inline_js = r#" export function get_initial_hydration_data() { + if (window.initial_dioxus_hydration_data === undefined) { + return new Uint8Array(); + } + const decoded = atob(window.initial_dioxus_hydration_data); return Uint8Array.from(decoded, (c) => c.charCodeAt(0)) } @@ -95,18 +99,23 @@ pub async fn run(mut virtual_dom: VirtualDom, web_config: Config) -> ! { fn get_initial_hydration_data() -> js_sys::Uint8Array; } let hydration_data = get_initial_hydration_data().to_vec(); - let server_data = HTMLDataCursor::from_serialized(&hydration_data); - // If the server serialized an error into the root suspense boundary, throw it into the root scope - if let Some(error) = server_data.error() { - virtual_dom.in_runtime(|| dioxus_core::ScopeId::APP.throw_error(error)); - } - with_server_data(server_data, || { - virtual_dom.rebuild(&mut websys_dom); - }); - websys_dom.skip_mutations = false; - let rx = websys_dom.rehydrate(&virtual_dom).unwrap(); - hydration_receiver = Some(rx); + if let Some(server_data) = HTMLDataCursor::from_serialized(&hydration_data) { + // If the server serialized an error into the root suspense boundary, throw it into the root scope + if let Some(error) = server_data.error() { + virtual_dom.in_runtime(|| dioxus_core::ScopeId::APP.throw_error(error)); + } + with_server_data(server_data, || { + virtual_dom.rebuild(&mut websys_dom); + }); + websys_dom.skip_mutations = false; + + let rx = websys_dom + .rehydrate(&virtual_dom) + .expect("Failed to rehydrate"); + + hydration_receiver = Some(rx); + } } #[cfg(not(feature = "hydrate"))] { From 01d8863c08ba9ed9765513aa433148274708590f Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 5 Sep 2024 03:29:17 -0700 Subject: [PATCH 075/139] fullstack mobile demo --- example-projects/fullstack-mobile/.gitignore | 2 + example-projects/fullstack-mobile/Cargo.toml | 34 + .../assets/dogs/cute/dog0.jpg | Bin 0 -> 39179 bytes .../assets/dogs/cute/dog1.jpg | Bin 0 -> 32904 bytes .../assets/dogs/cute/dog2.jpg | Bin 0 -> 46631 bytes .../assets/dogs/cute/dog3.jpg | Bin 0 -> 25476 bytes .../assets/dogs/cute/dog4.jpg | Bin 0 -> 38016 bytes .../assets/dogs/cute/dog5.jpg | Bin 0 -> 41820 bytes .../assets/dogs/fluffy/dog0.jpg | Bin 0 -> 7483 bytes .../assets/dogs/fluffy/dog1.jpg | Bin 0 -> 19694 bytes .../assets/dogs/fluffy/dog2.jpg | Bin 0 -> 39094 bytes .../assets/dogs/fluffy/dog3.jpg | Bin 0 -> 16840 bytes .../assets/dogs/fluffy/dog4.jpg | Bin 0 -> 30428 bytes .../assets/dogs/fluffy/dog5.jpg | Bin 0 -> 419636 bytes .../fullstack-mobile/assets/favicon.ico | Bin 0 -> 9662 bytes .../fullstack-mobile/assets/header.svg | 20 + .../fullstack-mobile/assets/style.css | 29 + .../fullstack-mobile/assets/tailwind.css | 664 ++++++++++++++++++ .../fullstack-mobile/makefile.toml | 45 ++ example-projects/fullstack-mobile/src/main.rs | 37 + .../fullstack-mobile/tailwind.config.js | 42 ++ .../fullstack-mobile/tailwind.css | 3 + .../tools/make-entitlements.sh | 41 ++ .../fullstack-mobile/tools/run-ios-device.sh | 34 + packages/fullstack/src/document/web.rs | 12 +- packages/web/src/devtools.rs | 9 +- packages/web/src/document.rs | 22 +- packages/web/src/hydration/mod.rs | 18 + packages/web/src/lib.rs | 21 +- 29 files changed, 1002 insertions(+), 31 deletions(-) create mode 100644 example-projects/fullstack-mobile/.gitignore create mode 100644 example-projects/fullstack-mobile/Cargo.toml create mode 100644 example-projects/fullstack-mobile/assets/dogs/cute/dog0.jpg create mode 100644 example-projects/fullstack-mobile/assets/dogs/cute/dog1.jpg create mode 100644 example-projects/fullstack-mobile/assets/dogs/cute/dog2.jpg create mode 100644 example-projects/fullstack-mobile/assets/dogs/cute/dog3.jpg create mode 100644 example-projects/fullstack-mobile/assets/dogs/cute/dog4.jpg create mode 100644 example-projects/fullstack-mobile/assets/dogs/cute/dog5.jpg create mode 100644 example-projects/fullstack-mobile/assets/dogs/fluffy/dog0.jpg create mode 100644 example-projects/fullstack-mobile/assets/dogs/fluffy/dog1.jpg create mode 100644 example-projects/fullstack-mobile/assets/dogs/fluffy/dog2.jpg create mode 100644 example-projects/fullstack-mobile/assets/dogs/fluffy/dog3.jpg create mode 100644 example-projects/fullstack-mobile/assets/dogs/fluffy/dog4.jpg create mode 100644 example-projects/fullstack-mobile/assets/dogs/fluffy/dog5.jpg create mode 100644 example-projects/fullstack-mobile/assets/favicon.ico create mode 100644 example-projects/fullstack-mobile/assets/header.svg create mode 100644 example-projects/fullstack-mobile/assets/style.css create mode 100644 example-projects/fullstack-mobile/assets/tailwind.css create mode 100644 example-projects/fullstack-mobile/makefile.toml create mode 100644 example-projects/fullstack-mobile/src/main.rs create mode 100644 example-projects/fullstack-mobile/tailwind.config.js create mode 100644 example-projects/fullstack-mobile/tailwind.css create mode 100755 example-projects/fullstack-mobile/tools/make-entitlements.sh create mode 100755 example-projects/fullstack-mobile/tools/run-ios-device.sh diff --git a/example-projects/fullstack-mobile/.gitignore b/example-projects/fullstack-mobile/.gitignore new file mode 100644 index 0000000000..a0f8b31977 --- /dev/null +++ b/example-projects/fullstack-mobile/.gitignore @@ -0,0 +1,2 @@ +/target +/ignore diff --git a/example-projects/fullstack-mobile/Cargo.toml b/example-projects/fullstack-mobile/Cargo.toml new file mode 100644 index 0000000000..dddc66fe13 --- /dev/null +++ b/example-projects/fullstack-mobile/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "DioxusApp" +version = "0.1.0" +edition = "2021" + +[dependencies] +dioxus = { path = "../../dioxus/packages/dioxus", features = ["fullstack"] } +# reqwest = { version = "0.12.7", features = ["json"] } +# serde = "1.0.208" +# dioxus = { path = "../../dioxus/packages/dioxus", features = ["mobile"] } +# dioxus-runtime-config = { path = "../../dioxus/packages/runtime-config" } + +[features] +desktop = ["dioxus/desktop"] +mobile = ["dioxus/mobile"] +web = ["dioxus/web"] +server = ["dioxus/server"] + +[package.metadata.cargo-apple.ios] +frameworks = [ "WebKit" ] + +[package.metadata.bundle] +name = "DioxusApp" +identifier = "com.dioxuslabs" +resources = ["assets/**/*"] +category = "Utility" +short_description = "An example of a bundled application" +long_description = """ +A trivial application that just displays a blank window with +a title bar. It serves as an example of an application that +can be bundled with cargo-bundle, as well as a test-case for +cargo-bundle's support for bundling crate examples. +""" +osx_frameworks = ["WebKit"] diff --git a/example-projects/fullstack-mobile/assets/dogs/cute/dog0.jpg b/example-projects/fullstack-mobile/assets/dogs/cute/dog0.jpg new file mode 100644 index 0000000000000000000000000000000000000000..519d90ad1064ed8c4a3f29276612fa7f22124cc4 GIT binary patch literal 39179 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 WxzE5#isy|g~S4@_R<6Od+ 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 literal 0 HcmV?d00001 diff --git a/example-projects/fullstack-mobile/assets/dogs/cute/dog1.jpg b/example-projects/fullstack-mobile/assets/dogs/cute/dog1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..12d029dab633a39c95707f25819f11518db47fbc GIT binary patch literal 32904 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 literal 0 HcmV?d00001 diff --git a/example-projects/fullstack-mobile/assets/dogs/cute/dog2.jpg b/example-projects/fullstack-mobile/assets/dogs/cute/dog2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0c27351dc16368c1b11f0701240b60e28d607939 GIT binary patch literal 46631 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 literal 0 HcmV?d00001 diff --git a/example-projects/fullstack-mobile/assets/dogs/cute/dog3.jpg b/example-projects/fullstack-mobile/assets/dogs/cute/dog3.jpg new file mode 100644 index 0000000000000000000000000000000000000000..98cf2f25048cd0f54ba44c92613296cc796d2f63 GIT binary patch literal 25476 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�o9%t_!bJ?NmV3Yc@oaBQ@`5qxPR1CVbDvI> z3_?nb@>jM^JAdxSZ)!%t0OSGdOgI1nK{@Ja2zxtcJBSpAeh1tVZBN z0gq5=fgbaNo!^NxzGoQ)hvQ9Vh0Ox6p@K2v9QLP%Tyj^hdW?~{Qj#;%tpQ=gf(a)B-jPmM91~HJbB11f15Zwe zAf9Lo4<`ii_~M#eki>30XCZo19Jb-qQG#Q}?kEcq1GoZQ^#>FHt&iRiGBKX?!0p#L z{3*&qk-*PhXbTQU0eAt5oE)B&UUMtpM&dR+>>qjJ{aw#|`3Sm#r2H4kNI~uRK&`0XzeWHxLx(B#d%;Qy7&aoO8x0 zq)?2CpDE{_w8AmR82VFKoQ#l8X!A3G3FP)YsfC1QA1|@z6w=Ch0|3;H=ly}W@NrQ8 z01ih0pIQKGh0fmRps0vFv&W@9!2m8t9SukKhfYUP>p)mfm3Sl{#}rVco|P#o5N#ZHqKcBo4B;TEBSP}{CMHC1`EHSWT4x_zJ zNF){qXFLziiYW+Kq*nQeJw`#PH=ORrOwmOEHCzG|01rK>4iJ|heMJ;f0ylLlhR-Jl zp0t6+;8z(mQ9#h4_Ln0B^fWBzEzbgqC<_W?J9zvkR$@w=7wwuTqym@u!R`k%Jfnfm zPp4Wapa{X*bGV!goYL+&>PYL>iYb8Y`RskUr9A+_9D4Plib4Q3oRRgV4mT3tg%nUR zu1jQg^%V3{y-rRjqL_zp+>G?%o4X1!KZ_kb=%RoC#4BVBk9tVicXQl*Xrh1*kUt08 z=}vR#2Tw{UphRJU=LZMAJ!#7$bOanxMF0%Mf`1xd9FfR0Q9#3uqpn3N;~aLPieMpO z*@>lG9DK%#C;@YW$;Bg*2d^|yK!n`eNGG14`%-zC?adTW1JQ|oc&BgLIVOrJjAIp1 zllW5|gYme6D58N7+N6Gxyx*{l3rqxANZ>fKE?KR|`Nu000pDI{^Q! z0UiR#NJvOYh{;GvNy*8{D5#jIsVFI_Kn#quOzf zl#!9)6p&YjNhyg-%SiptAOz&(&x1ok!@^@<$HgZkCLuGkvU76Z=H(ZZl~-UY ztEy{iTUy(2?H!$6?>-I;4h@ftj*ZVREG{j7TKT-Xy|cTwe}Mmbc=Y4^;_~YH=I8CN z|KTD40RK1Ezw7@7`+soJ|KlPgA_5YT{tp)cVaUG|NKZt}ElI+lZc6GDz{n#NO~#~= zQQF*34v{wd&g>jGM{$o=W}ENF|DgRZvj2BrG5^2F{x7iqn`;$71tj?Q@__UJReQlbwkOwQWp~_L8iw+7HR=dn1 ^`W?tDh;l)jzSBaiVxEHqez=bVT~a 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 P*WU#daok~4w~PM^iF>U1 literal 0 HcmV?d00001 diff --git a/example-projects/fullstack-mobile/assets/dogs/fluffy/dog1.jpg b/example-projects/fullstack-mobile/assets/dogs/fluffy/dog1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..dd6043485e64702b6f8c68a5c8ba38a27583554e GIT binary patch literal 19694 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

    XWd)q{&(J4kt*WM3pME^KcNe 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 z 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 z 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�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

    ZhS>uFx;AG$tNeu5ee;-x#5-^i^<(S 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 AO8@`> literal 0 HcmV?d00001 diff --git a/example-projects/fullstack-mobile/assets/favicon.ico b/example-projects/fullstack-mobile/assets/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..4b2cfff1ed3b1292ce273d0dc5a5ab3ae2a28f39 GIT binary patch literal 9662 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 \ No newline at end of file diff --git a/example-projects/fullstack-mobile/assets/style.css b/example-projects/fullstack-mobile/assets/style.css new file mode 100644 index 0000000000..bf9ae32440 --- /dev/null +++ b/example-projects/fullstack-mobile/assets/style.css @@ -0,0 +1,29 @@ +/* html, body { + height: 500px; + width: 500px; +} */ + +body { + color: "white"; + background-color: black; + /* background-color: #111216; */ + color: white; + font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; + /* overflow: hidden; */ + height: 100vh; +} + +body .sparkles { + color: yellow; +} +.container { + height: 100%; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.dog-pic { + border-radius: 10px; +} diff --git a/example-projects/fullstack-mobile/assets/tailwind.css b/example-projects/fullstack-mobile/assets/tailwind.css new file mode 100644 index 0000000000..8030ad5220 --- /dev/null +++ b/example-projects/fullstack-mobile/assets/tailwind.css @@ -0,0 +1,664 @@ +/* +! tailwindcss v3.4.10 | MIT License | https://tailwindcss.com +*/ + +/* +1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) +2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) +*/ + +*, +::before, +::after { + box-sizing: border-box; + /* 1 */ + border-width: 0; + /* 2 */ + border-style: solid; + /* 2 */ + border-color: #e5e7eb; + /* 2 */ +} + +::before, +::after { + --tw-content: ''; +} + +/* +1. Use a consistent sensible line-height in all browsers. +2. Prevent adjustments of font size after orientation changes in iOS. +3. Use a more readable tab size. +4. Use the user's configured `sans` font-family by default. +5. Use the user's configured `sans` font-feature-settings by default. +6. Use the user's configured `sans` font-variation-settings by default. +7. Disable tap highlights on iOS +*/ + +html, +:host { + line-height: 1.5; + /* 1 */ + -webkit-text-size-adjust: 100%; + /* 2 */ + -moz-tab-size: 4; + /* 3 */ + -o-tab-size: 4; + tab-size: 4; + /* 3 */ + font-family: Inter var, sans-serif; + /* 4 */ + font-feature-settings: normal; + /* 5 */ + font-variation-settings: normal; + /* 6 */ + -webkit-tap-highlight-color: transparent; + /* 7 */ +} + +/* +1. Remove the margin in all browsers. +2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. +*/ + +body { + margin: 0; + /* 1 */ + line-height: inherit; + /* 2 */ +} + +/* +1. Add the correct height in Firefox. +2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) +3. Ensure horizontal rules are visible by default. +*/ + +hr { + height: 0; + /* 1 */ + color: inherit; + /* 2 */ + border-top-width: 1px; + /* 3 */ +} + +/* +Add the correct text decoration in Chrome, Edge, and Safari. +*/ + +abbr:where([title]) { + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; +} + +/* +Remove the default font size and weight for headings. +*/ + +h1, +h2, +h3, +h4, +h5, +h6 { + font-size: inherit; + font-weight: inherit; +} + +/* +Reset links to optimize for opt-in styling instead of opt-out. +*/ + +a { + color: inherit; + text-decoration: inherit; +} + +/* +Add the correct font weight in Edge and Safari. +*/ + +b, +strong { + font-weight: bolder; +} + +/* +1. Use the user's configured `mono` font-family by default. +2. Use the user's configured `mono` font-feature-settings by default. +3. Use the user's configured `mono` font-variation-settings by default. +4. Correct the odd `em` font sizing in all browsers. +*/ + +code, +kbd, +samp, +pre { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + /* 1 */ + font-feature-settings: normal; + /* 2 */ + font-variation-settings: normal; + /* 3 */ + font-size: 1em; + /* 4 */ +} + +/* +Add the correct font size in all browsers. +*/ + +small { + font-size: 80%; +} + +/* +Prevent `sub` and `sup` elements from affecting the line height in all browsers. +*/ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* +1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) +2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) +3. Remove gaps between table borders by default. +*/ + +table { + text-indent: 0; + /* 1 */ + border-color: inherit; + /* 2 */ + border-collapse: collapse; + /* 3 */ +} + +/* +1. Change the font styles in all browsers. +2. Remove the margin in Firefox and Safari. +3. Remove default padding in all browsers. +*/ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; + /* 1 */ + font-feature-settings: inherit; + /* 1 */ + font-variation-settings: inherit; + /* 1 */ + font-size: 100%; + /* 1 */ + font-weight: inherit; + /* 1 */ + line-height: inherit; + /* 1 */ + letter-spacing: inherit; + /* 1 */ + color: inherit; + /* 1 */ + margin: 0; + /* 2 */ + padding: 0; + /* 3 */ +} + +/* +Remove the inheritance of text transform in Edge and Firefox. +*/ + +button, +select { + text-transform: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Remove default button styles. +*/ + +button, +input:where([type='button']), +input:where([type='reset']), +input:where([type='submit']) { + -webkit-appearance: button; + /* 1 */ + background-color: transparent; + /* 2 */ + background-image: none; + /* 2 */ +} + +/* +Use the modern Firefox focus style for all focusable elements. +*/ + +:-moz-focusring { + outline: auto; +} + +/* +Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) +*/ + +:-moz-ui-invalid { + box-shadow: none; +} + +/* +Add the correct vertical alignment in Chrome and Firefox. +*/ + +progress { + vertical-align: baseline; +} + +/* +Correct the cursor style of increment and decrement buttons in Safari. +*/ + +::-webkit-inner-spin-button, +::-webkit-outer-spin-button { + height: auto; +} + +/* +1. Correct the odd appearance in Chrome and Safari. +2. Correct the outline style in Safari. +*/ + +[type='search'] { + -webkit-appearance: textfield; + /* 1 */ + outline-offset: -2px; + /* 2 */ +} + +/* +Remove the inner padding in Chrome and Safari on macOS. +*/ + +::-webkit-search-decoration { + -webkit-appearance: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Change font properties to `inherit` in Safari. +*/ + +::-webkit-file-upload-button { + -webkit-appearance: button; + /* 1 */ + font: inherit; + /* 2 */ +} + +/* +Add the correct display in Chrome and Safari. +*/ + +summary { + display: list-item; +} + +/* +Removes the default spacing and border for appropriate elements. +*/ + +blockquote, +dl, +dd, +h1, +h2, +h3, +h4, +h5, +h6, +hr, +figure, +p, +pre { + margin: 0; +} + +fieldset { + margin: 0; + padding: 0; +} + +legend { + padding: 0; +} + +ol, +ul, +menu { + list-style: none; + margin: 0; + padding: 0; +} + +/* +Reset default styling for dialogs. +*/ + +dialog { + padding: 0; +} + +/* +Prevent resizing textareas horizontally by default. +*/ + +textarea { + resize: vertical; +} + +/* +1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) +2. Set the default placeholder color to the user's configured gray 400 color. +*/ + +input::-moz-placeholder, textarea::-moz-placeholder { + opacity: 1; + /* 1 */ + color: #9ca3af; + /* 2 */ +} + +input::placeholder, +textarea::placeholder { + opacity: 1; + /* 1 */ + color: #9ca3af; + /* 2 */ +} + +/* +Set the default cursor for buttons. +*/ + +button, +[role="button"] { + cursor: pointer; +} + +/* +Make sure disabled buttons don't get the pointer cursor. +*/ + +:disabled { + cursor: default; +} + +/* +1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) +2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) + This can trigger a poorly considered lint error in some tools but is included by design. +*/ + +img, +svg, +video, +canvas, +audio, +iframe, +embed, +object { + display: block; + /* 1 */ + vertical-align: middle; + /* 2 */ +} + +/* +Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) +*/ + +img, +video { + max-width: 100%; + height: auto; +} + +/* Make elements with the HTML hidden attribute stay hidden by default */ + +[hidden] { + display: none; +} + +*, ::before, ::after { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; + --tw-contain-size: ; + --tw-contain-layout: ; + --tw-contain-paint: ; + --tw-contain-style: ; +} + +::backdrop { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; + --tw-contain-size: ; + --tw-contain-layout: ; + --tw-contain-paint: ; + --tw-contain-style: ; +} + +.container { + width: 100%; +} + +@media (min-width: 640px) { + .container { + max-width: 640px; + } +} + +@media (min-width: 768px) { + .container { + max-width: 768px; + } +} + +@media (min-width: 1024px) { + .container { + max-width: 1024px; + } +} + +@media (min-width: 1280px) { + .container { + max-width: 1280px; + } +} + +@media (min-width: 1536px) { + .container { + max-width: 1536px; + } +} + +.my-4 { + margin-top: 1rem; + margin-bottom: 1rem; +} + +.my-12 { + margin-top: 3rem; + margin-bottom: 3rem; +} + +.my-20 { + margin-top: 5rem; + margin-bottom: 5rem; +} + +.mx-auto { + margin-left: auto; + margin-right: auto; +} + +.flex { + display: flex; +} + +.h-full { + height: 100%; +} + +.w-full { + width: 100%; +} + +.flex-row { + flex-direction: row; +} + +.flex-col { + flex-direction: column; +} + +.justify-center { + justify-content: center; +} + +.space-y-2 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.5rem * var(--tw-space-y-reverse)); +} + +.space-y-4 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(1rem * var(--tw-space-y-reverse)); +} + +.space-y-12 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(3rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(3rem * var(--tw-space-y-reverse)); +} + +.text-center { + text-align: center; +} + +.text-4xl { + font-size: 2.25rem; + line-height: 2.5rem; +} + +.font-bold { + font-weight: 700; +} diff --git a/example-projects/fullstack-mobile/makefile.toml b/example-projects/fullstack-mobile/makefile.toml new file mode 100644 index 0000000000..f4b94eac19 --- /dev/null +++ b/example-projects/fullstack-mobile/makefile.toml @@ -0,0 +1,45 @@ +[tasks.build_ios_sim] +args = ["bundle", "--target", "aarch64-apple-ios-sim", "--features", "mobile"] +command = "cargo" + +[tasks.install_ios_sim] +command = "xcrun" +args = [ + "simctl", + "install", + "booted", + "target/aarch64-apple-ios-sim/debug/bundle/ios/DioxusApp.app", +] + +[tasks.run_ios_sim] +env = { SIMCTL_CHILD_DIOXUS_DEVSERVER_ADDR="ws://0.0.0.0:8080/_dioxus", SIMCTL_CHILD_CARGO_MANIFEST_DIR="/Users/jonkelley/Development/Tinkering/ios-binary"} +args = ["simctl", "launch", "--console", "booted", "com.dioxuslabs"] +command = "xcrun" +dependencies = ["build_ios_sim", "install_ios_sim"] + +[tasks.serve-sim] +dependencies = ["build_ios_sim", "install_ios_sim", "run_ios_sim"] + +[tasks.build_ios_device] +command = "cargo" +args = ["bundle", "--target", "aarch64-apple-ios"] + +[tasks.code-sign-ios-device] +command = "./tools/make-entitlements.sh" + +# god bless the crypto bros <3 +# https://github.com/status-im/status-mobile/blob/6a5e718cd389f61d3708c4372856609afb40d11f/scripts/run-ios-device.sh#L6 +[tasks.run-ios-device] +command = "./tools/run-ios-device.sh" + +[tasks.serve-device] +dependencies = [ + "build_ios_device", + "code-sign-ios-device", + "run-ios-device", +] + +[tasks.serve] +dependencies = [ + "run_ios_sim", +] diff --git a/example-projects/fullstack-mobile/src/main.rs b/example-projects/fullstack-mobile/src/main.rs new file mode 100644 index 0000000000..b43880de7e --- /dev/null +++ b/example-projects/fullstack-mobile/src/main.rs @@ -0,0 +1,37 @@ +use dioxus::prelude::*; + +fn main() { + dioxus::launch(app); +} + +fn app() -> Element { + rsx! { + document::Stylesheet { href: asset!("/assets/style.css") } + document::Stylesheet { href: asset!("/assets/tailwind.css") } + + div { class: "w-full h-full text-center my-20", + h1 { class: "text-4xl font-bold", "Dioxus iOS apps!" } + h3 { class: "sparkles", "!!zero-xcode!!!!!!!!!" } + button { onclick: move |_| println!("High five!"), "High five!" } + } + + div { class: "w-full h-full flex flex-col mx-auto space-y-12", + ImageList { dogs: 3, style: "fluffy" } + } + } +} + +#[component] +fn ImageList(dogs: ReadOnlySignal, style: String) -> Element { + rsx! { + for i in 0..dogs() { + ul { class: "flex flex-row justify-center", + img { + src: "/assets/dogs/{style}/dog{i}.jpg", + height: "200px", + border_radius: "10px", + } + } + } + } +} diff --git a/example-projects/fullstack-mobile/tailwind.config.js b/example-projects/fullstack-mobile/tailwind.config.js new file mode 100644 index 0000000000..b3af0ed75c --- /dev/null +++ b/example-projects/fullstack-mobile/tailwind.config.js @@ -0,0 +1,42 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + mode: "all", + content: ["./src/**/*.{rs,html,css}", "./docs/**/*.html"], + theme: { + extend: { + colors: { + dxorange: "#E96020", + dxblue: "#00A8D6", + ghmetal: "#24292f", + ghdarkmetal: "#161b22", + // ideblack: "#222529", + ideblack: "#0e1116", + // ideblack: "#0a0a0a", + // ideblack: "#0E1116", + }, + fontFamily: { + // sans: [`"Poppins"`, "sans-serif"], + // sans: ["Arimo", "sans-serif"], + // sans: ["Lexend", "sans-serif"], + sans: ["Inter var", "sans-serif"], + }, + boxShadow: { + "3xl": "0 35px 60px -1ww5px rgba(0, 0, 0, 0.5)", + cutesy: "0px 0px 40px -5px rgba(255, 255, 255, 0.2)", + // cutesy: "0px 0px 30px -10px white", + // cutesy: "0px 0px 30px -10px red", + pop: "0px 0px 30px -10px rgba(0, 0, 0, 0.5)", + }, + keyframes: { + fadein: { + from: { opacity: "0" }, + to: { opacity: "1" }, + }, + }, + animation: { + "fadein-medium": "fadein 500ms ease-in-out forwards", + }, + }, + }, + plugins: [], +}; diff --git a/example-projects/fullstack-mobile/tailwind.css b/example-projects/fullstack-mobile/tailwind.css new file mode 100644 index 0000000000..b5c61c9567 --- /dev/null +++ b/example-projects/fullstack-mobile/tailwind.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/example-projects/fullstack-mobile/tools/make-entitlements.sh b/example-projects/fullstack-mobile/tools/make-entitlements.sh new file mode 100755 index 0000000000..50d68b9fbf --- /dev/null +++ b/example-projects/fullstack-mobile/tools/make-entitlements.sh @@ -0,0 +1,41 @@ +# get the mobile provisioning profile +export APP_DEV_NAME=$(xcrun security find-identity -v -p codesigning | grep "Apple Development: " | sed -E 's/.*"([^"]+)".*/\1/') + +# Find the provisioning profile from ~/Library/MobileDevice/Provisioning\ Profiles +export PROVISION_FILE=$(ls ~/Library/MobileDevice/Provisioning\ Profiles | grep mobileprovision) + +# Convert the provisioning profile to json so we can use jq to extract the important bits +security cms -D \ + -i ~/Library/MobileDevice/Provisioning\ Profiles/${PROVISION_FILE} | \ + python3 -c 'import plistlib,sys,json; print(json.dumps(plistlib.loads(sys.stdin.read().encode("utf-8")), default=lambda o:""))' \ + > target/provisioning.json + +# jq out the important bits of the provisioning profile +export TEAM_IDENTIFIER=$(jq -r '.TeamIdentifier[0]' target/provisioning.json) +export APPLICATION_IDENTIFIER_PREFIX=$(jq -r '.ApplicationIdentifierPrefix[0]' target/provisioning.json) +export APPLICATION_IDENTIFIER=$(jq -r '.Entitlements."application-identifier"' target/provisioning.json) +export APP_ID_ACCESS_GROUP=$(jq -r '.Entitlements."keychain-access-groups"[0]' target/provisioning.json) + +# now build the entitlements file +cat < target/entitlements.xcent + + + + application-identifier + ${APPLICATION_IDENTIFIER} + keychain-access-groups + + ${APP_ID_ACCESS_GROUP}.* + + get-task-allow + + com.apple.developer.team-identifier + ${TEAM_IDENTIFIER} + +EOF + +# sign the app +codesign --force \ + --entitlements target/entitlements.xcent \ + --sign "${APP_DEV_NAME}" \ + target/aarch64-apple-ios/debug/bundle/ios/DioxusApp.app diff --git a/example-projects/fullstack-mobile/tools/run-ios-device.sh b/example-projects/fullstack-mobile/tools/run-ios-device.sh new file mode 100755 index 0000000000..80f85d2718 --- /dev/null +++ b/example-projects/fullstack-mobile/tools/run-ios-device.sh @@ -0,0 +1,34 @@ + +APP_PATH="target/aarch64-apple-ios/debug/bundle/ios/DioxusApp.app" + +# get the device id by jq-ing the json of the device list +xcrun devicectl list devices --json-output target/deviceid.json +DEVICE_UUID=$(jq -r '.result.devices[0].identifier' target/deviceid.json) + +xcrun devicectl device install app --device "${DEVICE_UUID}" "${APP_PATH}" --json-output target/xcrun.json + +# get the installation url by jq-ing the json of the device install +INSTALLATION_URL=$(jq -r '.result.installedApplications[0].installationURL' target/xcrun.json) + +export SIMCTL_CHILD_IP_ADDRESS="123123" +export SIMCTL_CHILD_DIOXUS_DEVSERVER_ADDR="ws://127.0.0.1:8080/_dioxus" + +# launch the app +# todo: we can just background it immediately and then pick it up for loading its logs +xcrun devicectl device process launch --device "${DEVICE_UUID}" "${INSTALLATION_URL}" + +# # launch the app and put it in background +# xcrun devicectl device process launch --no-activate --verbose --device "${DEVICE_UUID}" "${INSTALLATION_URL}" --json-output "${XCRUN_DEVICE_PROCESS_LAUNCH_LOG_DIR}" + +# # Extract background PID of status app +# STATUS_PID=$(jq -r '.result.process.processIdentifier' "${XCRUN_DEVICE_PROCESS_LAUNCH_LOG_DIR}") +# "${GIT_ROOT}/scripts/wait-for-metro-port.sh" 2>&1 + +# # now that metro is ready, resume the app from background +# xcrun devicectl device process resume --device "${DEVICE_UUID}" --pid "${STATUS_PID}" > "${XCRUN_DEVICE_PROCESS_RESUME_LOG_DIR}" 2>&1 + + + + + + diff --git a/packages/fullstack/src/document/web.rs b/packages/fullstack/src/document/web.rs index 336e1e48af..fe87b724f7 100644 --- a/packages/fullstack/src/document/web.rs +++ b/packages/fullstack/src/document/web.rs @@ -11,9 +11,7 @@ fn head_element_written_on_server() -> bool { .unwrap_or_default() } -pub(crate) struct FullstackWebDocument { - // document: WebDocument, -} +pub(crate) struct FullstackWebDocument {} impl FullstackWebDocument { pub(crate) fn new() -> Self { @@ -32,8 +30,7 @@ impl Document for FullstackWebDocument { return; } - // self.document - // .create_head_element(name, attributes, contents); + WebDocument::get().create_head_element(name, attributes, contents); } fn set_title(&self, title: String) { @@ -41,12 +38,11 @@ impl Document for FullstackWebDocument { return; } - // self.document.set_title(title); + WebDocument::get().set_title(title); } fn eval(&self, js: String) -> dioxus_document::Eval { - todo!() - // self.document.eval(js) + WebDocument::get().eval(js) } fn as_any(&self) -> &dyn std::any::Any { diff --git a/packages/web/src/devtools.rs b/packages/web/src/devtools.rs index ada0ed0ff9..6b9ef9cbb1 100644 --- a/packages/web/src/devtools.rs +++ b/packages/web/src/devtools.rs @@ -233,8 +233,11 @@ pub(crate) fn invalidate_browser_asset_cache() { for x in 0..links.length() { use wasm_bindgen::JsCast; - let link: web_sys::Element = links.get(x).unwrap().unchecked_into(); - let href = link.get_attribute("href").unwrap(); - _ = link.set_attribute("href", &format!("{}?{}", href, noise)); + if let Some(link) = links.get(x) { + let link: web_sys::Element = link.unchecked_into(); + if let Some(href) = link.get_attribute("href") { + _ = link.set_attribute("href", &format!("{}?{}", href, noise)); + } + } } } diff --git a/packages/web/src/document.rs b/packages/web/src/document.rs index 84c77ff7da..74eceaa62f 100644 --- a/packages/web/src/document.rs +++ b/packages/web/src/document.rs @@ -5,10 +5,7 @@ use dioxus_document::{Document, Eval}; /// Provides the WebEvalProvider through [`ScopeId::provide_context`]. pub fn init_document() { - let window = web_sys::window().unwrap(); - let document = window.document().unwrap(); - let provider: Rc = Rc::new(WebDocument { document }); - + let provider = WebDocument::get(); if ScopeId::ROOT.has_context::>().is_none() { ScopeId::ROOT.provide_context(provider); } @@ -19,13 +16,28 @@ pub struct WebDocument { document: web_sys::Document, } +impl WebDocument { + /// Get the web document provider + pub fn get() -> Rc { + let window = web_sys::window().unwrap(); + let document = window.document().unwrap(); + let provider: Rc = Rc::new(WebDocument { document }); + provider + } +} + impl Document for WebDocument { fn eval(&self, js: String) -> Eval { let (tx, eval) = Eval::from_parts(); // todo: this deserialize is probably wrong. _ = match js_sys::eval(&js) { - Ok(ok) => tx.send(Ok(serde_wasm_bindgen::from_value(ok).unwrap())), + Ok(ok) => { + tracing::trace!("eval result: {ok:#?}"); + let msg = serde_wasm_bindgen::from_value(ok).unwrap_or_default(); + + tx.send(Ok(msg)) + } Err(_err) => tx.send(Err(dioxus_document::EvalError::Communication( "eval failed".to_string(), ))), diff --git a/packages/web/src/hydration/mod.rs b/packages/web/src/hydration/mod.rs index a0e56e6943..f7bc98dec4 100644 --- a/packages/web/src/hydration/mod.rs +++ b/packages/web/src/hydration/mod.rs @@ -5,6 +5,7 @@ mod hydrate; #[cfg(feature = "hydrate")] pub use deserialize::*; + #[cfg(feature = "hydrate")] #[allow(unused)] pub use hydrate::*; @@ -19,3 +20,20 @@ pub(crate) struct SuspenseMessage { /// The data to hydrate the suspense boundary with data: Vec, } + +#[cfg(feature = "hydrate")] +// Get the initial hydration data from the client +#[wasm_bindgen::prelude::wasm_bindgen(inline_js = r#" + export function get_initial_hydration_data() { + if (window.initial_dioxus_hydration_data === undefined) { + return new Uint8Array(); + } + + const decoded = atob(window.initial_dioxus_hydration_data); + return Uint8Array.from(decoded, (c) => c.charCodeAt(0)) + } +"#)] +extern "C" { + /// Get the initial hydration data from the client + pub fn get_initial_hydration_data() -> js_sys::Uint8Array; +} diff --git a/packages/web/src/lib.rs b/packages/web/src/lib.rs index 1683163c29..5d35079b97 100644 --- a/packages/web/src/lib.rs +++ b/packages/web/src/lib.rs @@ -83,21 +83,6 @@ pub async fn run(mut virtual_dom: VirtualDom, web_config: Config) -> ! { if should_hydrate { #[cfg(feature = "hydrate")] { - websys_dom.skip_mutations = true; - // Get the initial hydration data from the client - #[wasm_bindgen::prelude::wasm_bindgen(inline_js = r#" - export function get_initial_hydration_data() { - if (window.initial_dioxus_hydration_data === undefined) { - return new Uint8Array(); - } - - const decoded = atob(window.initial_dioxus_hydration_data); - return Uint8Array.from(decoded, (c) => c.charCodeAt(0)) - } - "#)] - extern "C" { - fn get_initial_hydration_data() -> js_sys::Uint8Array; - } let hydration_data = get_initial_hydration_data().to_vec(); if let Some(server_data) = HTMLDataCursor::from_serialized(&hydration_data) { @@ -105,6 +90,8 @@ pub async fn run(mut virtual_dom: VirtualDom, web_config: Config) -> ! { if let Some(error) = server_data.error() { virtual_dom.in_runtime(|| dioxus_core::ScopeId::APP.throw_error(error)); } + + websys_dom.skip_mutations = true; with_server_data(server_data, || { virtual_dom.rebuild(&mut websys_dom); }); @@ -115,6 +102,10 @@ pub async fn run(mut virtual_dom: VirtualDom, web_config: Config) -> ! { .expect("Failed to rehydrate"); hydration_receiver = Some(rx); + } else { + tracing::error!("Hydration data is missing"); + virtual_dom.rebuild(&mut websys_dom); + websys_dom.flush_edits(); } } #[cfg(not(feature = "hydrate"))] From e7ff8784f59aaeaf74b37531cc3ff988ee46a167 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 5 Sep 2024 03:36:29 -0700 Subject: [PATCH 076/139] fix launch function, move projects into example-projects folder --- Cargo.lock | 7 ++++++ Cargo.toml | 23 +++++++++--------- .../PWA-example/Cargo.toml | 0 .../PWA-example/Dioxus.toml | 0 .../PWA-example/LICENSE | 0 .../PWA-example/README.md | 0 .../PWA-example/index.html | 0 .../PWA-example/public/favicon.ico | Bin .../PWA-example/public/logo_192.png | Bin .../PWA-example/public/logo_512.png | Bin .../PWA-example/public/manifest.json | 0 .../PWA-example/public/sw.js | 0 .../PWA-example/src/main.rs | 0 example-projects/fullstack-mobile/Cargo.toml | 6 +---- .../manganis-test-package/Cargo.toml | 0 .../manganis-test-package/assets/asset.txt | 0 .../manganis-test-package/assets/data.json | 0 .../manganis-test-package/assets/script.js | 0 .../manganis-test-package/assets/test.mp4 | 0 .../manganis-test-package/src/main.rs | 0 .../test-package-dependency/Cargo.toml | 0 .../test-package-dependency/src/asset.txt | 0 .../test-package-dependency/src/file.rs | 0 .../test-package-dependency/src/lib.rs | 0 .../test-package-nested-dependency/Cargo.toml | 0 .../all_the_assets/data.json | 0 .../all_the_assets/rustacean-flat-gesture.png | Bin .../all_the_assets/script.js | 0 .../all_the_assets/style.css | 0 .../src/file.rs | 0 .../src/folder.rs | 0 .../src/font.rs | 0 .../test-package-nested-dependency/src/js.rs | 0 .../test-package-nested-dependency/src/lib.rs | 0 .../tailwind/.gitignore | 0 .../tailwind/Cargo.toml | 0 .../tailwind/Dioxus.toml | 0 .../tailwind/README.md | 0 .../tailwind/input.css | 0 .../tailwind/public/tailwind.css | 0 .../tailwind/src/main.rs | 0 .../tailwind/tailwind.config.js | 0 packages/fullstack/src/config.rs | 7 ++++++ 43 files changed, 27 insertions(+), 16 deletions(-) rename {examples => example-projects}/PWA-example/Cargo.toml (100%) rename {examples => example-projects}/PWA-example/Dioxus.toml (100%) rename {examples => example-projects}/PWA-example/LICENSE (100%) rename {examples => example-projects}/PWA-example/README.md (100%) rename {examples => example-projects}/PWA-example/index.html (100%) rename {examples => example-projects}/PWA-example/public/favicon.ico (100%) rename {examples => example-projects}/PWA-example/public/logo_192.png (100%) rename {examples => example-projects}/PWA-example/public/logo_512.png (100%) rename {examples => example-projects}/PWA-example/public/manifest.json (100%) rename {examples => example-projects}/PWA-example/public/sw.js (100%) rename {examples => example-projects}/PWA-example/src/main.rs (100%) rename {examples => example-projects}/manganis-test-package/Cargo.toml (100%) rename {examples => example-projects}/manganis-test-package/assets/asset.txt (100%) rename {examples => example-projects}/manganis-test-package/assets/data.json (100%) rename {examples => example-projects}/manganis-test-package/assets/script.js (100%) rename {examples => example-projects}/manganis-test-package/assets/test.mp4 (100%) rename {examples => example-projects}/manganis-test-package/src/main.rs (100%) rename {examples => example-projects}/manganis-test-package/test-package-dependency/Cargo.toml (100%) rename {examples => example-projects}/manganis-test-package/test-package-dependency/src/asset.txt (100%) rename {examples => example-projects}/manganis-test-package/test-package-dependency/src/file.rs (100%) rename {examples => example-projects}/manganis-test-package/test-package-dependency/src/lib.rs (100%) rename {examples => example-projects}/manganis-test-package/test-package-nested-dependency/Cargo.toml (100%) rename {examples => example-projects}/manganis-test-package/test-package-nested-dependency/all_the_assets/data.json (100%) rename {examples => example-projects}/manganis-test-package/test-package-nested-dependency/all_the_assets/rustacean-flat-gesture.png (100%) rename {examples => example-projects}/manganis-test-package/test-package-nested-dependency/all_the_assets/script.js (100%) rename {examples => example-projects}/manganis-test-package/test-package-nested-dependency/all_the_assets/style.css (100%) rename {examples => example-projects}/manganis-test-package/test-package-nested-dependency/src/file.rs (100%) rename {examples => example-projects}/manganis-test-package/test-package-nested-dependency/src/folder.rs (100%) rename {examples => example-projects}/manganis-test-package/test-package-nested-dependency/src/font.rs (100%) rename {examples => example-projects}/manganis-test-package/test-package-nested-dependency/src/js.rs (100%) rename {examples => example-projects}/manganis-test-package/test-package-nested-dependency/src/lib.rs (100%) rename {examples => example-projects}/tailwind/.gitignore (100%) rename {examples => example-projects}/tailwind/Cargo.toml (100%) rename {examples => example-projects}/tailwind/Dioxus.toml (100%) rename {examples => example-projects}/tailwind/README.md (100%) rename {examples => example-projects}/tailwind/input.css (100%) rename {examples => example-projects}/tailwind/public/tailwind.css (100%) rename {examples => example-projects}/tailwind/src/main.rs (100%) rename {examples => example-projects}/tailwind/tailwind.config.js (100%) diff --git a/Cargo.lock b/Cargo.lock index c517ab3d73..4ba6d2eac9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,13 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "DioxusApp" +version = "0.1.0" +dependencies = [ + "dioxus", +] + [[package]] name = "addr2line" version = "0.22.0" diff --git a/Cargo.toml b/Cargo.toml index 377bd537e6..c1818220f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,10 @@ members = [ "packages/lazy-js-bundle", "packages/document", "packages/runtime-config", + "packages/devtools", + "packages/devtools-types", + "packages/isrg", + "packages/manganis-core", # Fullstack examples "packages/fullstack/examples/hello-world", @@ -48,10 +52,6 @@ members = [ "packages/static-generation/examples/router", "packages/static-generation/examples/github-pages", - # Full project examples - "examples/tailwind", - "examples/PWA-example", - # Playwright tests "packages/playwright-tests/liveview", "packages/playwright-tests/web", @@ -63,13 +63,14 @@ members = [ # manganis "packages/manganis", "packages/manganis-macro", - "examples/manganis-test-package", - "examples/manganis-test-package/test-package-dependency", - "examples/manganis-test-package/test-package-nested-dependency", - "packages/devtools", - "packages/devtools-types", - "packages/isrg", - "packages/manganis-core", + "example-projects/manganis-test-package", + "example-projects/manganis-test-package/test-package-dependency", + "example-projects/manganis-test-package/test-package-nested-dependency", + + # Some default projects that act as integration tests + "example-projects/fullstack-mobile", + "example-projects/tailwind", + "example-projects/PWA-example", ] [workspace.package] diff --git a/examples/PWA-example/Cargo.toml b/example-projects/PWA-example/Cargo.toml similarity index 100% rename from examples/PWA-example/Cargo.toml rename to example-projects/PWA-example/Cargo.toml diff --git a/examples/PWA-example/Dioxus.toml b/example-projects/PWA-example/Dioxus.toml similarity index 100% rename from examples/PWA-example/Dioxus.toml rename to example-projects/PWA-example/Dioxus.toml diff --git a/examples/PWA-example/LICENSE b/example-projects/PWA-example/LICENSE similarity index 100% rename from examples/PWA-example/LICENSE rename to example-projects/PWA-example/LICENSE diff --git a/examples/PWA-example/README.md b/example-projects/PWA-example/README.md similarity index 100% rename from examples/PWA-example/README.md rename to example-projects/PWA-example/README.md diff --git a/examples/PWA-example/index.html b/example-projects/PWA-example/index.html similarity index 100% rename from examples/PWA-example/index.html rename to example-projects/PWA-example/index.html diff --git a/examples/PWA-example/public/favicon.ico b/example-projects/PWA-example/public/favicon.ico similarity index 100% rename from examples/PWA-example/public/favicon.ico rename to example-projects/PWA-example/public/favicon.ico diff --git a/examples/PWA-example/public/logo_192.png b/example-projects/PWA-example/public/logo_192.png similarity index 100% rename from examples/PWA-example/public/logo_192.png rename to example-projects/PWA-example/public/logo_192.png diff --git a/examples/PWA-example/public/logo_512.png b/example-projects/PWA-example/public/logo_512.png similarity index 100% rename from examples/PWA-example/public/logo_512.png rename to example-projects/PWA-example/public/logo_512.png diff --git a/examples/PWA-example/public/manifest.json b/example-projects/PWA-example/public/manifest.json similarity index 100% rename from examples/PWA-example/public/manifest.json rename to example-projects/PWA-example/public/manifest.json diff --git a/examples/PWA-example/public/sw.js b/example-projects/PWA-example/public/sw.js similarity index 100% rename from examples/PWA-example/public/sw.js rename to example-projects/PWA-example/public/sw.js diff --git a/examples/PWA-example/src/main.rs b/example-projects/PWA-example/src/main.rs similarity index 100% rename from examples/PWA-example/src/main.rs rename to example-projects/PWA-example/src/main.rs diff --git a/example-projects/fullstack-mobile/Cargo.toml b/example-projects/fullstack-mobile/Cargo.toml index dddc66fe13..4291756c5d 100644 --- a/example-projects/fullstack-mobile/Cargo.toml +++ b/example-projects/fullstack-mobile/Cargo.toml @@ -4,11 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -dioxus = { path = "../../dioxus/packages/dioxus", features = ["fullstack"] } -# reqwest = { version = "0.12.7", features = ["json"] } -# serde = "1.0.208" -# dioxus = { path = "../../dioxus/packages/dioxus", features = ["mobile"] } -# dioxus-runtime-config = { path = "../../dioxus/packages/runtime-config" } +dioxus = { workspace = true, features = ["fullstack", "mobile"] } [features] desktop = ["dioxus/desktop"] diff --git a/examples/manganis-test-package/Cargo.toml b/example-projects/manganis-test-package/Cargo.toml similarity index 100% rename from examples/manganis-test-package/Cargo.toml rename to example-projects/manganis-test-package/Cargo.toml diff --git a/examples/manganis-test-package/assets/asset.txt b/example-projects/manganis-test-package/assets/asset.txt similarity index 100% rename from examples/manganis-test-package/assets/asset.txt rename to example-projects/manganis-test-package/assets/asset.txt diff --git a/examples/manganis-test-package/assets/data.json b/example-projects/manganis-test-package/assets/data.json similarity index 100% rename from examples/manganis-test-package/assets/data.json rename to example-projects/manganis-test-package/assets/data.json diff --git a/examples/manganis-test-package/assets/script.js b/example-projects/manganis-test-package/assets/script.js similarity index 100% rename from examples/manganis-test-package/assets/script.js rename to example-projects/manganis-test-package/assets/script.js diff --git a/examples/manganis-test-package/assets/test.mp4 b/example-projects/manganis-test-package/assets/test.mp4 similarity index 100% rename from examples/manganis-test-package/assets/test.mp4 rename to example-projects/manganis-test-package/assets/test.mp4 diff --git a/examples/manganis-test-package/src/main.rs b/example-projects/manganis-test-package/src/main.rs similarity index 100% rename from examples/manganis-test-package/src/main.rs rename to example-projects/manganis-test-package/src/main.rs diff --git a/examples/manganis-test-package/test-package-dependency/Cargo.toml b/example-projects/manganis-test-package/test-package-dependency/Cargo.toml similarity index 100% rename from examples/manganis-test-package/test-package-dependency/Cargo.toml rename to example-projects/manganis-test-package/test-package-dependency/Cargo.toml diff --git a/examples/manganis-test-package/test-package-dependency/src/asset.txt b/example-projects/manganis-test-package/test-package-dependency/src/asset.txt similarity index 100% rename from examples/manganis-test-package/test-package-dependency/src/asset.txt rename to example-projects/manganis-test-package/test-package-dependency/src/asset.txt diff --git a/examples/manganis-test-package/test-package-dependency/src/file.rs b/example-projects/manganis-test-package/test-package-dependency/src/file.rs similarity index 100% rename from examples/manganis-test-package/test-package-dependency/src/file.rs rename to example-projects/manganis-test-package/test-package-dependency/src/file.rs diff --git a/examples/manganis-test-package/test-package-dependency/src/lib.rs b/example-projects/manganis-test-package/test-package-dependency/src/lib.rs similarity index 100% rename from examples/manganis-test-package/test-package-dependency/src/lib.rs rename to example-projects/manganis-test-package/test-package-dependency/src/lib.rs diff --git a/examples/manganis-test-package/test-package-nested-dependency/Cargo.toml b/example-projects/manganis-test-package/test-package-nested-dependency/Cargo.toml similarity index 100% rename from examples/manganis-test-package/test-package-nested-dependency/Cargo.toml rename to example-projects/manganis-test-package/test-package-nested-dependency/Cargo.toml diff --git a/examples/manganis-test-package/test-package-nested-dependency/all_the_assets/data.json b/example-projects/manganis-test-package/test-package-nested-dependency/all_the_assets/data.json similarity index 100% rename from examples/manganis-test-package/test-package-nested-dependency/all_the_assets/data.json rename to example-projects/manganis-test-package/test-package-nested-dependency/all_the_assets/data.json diff --git a/examples/manganis-test-package/test-package-nested-dependency/all_the_assets/rustacean-flat-gesture.png b/example-projects/manganis-test-package/test-package-nested-dependency/all_the_assets/rustacean-flat-gesture.png similarity index 100% rename from examples/manganis-test-package/test-package-nested-dependency/all_the_assets/rustacean-flat-gesture.png rename to example-projects/manganis-test-package/test-package-nested-dependency/all_the_assets/rustacean-flat-gesture.png diff --git a/examples/manganis-test-package/test-package-nested-dependency/all_the_assets/script.js b/example-projects/manganis-test-package/test-package-nested-dependency/all_the_assets/script.js similarity index 100% rename from examples/manganis-test-package/test-package-nested-dependency/all_the_assets/script.js rename to example-projects/manganis-test-package/test-package-nested-dependency/all_the_assets/script.js diff --git a/examples/manganis-test-package/test-package-nested-dependency/all_the_assets/style.css b/example-projects/manganis-test-package/test-package-nested-dependency/all_the_assets/style.css similarity index 100% rename from examples/manganis-test-package/test-package-nested-dependency/all_the_assets/style.css rename to example-projects/manganis-test-package/test-package-nested-dependency/all_the_assets/style.css diff --git a/examples/manganis-test-package/test-package-nested-dependency/src/file.rs b/example-projects/manganis-test-package/test-package-nested-dependency/src/file.rs similarity index 100% rename from examples/manganis-test-package/test-package-nested-dependency/src/file.rs rename to example-projects/manganis-test-package/test-package-nested-dependency/src/file.rs diff --git a/examples/manganis-test-package/test-package-nested-dependency/src/folder.rs b/example-projects/manganis-test-package/test-package-nested-dependency/src/folder.rs similarity index 100% rename from examples/manganis-test-package/test-package-nested-dependency/src/folder.rs rename to example-projects/manganis-test-package/test-package-nested-dependency/src/folder.rs diff --git a/examples/manganis-test-package/test-package-nested-dependency/src/font.rs b/example-projects/manganis-test-package/test-package-nested-dependency/src/font.rs similarity index 100% rename from examples/manganis-test-package/test-package-nested-dependency/src/font.rs rename to example-projects/manganis-test-package/test-package-nested-dependency/src/font.rs diff --git a/examples/manganis-test-package/test-package-nested-dependency/src/js.rs b/example-projects/manganis-test-package/test-package-nested-dependency/src/js.rs similarity index 100% rename from examples/manganis-test-package/test-package-nested-dependency/src/js.rs rename to example-projects/manganis-test-package/test-package-nested-dependency/src/js.rs diff --git a/examples/manganis-test-package/test-package-nested-dependency/src/lib.rs b/example-projects/manganis-test-package/test-package-nested-dependency/src/lib.rs similarity index 100% rename from examples/manganis-test-package/test-package-nested-dependency/src/lib.rs rename to example-projects/manganis-test-package/test-package-nested-dependency/src/lib.rs diff --git a/examples/tailwind/.gitignore b/example-projects/tailwind/.gitignore similarity index 100% rename from examples/tailwind/.gitignore rename to example-projects/tailwind/.gitignore diff --git a/examples/tailwind/Cargo.toml b/example-projects/tailwind/Cargo.toml similarity index 100% rename from examples/tailwind/Cargo.toml rename to example-projects/tailwind/Cargo.toml diff --git a/examples/tailwind/Dioxus.toml b/example-projects/tailwind/Dioxus.toml similarity index 100% rename from examples/tailwind/Dioxus.toml rename to example-projects/tailwind/Dioxus.toml diff --git a/examples/tailwind/README.md b/example-projects/tailwind/README.md similarity index 100% rename from examples/tailwind/README.md rename to example-projects/tailwind/README.md diff --git a/examples/tailwind/input.css b/example-projects/tailwind/input.css similarity index 100% rename from examples/tailwind/input.css rename to example-projects/tailwind/input.css diff --git a/examples/tailwind/public/tailwind.css b/example-projects/tailwind/public/tailwind.css similarity index 100% rename from examples/tailwind/public/tailwind.css rename to example-projects/tailwind/public/tailwind.css diff --git a/examples/tailwind/src/main.rs b/example-projects/tailwind/src/main.rs similarity index 100% rename from examples/tailwind/src/main.rs rename to example-projects/tailwind/src/main.rs diff --git a/examples/tailwind/tailwind.config.js b/example-projects/tailwind/tailwind.config.js similarity index 100% rename from examples/tailwind/tailwind.config.js rename to example-projects/tailwind/tailwind.config.js diff --git a/packages/fullstack/src/config.rs b/packages/fullstack/src/config.rs index 52d8051119..1fd9b221d8 100644 --- a/packages/fullstack/src/config.rs +++ b/packages/fullstack/src/config.rs @@ -102,4 +102,11 @@ impl Config { pub fn with_mobile_cfg(self, mobile_cfg: dioxus_mobile::Config) -> Self { Self { mobile_cfg, ..self } } + + /// Set the mobile config. + #[cfg(feature = "mobile")] + #[cfg_attr(docsrs, doc(cfg(feature = "mobile")))] + pub fn set_mobile_cfg(&mut self, mobile_cfg: dioxus_mobile::Config) { + self.mobile_cfg = mobile_cfg; + } } From 17456e6ccc513444a8f592b2a30595c8fc9475df Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 5 Sep 2024 03:51:02 -0700 Subject: [PATCH 077/139] hoist examples --- Cargo.lock | 2 ++ Cargo.toml | 16 +++++------ .../auth/.gitignore | 0 .../auth/Cargo.toml | 0 .../auth/src/auth.rs | 0 .../auth/src/main.rs | 0 .../desktop/.gitignore | 0 .../desktop/Cargo.toml | 0 .../desktop/src/main.rs | 0 example-projects/fullstack-mobile/Cargo.toml | 4 ++- example-projects/fullstack-mobile/src/main.rs | 27 +++++++++++++++++-- .../hackernews/.gitignore | 0 .../hackernews/Cargo.toml | 0 .../hackernews/assets/hackernews.css | 0 .../hackernews/src/main.rs | 0 .../hello-world/.gitignore | 0 .../hello-world/Cargo.toml | 0 .../hello-world/src/main.rs | 0 .../router/.gitignore | 0 .../router/Cargo.toml | 0 .../router/src/main.rs | 0 .../streaming/.gitignore | 0 .../streaming/Cargo.toml | 0 .../streaming/src/main.rs | 0 packages/cli/src/bundler/app.rs | 9 +++++-- packages/fullstack/Cargo.toml | 2 +- 26 files changed, 45 insertions(+), 15 deletions(-) rename {packages/fullstack/examples => example-projects}/auth/.gitignore (100%) rename {packages/fullstack/examples => example-projects}/auth/Cargo.toml (100%) rename {packages/fullstack/examples => example-projects}/auth/src/auth.rs (100%) rename {packages/fullstack/examples => example-projects}/auth/src/main.rs (100%) rename {packages/fullstack/examples => example-projects}/desktop/.gitignore (100%) rename {packages/fullstack/examples => example-projects}/desktop/Cargo.toml (100%) rename {packages/fullstack/examples => example-projects}/desktop/src/main.rs (100%) rename {packages/fullstack/examples => example-projects}/hackernews/.gitignore (100%) rename {packages/fullstack/examples => example-projects}/hackernews/Cargo.toml (100%) rename {packages/fullstack/examples => example-projects}/hackernews/assets/hackernews.css (100%) rename {packages/fullstack/examples => example-projects}/hackernews/src/main.rs (100%) rename {packages/fullstack/examples => example-projects}/hello-world/.gitignore (100%) rename {packages/fullstack/examples => example-projects}/hello-world/Cargo.toml (100%) rename {packages/fullstack/examples => example-projects}/hello-world/src/main.rs (100%) rename {packages/fullstack/examples => example-projects}/router/.gitignore (100%) rename {packages/fullstack/examples => example-projects}/router/Cargo.toml (100%) rename {packages/fullstack/examples => example-projects}/router/src/main.rs (100%) rename {packages/fullstack/examples => example-projects}/streaming/.gitignore (100%) rename {packages/fullstack/examples => example-projects}/streaming/Cargo.toml (100%) rename {packages/fullstack/examples => example-projects}/streaming/src/main.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 4ba6d2eac9..54949786dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7,6 +7,8 @@ name = "DioxusApp" version = "0.1.0" dependencies = [ "dioxus", + "reqwest", + "serde", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index c1818220f6..579d545f70 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,14 +39,6 @@ members = [ "packages/isrg", "packages/manganis-core", - # Fullstack examples - "packages/fullstack/examples/hello-world", - "packages/fullstack/examples/router", - "packages/fullstack/examples/streaming", - "packages/fullstack/examples/desktop", - "packages/fullstack/examples/auth", - "packages/fullstack/examples/hackernews", - # Static generation examples "packages/static-generation/examples/simple", "packages/static-generation/examples/router", @@ -67,7 +59,13 @@ members = [ "example-projects/manganis-test-package/test-package-dependency", "example-projects/manganis-test-package/test-package-nested-dependency", - # Some default projects that act as integration tests + # Helpful full projects + "example-projects/hello-world", + "example-projects/router", + "example-projects/streaming", + "example-projects/desktop", + "example-projects/auth", + "example-projects/hackernews", "example-projects/fullstack-mobile", "example-projects/tailwind", "example-projects/PWA-example", diff --git a/packages/fullstack/examples/auth/.gitignore b/example-projects/auth/.gitignore similarity index 100% rename from packages/fullstack/examples/auth/.gitignore rename to example-projects/auth/.gitignore diff --git a/packages/fullstack/examples/auth/Cargo.toml b/example-projects/auth/Cargo.toml similarity index 100% rename from packages/fullstack/examples/auth/Cargo.toml rename to example-projects/auth/Cargo.toml diff --git a/packages/fullstack/examples/auth/src/auth.rs b/example-projects/auth/src/auth.rs similarity index 100% rename from packages/fullstack/examples/auth/src/auth.rs rename to example-projects/auth/src/auth.rs diff --git a/packages/fullstack/examples/auth/src/main.rs b/example-projects/auth/src/main.rs similarity index 100% rename from packages/fullstack/examples/auth/src/main.rs rename to example-projects/auth/src/main.rs diff --git a/packages/fullstack/examples/desktop/.gitignore b/example-projects/desktop/.gitignore similarity index 100% rename from packages/fullstack/examples/desktop/.gitignore rename to example-projects/desktop/.gitignore diff --git a/packages/fullstack/examples/desktop/Cargo.toml b/example-projects/desktop/Cargo.toml similarity index 100% rename from packages/fullstack/examples/desktop/Cargo.toml rename to example-projects/desktop/Cargo.toml diff --git a/packages/fullstack/examples/desktop/src/main.rs b/example-projects/desktop/src/main.rs similarity index 100% rename from packages/fullstack/examples/desktop/src/main.rs rename to example-projects/desktop/src/main.rs diff --git a/example-projects/fullstack-mobile/Cargo.toml b/example-projects/fullstack-mobile/Cargo.toml index 4291756c5d..f5724395de 100644 --- a/example-projects/fullstack-mobile/Cargo.toml +++ b/example-projects/fullstack-mobile/Cargo.toml @@ -4,7 +4,9 @@ version = "0.1.0" edition = "2021" [dependencies] -dioxus = { workspace = true, features = ["fullstack", "mobile"] } +dioxus = { workspace = true, features = ["fullstack"] } +serde = { workspace = true, features = ["derive"] } +reqwest = { workspace = true, features = ["json"] } [features] desktop = ["dioxus/desktop"] diff --git a/example-projects/fullstack-mobile/src/main.rs b/example-projects/fullstack-mobile/src/main.rs index b43880de7e..48e4f401cb 100644 --- a/example-projects/fullstack-mobile/src/main.rs +++ b/example-projects/fullstack-mobile/src/main.rs @@ -5,14 +5,21 @@ fn main() { } fn app() -> Element { + let mut favorite_dog = use_signal(|| "Fido".to_string()); + rsx! { document::Stylesheet { href: asset!("/assets/style.css") } document::Stylesheet { href: asset!("/assets/tailwind.css") } div { class: "w-full h-full text-center my-20", h1 { class: "text-4xl font-bold", "Dioxus iOS apps!" } - h3 { class: "sparkles", "!!zero-xcode!!!!!!!!!" } - button { onclick: move |_| println!("High five!"), "High five!" } + h3 { class: "sparkles", "Favorite dog: {favorite_dog}" } + button { onclick: move |_| async move { + let dog = get_random_dog( + "husky".to_string() + ).await.unwrap_or_else(|err| format!("Error: {err}")); + favorite_dog.set(dog); + }, "New favorite dog!" } } div { class: "w-full h-full flex flex-col mx-auto space-y-12", @@ -35,3 +42,19 @@ fn ImageList(dogs: ReadOnlySignal, style: String) -> Element { } } } + +#[server] +async fn get_random_dog(breed: String) -> Result { + #[derive(serde::Deserialize, Debug)] + struct DogApi { + message: String, + } + + let dog = reqwest::get(format!("https://dog.ceo/api/breed/{breed}/images/random")) + .await + .unwrap() + .json::() + .await?; + + Ok(dog.message) +} diff --git a/packages/fullstack/examples/hackernews/.gitignore b/example-projects/hackernews/.gitignore similarity index 100% rename from packages/fullstack/examples/hackernews/.gitignore rename to example-projects/hackernews/.gitignore diff --git a/packages/fullstack/examples/hackernews/Cargo.toml b/example-projects/hackernews/Cargo.toml similarity index 100% rename from packages/fullstack/examples/hackernews/Cargo.toml rename to example-projects/hackernews/Cargo.toml diff --git a/packages/fullstack/examples/hackernews/assets/hackernews.css b/example-projects/hackernews/assets/hackernews.css similarity index 100% rename from packages/fullstack/examples/hackernews/assets/hackernews.css rename to example-projects/hackernews/assets/hackernews.css diff --git a/packages/fullstack/examples/hackernews/src/main.rs b/example-projects/hackernews/src/main.rs similarity index 100% rename from packages/fullstack/examples/hackernews/src/main.rs rename to example-projects/hackernews/src/main.rs diff --git a/packages/fullstack/examples/hello-world/.gitignore b/example-projects/hello-world/.gitignore similarity index 100% rename from packages/fullstack/examples/hello-world/.gitignore rename to example-projects/hello-world/.gitignore diff --git a/packages/fullstack/examples/hello-world/Cargo.toml b/example-projects/hello-world/Cargo.toml similarity index 100% rename from packages/fullstack/examples/hello-world/Cargo.toml rename to example-projects/hello-world/Cargo.toml diff --git a/packages/fullstack/examples/hello-world/src/main.rs b/example-projects/hello-world/src/main.rs similarity index 100% rename from packages/fullstack/examples/hello-world/src/main.rs rename to example-projects/hello-world/src/main.rs diff --git a/packages/fullstack/examples/router/.gitignore b/example-projects/router/.gitignore similarity index 100% rename from packages/fullstack/examples/router/.gitignore rename to example-projects/router/.gitignore diff --git a/packages/fullstack/examples/router/Cargo.toml b/example-projects/router/Cargo.toml similarity index 100% rename from packages/fullstack/examples/router/Cargo.toml rename to example-projects/router/Cargo.toml diff --git a/packages/fullstack/examples/router/src/main.rs b/example-projects/router/src/main.rs similarity index 100% rename from packages/fullstack/examples/router/src/main.rs rename to example-projects/router/src/main.rs diff --git a/packages/fullstack/examples/streaming/.gitignore b/example-projects/streaming/.gitignore similarity index 100% rename from packages/fullstack/examples/streaming/.gitignore rename to example-projects/streaming/.gitignore diff --git a/packages/fullstack/examples/streaming/Cargo.toml b/example-projects/streaming/Cargo.toml similarity index 100% rename from packages/fullstack/examples/streaming/Cargo.toml rename to example-projects/streaming/Cargo.toml diff --git a/packages/fullstack/examples/streaming/src/main.rs b/example-projects/streaming/src/main.rs similarity index 100% rename from packages/fullstack/examples/streaming/src/main.rs rename to example-projects/streaming/src/main.rs diff --git a/packages/cli/src/bundler/app.rs b/packages/cli/src/bundler/app.rs index 3b231f22b3..f06df29cdc 100644 --- a/packages/cli/src/bundler/app.rs +++ b/packages/cli/src/bundler/app.rs @@ -53,8 +53,13 @@ impl AppBundle { // let output_location = destination.join(self.build.app_name()); // Ok(output_location) } - Platform::Server => todo!(), - Platform::Liveview => todo!(), + + Platform::Server => { + Ok(self.executable.clone()) + }, + Platform::Liveview => { + Ok(self.executable.clone()) + }, // Create a .ipa, only from macOS Platform::Ios => todo!(), diff --git a/packages/fullstack/Cargo.toml b/packages/fullstack/Cargo.toml index ede29feead..722efb1094 100644 --- a/packages/fullstack/Cargo.toml +++ b/packages/fullstack/Cargo.toml @@ -12,7 +12,7 @@ resolver = "2" [dependencies] # server functions -server_fn = { version = "0.6.5", features = ["json", "url"], default-features = false } +server_fn = { version = "0.6.5", features = ["json", "url", "browser"], default-features = false } dioxus_server_macro = { workspace = true } # axum From bb46819ac656f0a7a878270feaa69192623bfc08 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 5 Sep 2024 04:45:12 -0700 Subject: [PATCH 078/139] add "run" command --- Cargo.lock | 1 + example-projects/fullstack-mobile/Cargo.toml | 1 + example-projects/fullstack-mobile/src/main.rs | 36 +- example-projects/router/src/main.rs | 2 +- packages/cli/src/cli/build.rs | 7 +- packages/cli/src/cli/mod.rs | 6 + packages/cli/src/cli/run.rs | 61 ++ packages/cli/src/cli/serve.rs | 12 +- packages/cli/src/main.rs | 2 + packages/cli/src/serve/handle.rs | 4 +- packages/cli/src/serve/mod.rs | 10 +- packages/cli/src/serve/runner.rs | 6 +- packages/cli/src/serve/server.rs | 4 +- packages/dioxus/src/launch.rs | 714 +++++++++--------- 14 files changed, 465 insertions(+), 401 deletions(-) create mode 100644 packages/cli/src/cli/run.rs diff --git a/Cargo.lock b/Cargo.lock index 54949786dd..1b3a43e05f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9,6 +9,7 @@ dependencies = [ "dioxus", "reqwest", "serde", + "tracing", ] [[package]] diff --git a/example-projects/fullstack-mobile/Cargo.toml b/example-projects/fullstack-mobile/Cargo.toml index f5724395de..5d047a3d26 100644 --- a/example-projects/fullstack-mobile/Cargo.toml +++ b/example-projects/fullstack-mobile/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" dioxus = { workspace = true, features = ["fullstack"] } serde = { workspace = true, features = ["derive"] } reqwest = { workspace = true, features = ["json"] } +tracing = { workspace = true } [features] desktop = ["dioxus/desktop"] diff --git a/example-projects/fullstack-mobile/src/main.rs b/example-projects/fullstack-mobile/src/main.rs index 48e4f401cb..8582bb927d 100644 --- a/example-projects/fullstack-mobile/src/main.rs +++ b/example-projects/fullstack-mobile/src/main.rs @@ -1,6 +1,8 @@ use dioxus::prelude::*; fn main() { + println!("[server] Launching app!"); + dioxus::launch(app); } @@ -15,9 +17,7 @@ fn app() -> Element { h1 { class: "text-4xl font-bold", "Dioxus iOS apps!" } h3 { class: "sparkles", "Favorite dog: {favorite_dog}" } button { onclick: move |_| async move { - let dog = get_random_dog( - "husky".to_string() - ).await.unwrap_or_else(|err| format!("Error: {err}")); + let dog = get_random_dog().await.unwrap_or_else(|err| format!("Error: {err}")); favorite_dog.set(dog); }, "New favorite dog!" } } @@ -43,18 +43,24 @@ fn ImageList(dogs: ReadOnlySignal, style: String) -> Element { } } -#[server] -async fn get_random_dog(breed: String) -> Result { - #[derive(serde::Deserialize, Debug)] - struct DogApi { - message: String, - } +#[server(endpoint = "get_random_dog")] +async fn get_random_dog() -> Result { + println!("Getting a random dog from the server!"); + tracing::info!("Getting a random dog from the server!"); + let breed = "husky".to_string(); + + Ok(breed) + + // #[derive(serde::Deserialize, Debug)] + // struct DogApi { + // message: String, + // } - let dog = reqwest::get(format!("https://dog.ceo/api/breed/{breed}/images/random")) - .await - .unwrap() - .json::() - .await?; + // let dog = reqwest::get(format!("https://dog.ceo/api/breed/{breed}/images/random")) + // .await + // .unwrap() + // .json::() + // .await?; - Ok(dog.message) + // Ok(dog.message) } diff --git a/example-projects/router/src/main.rs b/example-projects/router/src/main.rs index d4ef56088b..3058dd7f6a 100644 --- a/example-projects/router/src/main.rs +++ b/example-projects/router/src/main.rs @@ -7,7 +7,7 @@ use dioxus::prelude::*; fn main() { - LaunchBuilder::fullstack() + LaunchBuilder::new() .with_cfg(server_only!(ServeConfig::builder().incremental( IncrementalRendererConfig::default() .invalidate_after(std::time::Duration::from_secs(120)), diff --git a/packages/cli/src/cli/build.rs b/packages/cli/src/cli/build.rs index d00bee1b15..2dd17c3173 100644 --- a/packages/cli/src/cli/build.rs +++ b/packages/cli/src/cli/build.rs @@ -25,11 +25,16 @@ pub(crate) struct BuildArgs { #[serde(default)] pub(crate) force_sequential: bool, - // Use verbose output [default: false] + /// Use verbose output [default: false] #[clap(long)] #[serde(default)] pub(crate) verbose: bool, + /// Pass -Awarnings to the cargo build + #[clap(long)] + #[serde(default)] + pub(crate) silent: bool, + /// Build with custom profile #[clap(long)] pub(crate) profile: Option, diff --git a/packages/cli/src/cli/mod.rs b/packages/cli/src/cli/mod.rs index 6196349f56..4d604a019e 100644 --- a/packages/cli/src/cli/mod.rs +++ b/packages/cli/src/cli/mod.rs @@ -8,6 +8,7 @@ pub(crate) mod create; pub(crate) mod httpserver; pub(crate) mod init; pub(crate) mod link; +pub(crate) mod run; pub(crate) mod serve; pub(crate) mod translate; @@ -84,6 +85,10 @@ pub(crate) enum Commands { #[clap(name = "http-server")] HttpServer(httpserver::Httpserver), + /// Run the project without any hotreloading + #[clap(name = "run")] + Run(run::RunArgs), + /// Dioxus config file controls. #[clap(subcommand)] Config(config::Config), @@ -103,6 +108,7 @@ impl Display for Commands { Commands::Check(_) => write!(f, "check"), Commands::Bundle(_) => write!(f, "bundle"), Commands::HttpServer(_) => write!(f, "http-server"), + Commands::Run(_) => write!(f, "run"), } } } diff --git a/packages/cli/src/cli/run.rs b/packages/cli/src/cli/run.rs new file mode 100644 index 0000000000..27597b0fa1 --- /dev/null +++ b/packages/cli/src/cli/run.rs @@ -0,0 +1,61 @@ +use crate::{build::TargetArgs, builder::Builder, serve::ServeUpdate}; +use anyhow::Context; +use build::BuildArgs; +use futures_util::{stream::FuturesUnordered, StreamExt}; +use std::{path::Path, process::exit}; + +use crate::DioxusCrate; + +use super::*; + +/// Check the Rust files in the project for issues. +#[derive(Clone, Debug, Parser)] +pub(crate) struct RunArgs { + /// Information about the target to check + #[clap(flatten)] + pub(crate) build_args: BuildArgs, +} + +impl RunArgs { + pub(crate) async fn run(mut self) -> anyhow::Result<()> { + let mut dioxus_crate = DioxusCrate::new(&self.build_args.target_args) + .context("Failed to load Dioxus workspace")?; + + self.build_args.resolve(&mut dioxus_crate)?; + + let bundles = Builder::start(&mut dioxus_crate, self.build_args.clone())? + .wait_for_finish() + .await?; + + let mut runner = crate::serve::AppRunner::start(); + + let devserver_ip = "127.0.0.1:8080".parse().unwrap(); + let fullstack_ip = "127.0.0.1:6955".parse().unwrap(); + + for bundle in bundles { + runner.open(bundle, devserver_ip, Some(fullstack_ip)).await; + } + + loop { + let msg = runner.wait().await; + + match msg { + ServeUpdate::StderrReceived { platform, msg } => println!("[{platform}]: {msg}"), + ServeUpdate::StdoutReceived { platform, msg } => println!("[{platform}]: {msg}"), + ServeUpdate::ProcessExited { platform, status } => { + runner.kill(platform).await; + eprintln!("[{platform}]: process exited with status: {status:?}") + } + + ServeUpdate::TracingLog { log } => todo!(), + ServeUpdate::NewConnection => todo!(), + ServeUpdate::WsMessage(_) => todo!(), + ServeUpdate::BuildUpdate(_) => todo!(), + ServeUpdate::FilesChanged { files } => todo!(), + ServeUpdate::TuiInput { event } => todo!(), + } + } + + Ok(()) + } +} diff --git a/packages/cli/src/cli/serve.rs b/packages/cli/src/cli/serve.rs index b11b9bd7b6..59fe025e12 100644 --- a/packages/cli/src/cli/serve.rs +++ b/packages/cli/src/cli/serve.rs @@ -1,7 +1,7 @@ use super::*; -use crate::config::AddressArguments; use crate::settings; use crate::DioxusCrate; +use crate::{builder::Platform, config::AddressArguments}; use anyhow::Context; use build::BuildArgs; use crossterm::tty::IsTty; @@ -114,6 +114,16 @@ impl ServeArgs { pub(crate) fn interactive_tty(&self) -> bool { std::io::stdout().is_tty() && self.interactive.unwrap_or(true) } + + pub(crate) fn should_boot_default_server(&self) -> bool { + match self.build_arguments.platform() { + Platform::Server => true, + Platform::Liveview => true, + Platform::Web | Platform::Desktop | Platform::Ios | Platform::Android => { + self.build_arguments.fullstack + } + } + } } impl std::ops::Deref for ServeArgs { diff --git a/packages/cli/src/main.rs b/packages/cli/src/main.rs index 8ca89ef65e..b73db904af 100644 --- a/packages/cli/src/main.rs +++ b/packages/cli/src/main.rs @@ -59,6 +59,8 @@ async fn main() -> anyhow::Result<()> { Bundle(opts) => opts.bundle().await.context("🚫 Bundling project failed:"), + Run(opts) => opts.run().await.context("🚫 Running project failed:"), + HttpServer(opts) => opts.serve().await.context("🚫 Serving project failed:"), } } diff --git a/packages/cli/src/serve/handle.rs b/packages/cli/src/serve/handle.rs index 1d295fbd74..08e5b589c4 100644 --- a/packages/cli/src/serve/handle.rs +++ b/packages/cli/src/serve/handle.rs @@ -30,8 +30,8 @@ impl AppHandle { let platform = app.build.platform(); let ip = devserver_ip.to_string(); - if platform == Platform::Server { - tracing::trace!( + if platform == Platform::Server || app.build.build.fullstack { + tracing::info!( "Proxying fullstack server from port {:?}", fullstack_address ); diff --git a/packages/cli/src/serve/mod.rs b/packages/cli/src/serve/mod.rs index 1ac9150ba2..40fe75e0ad 100644 --- a/packages/cli/src/serve/mod.rs +++ b/packages/cli/src/serve/mod.rs @@ -18,9 +18,10 @@ mod watcher; use handle::*; use output::*; -use runner::*; +pub(crate) use runner::*; use server::*; pub(crate) use tracer::*; +pub(crate) use update::*; use update::*; use watcher::*; @@ -53,7 +54,7 @@ pub(crate) async fn serve_all(args: ServeArgs, krate: DioxusCrate) -> Result<()> let mut screen = Output::start(&args).expect("Failed to open terminal logger"); let mut devserver = DevServer::start(&args, &krate); let mut watcher = Watcher::start(&args, &krate); - let mut runner = AppRunner::start(&args, &krate); + let mut runner = AppRunner::start(); loop { // Make sure we don't hog the CPU: these loop { select! {} } blocks can starve the executor if we're not careful @@ -212,13 +213,12 @@ async fn handle_msg( // If the process exited *cleanly*, we can exit ServeUpdate::ProcessExited { status, platform } => { - runner.shutdown().await; - if !status.success() { tracing::error!("Application [{platform}] exited with status: {status}"); + return Ok(ControlFlow::Break(())); } - return Ok(ControlFlow::Break(())); + runner.kill(platform).await; } ServeUpdate::StdoutReceived { platform, msg } => { diff --git a/packages/cli/src/serve/runner.rs b/packages/cli/src/serve/runner.rs index 947375433d..8bf85b2ae4 100644 --- a/packages/cli/src/serve/runner.rs +++ b/packages/cli/src/serve/runner.rs @@ -14,7 +14,7 @@ pub(crate) struct AppRunner { } impl AppRunner { - pub(crate) fn start(_args: &ServeArgs, _krate: &DioxusCrate) -> Self { + pub(crate) fn start() -> Self { Self { running: Default::default(), } @@ -78,4 +78,8 @@ impl AppRunner { Ok(self.running.get(&platform).unwrap()) } + + pub(crate) async fn kill(&mut self, platform: Platform) { + self.running.remove(&platform); + } } diff --git a/packages/cli/src/serve/server.rs b/packages/cli/src/serve/server.rs index 905a6990f1..5cbd56f1af 100644 --- a/packages/cli/src/serve/server.rs +++ b/packages/cli/src/serve/server.rs @@ -75,7 +75,7 @@ impl DevServer { let start_browser = args.open.unwrap_or_default(); // If we're serving a fullstack app, we need to find a port to proxy to - let fullstack_port = if matches!(args.build_arguments.platform(), Platform::Liveview) { + let fullstack_port = if args.should_boot_default_server() { get_available_port(addr.ip()) } else { None @@ -352,7 +352,7 @@ impl DevServer { router = router.nest_service(&base_path, build_serve_dir(args, krate)); } - Platform::Liveview => { + Platform::Liveview | Platform::Server => { // For fullstack and static generation, forward all requests to the server let address = fullstack_address.unwrap(); diff --git a/packages/dioxus/src/launch.rs b/packages/dioxus/src/launch.rs index 35de2321c2..24b31c69f6 100644 --- a/packages/dioxus/src/launch.rs +++ b/packages/dioxus/src/launch.rs @@ -6,108 +6,98 @@ use std::any::Any; use crate::prelude::*; +pub fn launch(app: fn() -> Element) { + #[allow(deprecated)] + LaunchBuilder::new().launch(app) +} + /// A builder for a fullstack app. #[must_use] -pub struct LaunchBuilder { - launch_fn: LaunchFn, - contexts: Vec>, - - platform_config: Option, +pub struct LaunchBuilder { + // launch_fn: LaunchFn, + // contexts: Vec>, + // platform_config: Option, } pub type LaunchFn = fn(fn() -> Element, Vec>, Cfg); -#[cfg(any( - feature = "fullstack", - feature = "static-generation", - feature = "liveview" -))] -type ValidContext = SendContext; - -#[cfg(not(any( - feature = "fullstack", - feature = "static-generation", - feature = "liveview" -)))] -type ValidContext = UnsendContext; - type SendContext = dyn Fn() -> Box + Send + Sync + 'static; - type UnsendContext = dyn Fn() -> Box + 'static; #[allow(clippy::redundant_closure)] // clippy doesnt doesn't understand our coercion to fn impl LaunchBuilder { - /// Create a new builder for your application. This will create a launch configuration for the current platform based on the features enabled on the `dioxus` crate. - // If you aren't using a third party renderer and this is not a docs.rs build, generate a warning about no renderer being enabled - #[cfg_attr( - all(not(any( - docsrs, - feature = "third-party-renderer", - feature = "liveview", - feature = "desktop", - feature = "mobile", - feature = "web", - feature = "fullstack", - feature = "static-generation" - ))), - deprecated( - note = "No renderer is enabled. You must enable a renderer feature on the dioxus crate before calling the launch function.\nAdd `web`, `desktop`, `mobile`, `fullstack`, or `static-generation` to the `features` of dioxus field in your Cargo.toml.\n# Example\n```toml\n# ...\n[dependencies]\ndioxus = { version = \"0.5.0\", features = [\"web\"] }\n# ...\n```" - ) - )] - pub fn new() -> LaunchBuilder { - LaunchBuilder { - launch_fn: |root, contexts, cfg| current_platform::launch(root, contexts, cfg), - contexts: Vec::new(), - platform_config: None, - } - } - - /// Launch your web application. - #[cfg(feature = "web")] - #[cfg_attr(docsrs, doc(cfg(feature = "web")))] - pub fn web() -> LaunchBuilder { - LaunchBuilder { - launch_fn: dioxus_web::launch::launch, - contexts: Vec::new(), - platform_config: None, - } - } - - /// Launch your desktop application. - #[cfg(feature = "desktop")] - #[cfg_attr(docsrs, doc(cfg(feature = "desktop")))] - pub fn desktop() -> LaunchBuilder { - LaunchBuilder { - launch_fn: |root, contexts, cfg| dioxus_desktop::launch::launch(root, contexts, cfg), - contexts: Vec::new(), - platform_config: None, - } + // /// Create a new builder for your application. This will create a launch configuration for the current platform based on the features enabled on the `dioxus` crate. + // // If you aren't using a third party renderer and this is not a docs.rs build, generate a warning about no renderer being enabled + // #[cfg_attr( + // all(not(any( + // docsrs, + // feature = "third-party-renderer", + // feature = "liveview", + // feature = "desktop", + // feature = "mobile", + // feature = "web", + // feature = "fullstack", + // feature = "static-generation" + // ))), + // deprecated( + // note = "No renderer is enabled. You must enable a renderer feature on the dioxus crate before calling the launch function.\nAdd `web`, `desktop`, `mobile`, `fullstack`, or `static-generation` to the `features` of dioxus field in your Cargo.toml.\n# Example\n```toml\n# ...\n[dependencies]\ndioxus = { version = \"0.5.0\", features = [\"web\"] }\n# ...\n```" + // ) + // )] + pub fn new() -> LaunchBuilder { + LaunchBuilder {} + // LaunchBuilder { + // launch_fn: |root, contexts, cfg| current_platform::launch(root, contexts, cfg), + // contexts: Vec::new(), + // platform_config: None, + // } } - /// Launch your fullstack application. - #[cfg(feature = "fullstack")] - #[cfg_attr(docsrs, doc(cfg(feature = "fullstack")))] - pub fn fullstack() -> LaunchBuilder { - LaunchBuilder { - launch_fn: |root, contexts, cfg| dioxus_fullstack::launch::launch(root, contexts, cfg), - contexts: Vec::new(), - platform_config: None, - } - } - - /// Launch your static site generation application. - #[cfg(feature = "static-generation")] - #[cfg_attr(docsrs, doc(cfg(feature = "static-generation")))] - pub fn static_generation() -> LaunchBuilder - { - LaunchBuilder { - launch_fn: |root, contexts, cfg| { - dioxus_static_site_generation::launch::launch(root, contexts, cfg) - }, - contexts: Vec::new(), - platform_config: None, - } - } + // /// Launch your web application. + // #[cfg(feature = "web")] + // #[cfg_attr(docsrs, doc(cfg(feature = "web")))] + // pub fn web() -> LaunchBuilder { + // LaunchBuilder { + // launch_fn: dioxus_web::launch::launch, + // contexts: Vec::new(), + // platform_config: None, + // } + // } + + // /// Launch your desktop application. + // #[cfg(feature = "desktop")] + // #[cfg_attr(docsrs, doc(cfg(feature = "desktop")))] + // pub fn desktop() -> LaunchBuilder { + // LaunchBuilder { + // launch_fn: |root, contexts, cfg| dioxus_desktop::launch::launch(root, contexts, cfg), + // contexts: Vec::new(), + // platform_config: None, + // } + // } + + // /// Launch your fullstack application. + // #[cfg(feature = "fullstack")] + // #[cfg_attr(docsrs, doc(cfg(feature = "fullstack")))] + // pub fn fullstack() -> LaunchBuilder { + // LaunchBuilder { + // launch_fn: |root, contexts, cfg| dioxus_fullstack::launch::launch(root, contexts, cfg), + // contexts: Vec::new(), + // platform_config: None, + // } + // } + + // /// Launch your static site generation application. + // #[cfg(feature = "static-generation")] + // #[cfg_attr(docsrs, doc(cfg(feature = "static-generation")))] + // pub fn static_generation() -> LaunchBuilder + // { + // LaunchBuilder { + // launch_fn: |root, contexts, cfg| { + // dioxus_static_site_generation::launch::launch(root, contexts, cfg) + // }, + // contexts: Vec::new(), + // platform_config: None, + // } + // } /// Launch your fullstack application. #[cfg(feature = "mobile")] @@ -120,321 +110,299 @@ impl LaunchBuilder { } } - /// Provide a custom launch function for your application. - /// - /// Useful for third party renderers to tap into the launch builder API without having to reimplement it. - pub fn custom(launch_fn: LaunchFn) -> LaunchBuilder { - LaunchBuilder { - launch_fn, - contexts: vec![], - platform_config: None, - } - } + // /// Provide a custom launch function for your application. + // /// + // /// Useful for third party renderers to tap into the launch builder API without having to reimplement it. + // pub fn custom(launch_fn: LaunchFn) -> LaunchBuilder { + // LaunchBuilder { + // launch_fn, + // contexts: vec![], + // platform_config: None, + // } + // } } -// Fullstack platform builder -impl LaunchBuilder { - /// Inject state into the root component's context that is created on the thread that the app is launched on. - pub fn with_context_provider(mut self, state: impl Fn() -> Box + 'static) -> Self { - self.contexts.push(Box::new(state) as Box); - self - } - - /// Inject state into the root component's context. - pub fn with_context(mut self, state: impl Any + Clone + 'static) -> Self { - self.contexts - .push(Box::new(move || Box::new(state.clone()))); - self - } -} +// // Fullstack platform builder +// impl LaunchBuilder { +// /// Inject state into the root component's context that is created on the thread that the app is launched on. +// pub fn with_context_provider(mut self, state: impl Fn() -> Box + 'static) -> Self { +// self.contexts.push(Box::new(state) as Box); +// self +// } + +// /// Inject state into the root component's context. +// pub fn with_context(mut self, state: impl Any + Clone + 'static) -> Self { +// self.contexts +// .push(Box::new(move || Box::new(state.clone()))); +// self +// } +// } -impl LaunchBuilder { +impl LaunchBuilder { /// Inject state into the root component's context that is created on the thread that the app is launched on. pub fn with_context_provider( mut self, state: impl Fn() -> Box + Send + Sync + 'static, ) -> Self { - self.contexts.push(Box::new(state) as Box); + // self.contexts.push(Box::new(state) as Box); self } /// Inject state into the root component's context. pub fn with_context(mut self, state: impl Any + Clone + Send + Sync + 'static) -> Self { - self.contexts - .push(Box::new(move || Box::new(state.clone()))); + // self.contexts + // .push(Box::new(move || Box::new(state.clone()))); self } } -/// A trait for converting a type into a platform-specific config: -/// - A unit value will be converted into `None` -/// - Any config will be converted into `Some(config)` -/// - If the config is for another platform, it will be converted into `None` -pub trait TryIntoConfig { - fn into_config(self, config: &mut Option); +// // The unit type can be converted into the current platform config. +// // This makes it possible to use the `desktop!`, `web!`, etc macros with the launch API. +pub trait TryIntoConfig { + fn into_config(self, config: &mut Option); } - -// A config can always be converted into itself -impl TryIntoConfig for Cfg { - fn into_config(self, config: &mut Option) { - *config = Some(self); - } -} - -// The unit type can be converted into the current platform config. -// This makes it possible to use the `desktop!`, `web!`, etc macros with the launch API. -#[cfg(any( - feature = "liveview", - feature = "desktop", - feature = "mobile", - feature = "web", - feature = "fullstack" -))] -impl TryIntoConfig for () { - fn into_config(self, config: &mut Option) {} +impl TryIntoConfig for () { + fn into_config(self, config: &mut Option) {} } -impl LaunchBuilder { +impl LaunchBuilder { /// Provide a platform-specific config to the builder. - pub fn with_cfg(mut self, config: impl TryIntoConfig) -> Self { - config.into_config(&mut self.platform_config); + pub fn with_cfg(mut self, config: impl TryIntoConfig) -> Self { + // config.into_config(&mut self.platform_config); self } // Static generation is the only platform that may exit. We can't use the `!` type here - #[cfg(any(feature = "static-generation", feature = "web"))] + // #[cfg(any(feature = "static-generation", feature = "web"))] /// Launch your application. pub fn launch(self, app: fn() -> Element) { - let cfg = self.platform_config.unwrap_or_default(); - - (self.launch_fn)(app, self.contexts, cfg) - } - - #[cfg(not(any(feature = "static-generation", feature = "web")))] - /// Launch your application. - pub fn launch(self, app: fn() -> Element) -> ! { - let cfg = self.platform_config.unwrap_or_default(); - - (self.launch_fn)(app, self.contexts, cfg); - unreachable!("Launching an application will never exit") - } -} - -/// Re-export the platform we expect the user wants -/// -/// If multiple platforms are enabled, we use this priority (from highest to lowest): -/// - `fullstack` -/// - `desktop` -/// - `mobile` -/// - `static-generation` -/// - `web` -/// - `liveview` -mod current_platform { - macro_rules! if_else_cfg { - (if $attr:meta { $($then:item)* } else { $($else:item)* }) => { - $( - #[cfg($attr)] - $then - )* - $( - #[cfg(not($attr))] - $else - )* - }; - } - use crate::prelude::TryIntoConfig; - - #[cfg(feature = "fullstack")] - pub use dioxus_fullstack::launch::*; - - #[cfg(all(feature = "fullstack", feature = "axum"))] - impl TryIntoConfig - for ::dioxus_fullstack::prelude::ServeConfigBuilder - { - fn into_config(self, config: &mut Option) { - match config { - Some(config) => config.set_server_cfg(self), - None => { - *config = - Some(crate::launch::current_platform::Config::new().with_server_cfg(self)) - } - } - } - } - - #[cfg(any(feature = "desktop", feature = "mobile"))] - if_else_cfg! { - if not(feature = "fullstack") { - #[cfg(feature = "desktop")] - pub use dioxus_desktop::launch::*; - #[cfg(not(feature = "desktop"))] - pub use dioxus_mobile::launch::*; - } else { - if_else_cfg! { - if feature = "desktop" { - impl TryIntoConfig for ::dioxus_desktop::Config { - fn into_config(self, config: &mut Option) { - match config { - Some(config) => config.set_desktop_config(self), - None => *config = Some(crate::launch::current_platform::Config::new().with_desktop_config(self)), - } - } - } - } else { - impl TryIntoConfig for ::dioxus_mobile::Config { - fn into_config(self, config: &mut Option) { - match config { - Some(config) => config.set_mobile_cfg(self), - None => *config = Some(crate::launch::current_platform::Config::new().with_mobile_cfg(self)), - } - } - } - } - } - } - } - - #[cfg(feature = "static-generation")] - if_else_cfg! { - if all(not(feature = "fullstack"), not(feature = "desktop"), not(feature = "mobile")) { - pub use dioxus_static_site_generation::launch::*; - } else { - impl TryIntoConfig for ::dioxus_static_site_generation::Config { - fn into_config(self, config: &mut Option) {} - } - } - } - - #[cfg(feature = "web")] - if_else_cfg! { - if not(any(feature = "desktop", feature = "mobile", feature = "fullstack", feature = "static-generation")) { - pub use dioxus_web::launch::*; - } else { - if_else_cfg! { - if feature = "fullstack" { - impl TryIntoConfig for ::dioxus_web::Config { - fn into_config(self, config: &mut Option) { - match config { - Some(config) => config.set_web_config(self), - None => *config = Some(crate::launch::current_platform::Config::new().with_web_config(self)), - } - } - } - } else { - impl TryIntoConfig for ::dioxus_web::Config { - fn into_config(self, config: &mut Option) {} - } - } - } + #[cfg(feature = "fullstack")] + { + dioxus_fullstack::launch::launch(app, Default::default(), Default::default()); } - } - #[cfg(feature = "liveview")] - if_else_cfg! { - if - not(any( - feature = "web", - feature = "desktop", - feature = "mobile", - feature = "fullstack", - feature = "static-generation" - )) + #[cfg(feature = "web")] { - pub use dioxus_liveview::launch::*; - } else { - impl TryIntoConfig for ::dioxus_liveview::Config { - fn into_config(self, config: &mut Option) {} - } + dioxus_web::launch::launch(app, Default::default(), Default::default()); } } - - #[cfg(not(any( - feature = "liveview", - feature = "desktop", - feature = "mobile", - feature = "web", - feature = "fullstack", - feature = "static-generation" - )))] - pub type Config = (); - - #[cfg(not(any( - feature = "liveview", - feature = "desktop", - feature = "mobile", - feature = "web", - feature = "fullstack", - feature = "static-generation" - )))] - pub fn launch( - root: fn() -> dioxus_core::Element, - contexts: Vec>, - platform_config: (), - ) -> ! { - #[cfg(feature = "third-party-renderer")] - panic!("No first party renderer feature enabled. It looks like you are trying to use a third party renderer. You will need to use the launch function from the third party renderer crate."); - - panic!("No platform feature enabled. Please enable one of the following features: liveview, desktop, mobile, web, tui, fullstack to use the launch API.") - } } -// ! is unstable, so we can't name the type with an alias. Instead we need to generate different variants of items with macros -macro_rules! impl_launch { - ($($return_type:tt),*) => { - /// Launch your application without any additional configuration. See [`LaunchBuilder`] for more options. - // If you aren't using a third party renderer and this is not a docs.rs build, generate a warning about no renderer being enabled - #[cfg_attr( - all(not(any( - docsrs, - feature = "third-party-renderer", - feature = "liveview", - feature = "desktop", - feature = "mobile", - feature = "web", - feature = "fullstack", - feature = "static-generation" - ))), - deprecated( - note = "No renderer is enabled. You must enable a renderer feature on the dioxus crate before calling the launch function.\nAdd `web`, `desktop`, `mobile`, `fullstack`, or `static-generation` to the `features` of dioxus field in your Cargo.toml.\n# Example\n```toml\n# ...\n[dependencies]\ndioxus = { version = \"0.5.0\", features = [\"web\"] }\n# ...\n```" - ) - )] - pub fn launch(app: fn() -> Element) -> $($return_type)* { - #[allow(deprecated)] - LaunchBuilder::new().launch(app) - } - }; -} - -// Static generation is the only platform that may exit. We can't use the `!` type here -#[cfg(any(feature = "static-generation", feature = "web"))] -impl_launch!(()); -#[cfg(not(any(feature = "static-generation", feature = "web")))] -impl_launch!(!); - -#[cfg(feature = "web")] -#[cfg_attr(docsrs, doc(cfg(feature = "web")))] -/// Launch your web application without any additional configuration. See [`LaunchBuilder`] for more options. -pub fn launch_web(app: fn() -> Element) { - LaunchBuilder::web().launch(app) -} - -#[cfg(feature = "desktop")] -#[cfg_attr(docsrs, doc(cfg(feature = "desktop")))] -/// Launch your desktop application without any additional configuration. See [`LaunchBuilder`] for more options. -pub fn launch_desktop(app: fn() -> Element) { - LaunchBuilder::desktop().launch(app) -} - -#[cfg(feature = "fullstack")] -#[cfg_attr(docsrs, doc(cfg(feature = "fullstack")))] -/// Launch your fullstack application without any additional configuration. See [`LaunchBuilder`] for more options. -pub fn launch_fullstack(app: fn() -> Element) { - LaunchBuilder::fullstack().launch(app) -} - -#[cfg(feature = "mobile")] -#[cfg_attr(docsrs, doc(cfg(feature = "mobile")))] -/// Launch your mobile application without any additional configuration. See [`LaunchBuilder`] for more options. -pub fn launch_mobile(app: fn() -> Element) { - LaunchBuilder::mobile().launch(app) -} +// /// Re-export the platform we expect the user wants +// /// +// /// If multiple platforms are enabled, we use this priority (from highest to lowest): +// /// - `fullstack` +// /// - `desktop` +// /// - `mobile` +// /// - `static-generation` +// /// - `web` +// /// - `liveview` +// mod current_platform { +// macro_rules! if_else_cfg { +// (if $attr:meta { $($then:item)* } else { $($else:item)* }) => { +// $( +// #[cfg($attr)] +// $then +// )* +// $( +// #[cfg(not($attr))] +// $else +// )* +// }; +// } +// use crate::prelude::TryIntoConfig; + +// #[cfg(feature = "fullstack")] +// pub use dioxus_fullstack::launch::*; + +// #[cfg(all(feature = "fullstack", feature = "axum"))] +// impl TryIntoConfig +// for ::dioxus_fullstack::prelude::ServeConfigBuilder +// { +// fn into_config(self, config: &mut Option) { +// match config { +// Some(config) => config.set_server_cfg(self), +// None => { +// *config = +// Some(crate::launch::current_platform::Config::new().with_server_cfg(self)) +// } +// } +// } +// } + +// #[cfg(any(feature = "desktop", feature = "mobile"))] +// if_else_cfg! { +// if not(feature = "fullstack") { +// #[cfg(feature = "desktop")] +// pub use dioxus_desktop::launch::*; +// #[cfg(not(feature = "desktop"))] +// pub use dioxus_mobile::launch::*; +// } else { +// if_else_cfg! { +// if feature = "desktop" { +// impl TryIntoConfig for ::dioxus_desktop::Config { +// fn into_config(self, config: &mut Option) { +// match config { +// Some(config) => config.set_desktop_config(self), +// None => *config = Some(crate::launch::current_platform::Config::new().with_desktop_config(self)), +// } +// } +// } +// } else { +// impl TryIntoConfig for ::dioxus_mobile::Config { +// fn into_config(self, config: &mut Option) { +// match config { +// Some(config) => config.set_mobile_cfg(self), +// None => *config = Some(crate::launch::current_platform::Config::new().with_mobile_cfg(self)), +// } +// } +// } +// } +// } +// } +// } + +// #[cfg(feature = "static-generation")] +// if_else_cfg! { +// if all(not(feature = "fullstack"), not(feature = "desktop"), not(feature = "mobile")) { +// pub use dioxus_static_site_generation::launch::*; +// } else { +// impl TryIntoConfig for ::dioxus_static_site_generation::Config { +// fn into_config(self, config: &mut Option) {} +// } +// } +// } + +// #[cfg(feature = "web")] +// if_else_cfg! { +// if not(any(feature = "desktop", feature = "mobile", feature = "fullstack", feature = "static-generation")) { +// pub use dioxus_web::launch::*; +// } else { +// if_else_cfg! { +// if feature = "fullstack" { +// impl TryIntoConfig for ::dioxus_web::Config { +// fn into_config(self, config: &mut Option) { +// match config { +// Some(config) => config.set_web_config(self), +// None => *config = Some(crate::launch::current_platform::Config::new().with_web_config(self)), +// } +// } +// } +// } else { +// impl TryIntoConfig for ::dioxus_web::Config { +// fn into_config(self, config: &mut Option) {} +// } +// } +// } +// } +// } + +// #[cfg(feature = "liveview")] +// if_else_cfg! { +// if +// not(any( +// feature = "web", +// feature = "desktop", +// feature = "mobile", +// feature = "fullstack", +// feature = "static-generation" +// )) +// { +// pub use dioxus_liveview::launch::*; +// } else { +// impl TryIntoConfig for ::dioxus_liveview::Config { +// fn into_config(self, config: &mut Option) {} +// } +// } +// } + +// #[cfg(not(any( +// feature = "liveview", +// feature = "desktop", +// feature = "mobile", +// feature = "web", +// feature = "fullstack", +// feature = "static-generation" +// )))] +// pub type Config = (); + +// #[cfg(not(any( +// feature = "liveview", +// feature = "desktop", +// feature = "mobile", +// feature = "web", +// feature = "fullstack", +// feature = "static-generation" +// )))] +// pub fn launch( +// root: fn() -> dioxus_core::Element, +// contexts: Vec>, +// platform_config: (), +// ) -> ! { +// #[cfg(feature = "third-party-renderer")] +// panic!("No first party renderer feature enabled. It looks like you are trying to use a third party renderer. You will need to use the launch function from the third party renderer crate."); + +// panic!("No platform feature enabled. Please enable one of the following features: liveview, desktop, mobile, web, tui, fullstack to use the launch API.") +// } +// } + +// // ! is unstable, so we can't name the type with an alias. Instead we need to generate different variants of items with macros +// macro_rules! impl_launch { +// ($($return_type:tt),*) => { +// /// Launch your application without any additional configuration. See [`LaunchBuilder`] for more options. +// // If you aren't using a third party renderer and this is not a docs.rs build, generate a warning about no renderer being enabled +// #[cfg_attr( +// all(not(any( +// docsrs, +// feature = "third-party-renderer", +// feature = "liveview", +// feature = "desktop", +// feature = "mobile", +// feature = "web", +// feature = "fullstack", +// feature = "static-generation" +// ))), +// deprecated( +// note = "No renderer is enabled. You must enable a renderer feature on the dioxus crate before calling the launch function.\nAdd `web`, `desktop`, `mobile`, `fullstack`, or `static-generation` to the `features` of dioxus field in your Cargo.toml.\n# Example\n```toml\n# ...\n[dependencies]\ndioxus = { version = \"0.5.0\", features = [\"web\"] }\n# ...\n```" +// ) +// )] +// pub fn launch(app: fn() -> Element) -> $($return_type)* { +// #[allow(deprecated)] +// LaunchBuilder::new().launch(app) +// } +// }; +// } + +// // Static generation is the only platform that may exit. We can't use the `!` type here +// #[cfg(any(feature = "static-generation", feature = "web"))] +// impl_launch!(()); +// #[cfg(not(any(feature = "static-generation", feature = "web")))] +// // impl_launch!(!); + +// #[cfg(feature = "web")] +// #[cfg_attr(docsrs, doc(cfg(feature = "web")))] +// /// Launch your web application without any additional configuration. See [`LaunchBuilder`] for more options. +// pub fn launch_web(app: fn() -> Element) { +// LaunchBuilder::web().launch(app) +// } + +// #[cfg(feature = "desktop")] +// #[cfg_attr(docsrs, doc(cfg(feature = "desktop")))] +// /// Launch your desktop application without any additional configuration. See [`LaunchBuilder`] for more options. +// pub fn launch_desktop(app: fn() -> Element) { +// LaunchBuilder::desktop().launch(app) +// } + +// #[cfg(feature = "fullstack")] +// #[cfg_attr(docsrs, doc(cfg(feature = "fullstack")))] +// /// Launch your fullstack application without any additional configuration. See [`LaunchBuilder`] for more options. +// pub fn launch_fullstack(app: fn() -> Element) { +// LaunchBuilder::fullstack().launch(app) +// } + +// #[cfg(feature = "mobile")] +// #[cfg_attr(docsrs, doc(cfg(feature = "mobile")))] +// /// Launch your mobile application without any additional configuration. See [`LaunchBuilder`] for more options. +// pub fn launch_mobile(app: fn() -> Element) { +// LaunchBuilder::mobile().launch(app) +// } From c413934b2e7537ab5a2275681d9697a55012a5ab Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 5 Sep 2024 05:14:46 -0700 Subject: [PATCH 079/139] fix some path issues --- packages/cli/src/builder/request.rs | 2 +- packages/cli/src/bundler/app.rs | 17 +++++++++++------ packages/cli/src/cli/run.rs | 4 +++- packages/cli/src/serve/handle.rs | 4 +++- packages/cli/src/serve/mod.rs | 9 ++++----- packages/cli/src/serve/runner.rs | 19 +++++-------------- packages/cli/src/serve/tracer.rs | 17 +++++++---------- 7 files changed, 34 insertions(+), 38 deletions(-) diff --git a/packages/cli/src/builder/request.rs b/packages/cli/src/builder/request.rs index 85205808a7..211a9880e7 100644 --- a/packages/cli/src/builder/request.rs +++ b/packages/cli/src/builder/request.rs @@ -121,7 +121,7 @@ impl BuildRequest { Platform::Web => "web".to_string(), Platform::Desktop => todo!(), Platform::Ios => todo!(), - Platform::Server => todo!(), + Platform::Server => "server".to_string(), Platform::Android => todo!(), Platform::Liveview => todo!(), } diff --git a/packages/cli/src/bundler/app.rs b/packages/cli/src/bundler/app.rs index f06df29cdc..ae54ea6546 100644 --- a/packages/cli/src/bundler/app.rs +++ b/packages/cli/src/bundler/app.rs @@ -39,6 +39,8 @@ impl AppBundle { /// Perform any finishing steps here: /// - Signing the bundle pub(crate) async fn finish(&self, destination: PathBuf) -> Result { + // std::fs::create_dir_all(&destination.join(self.build.app_name()))?; + match self.build.platform() { // Nothing special to do - just copy the workdir to the output location Platform::Web => { @@ -49,17 +51,20 @@ impl AppBundle { // Create a final .app/.exe/etc depending on the host platform, not dependent on the host Platform::Desktop => { // for now, until we have bundled hotreload, just copy the executable to the output location - Ok(self.executable.clone()) // let output_location = destination.join(self.build.app_name()); + Ok(self.executable.clone()) // Ok(output_location) } Platform::Server => { - Ok(self.executable.clone()) - }, - Platform::Liveview => { - Ok(self.executable.clone()) - }, + std::fs::copy( + self.executable.clone(), + destination.join(self.build.app_name()), + )?; + + Ok(destination.join(self.build.app_name())) + } + Platform::Liveview => Ok(self.executable.clone()), // Create a .ipa, only from macOS Platform::Ios => todo!(), diff --git a/packages/cli/src/cli/run.rs b/packages/cli/src/cli/run.rs index 27597b0fa1..ce2bb8a173 100644 --- a/packages/cli/src/cli/run.rs +++ b/packages/cli/src/cli/run.rs @@ -33,7 +33,9 @@ impl RunArgs { let fullstack_ip = "127.0.0.1:6955".parse().unwrap(); for bundle in bundles { - runner.open(bundle, devserver_ip, Some(fullstack_ip)).await; + runner + .open(bundle, devserver_ip, Some(fullstack_ip)) + .await?; } loop { diff --git a/packages/cli/src/serve/handle.rs b/packages/cli/src/serve/handle.rs index 08e5b589c4..f3d4767cb5 100644 --- a/packages/cli/src/serve/handle.rs +++ b/packages/cli/src/serve/handle.rs @@ -37,7 +37,9 @@ impl AppHandle { ); } - let work_dir = std::env::temp_dir(); + // let work_dir = std::env::temp_dir(); + let work_dir = app.build.krate.out_dir().join("launch"); + std::fs::create_dir_all(&work_dir)?; let executable = app.finish(work_dir).await?; let mut handle = AppHandle { diff --git a/packages/cli/src/serve/mod.rs b/packages/cli/src/serve/mod.rs index 40fe75e0ad..f490e5e6df 100644 --- a/packages/cli/src/serve/mod.rs +++ b/packages/cli/src/serve/mod.rs @@ -16,14 +16,13 @@ mod tracer; mod update; mod watcher; -use handle::*; -use output::*; +pub(crate) use handle::*; +pub(crate) use output::*; pub(crate) use runner::*; -use server::*; +pub(crate) use server::*; pub(crate) use tracer::*; pub(crate) use update::*; -use update::*; -use watcher::*; +pub(crate) use watcher::*; /// For *all* builds, the CLI spins up a dedicated webserver, file watcher, and build infrastructure to serve the project. /// diff --git a/packages/cli/src/serve/runner.rs b/packages/cli/src/serve/runner.rs index 8bf85b2ae4..e389023b83 100644 --- a/packages/cli/src/serve/runner.rs +++ b/packages/cli/src/serve/runner.rs @@ -1,5 +1,5 @@ use super::{handle::AppHandle, ServeUpdate}; -use crate::{builder::Platform, bundler::AppBundle, cli::serve::ServeArgs, DioxusCrate, Result}; +use crate::{builder::Platform, bundler::AppBundle, Result}; use futures_util::{future::OptionFuture, stream::FuturesUnordered}; use std::{collections::HashMap, net::SocketAddr}; use tokio_stream::StreamExt; @@ -20,14 +20,6 @@ impl AppRunner { } } - pub(crate) async fn shutdown(&mut self) { - for (_, mut handle) in self.running.drain() { - if let Some(mut child) = handle.child.take() { - let _ = child.kill().await; - } - } - } - pub(crate) async fn wait(&mut self) -> ServeUpdate { // If there are no running apps, we can just return pending to avoid deadlocking if self.running.is_empty() { @@ -69,12 +61,11 @@ impl AppRunner { devserver_ip: SocketAddr, fullstack_address: Option, ) -> Result<&AppHandle> { - let handle = AppHandle::start(app, devserver_ip, fullstack_address).await?; - let platform = handle.app.build.platform(); + let platform = app.build.build.platform(); + self.kill(platform).await; - if let Some(_previous) = self.running.insert(platform, handle) { - // close the old app, gracefully, hopefully - } + let handle = AppHandle::start(app, devserver_ip, fullstack_address).await?; + self.running.insert(platform, handle); Ok(self.running.get(&platform).unwrap()) } diff --git a/packages/cli/src/serve/tracer.rs b/packages/cli/src/serve/tracer.rs index 5b7d8fe26a..c9a8a7caa2 100644 --- a/packages/cli/src/serve/tracer.rs +++ b/packages/cli/src/serve/tracer.rs @@ -33,16 +33,13 @@ impl TraceController { filter = EnvFilter::from_env(LOG_ENV); } - let cli_writer = Mutex::new(Writer { - stdout: io::stdout(), - }); - - // Build tracing - let fmt_layer = tracing_subscriber::fmt::layer() - .with_writer(cli_writer) - .with_filter(filter); - - let sub = tracing_subscriber::registry().with(fmt_layer); + let sub = tracing_subscriber::registry().with( + tracing_subscriber::fmt::layer() + .with_writer(Mutex::new(Writer { + stdout: io::stdout(), + })) + .with_filter(filter), + ); #[cfg(feature = "tokio-console")] let sub = sub.with(console_subscriber::spawn()); From 962eb060310ca439a6c4a798a5587ab684b42c1a Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 5 Sep 2024 05:36:28 -0700 Subject: [PATCH 080/139] clean up launch --- example-projects/router/src/main.rs | 2 +- examples/crm.rs | 2 +- examples/custom_html.rs | 2 +- examples/suspense.rs | 2 +- packages/cli/.gitignore | 1 - packages/dioxus/src/launch.rs | 6 +++++- packages/dioxus/src/lib.rs | 2 +- packages/fullstack/src/document/mod.rs | 2 -- packages/playwright-tests/fullstack/src/main.rs | 2 +- packages/playwright-tests/nested-suspense/src/main.rs | 2 +- .../static-generation/examples/github-pages/src/main.rs | 2 +- 11 files changed, 13 insertions(+), 12 deletions(-) diff --git a/example-projects/router/src/main.rs b/example-projects/router/src/main.rs index 3058dd7f6a..9a9d4328dc 100644 --- a/example-projects/router/src/main.rs +++ b/example-projects/router/src/main.rs @@ -7,7 +7,7 @@ use dioxus::prelude::*; fn main() { - LaunchBuilder::new() + dioxus::launch_builder() .with_cfg(server_only!(ServeConfig::builder().incremental( IncrementalRendererConfig::default() .invalidate_after(std::time::Duration::from_secs(120)), diff --git a/examples/crm.rs b/examples/crm.rs index 4299a962d0..7c19413b06 100644 --- a/examples/crm.rs +++ b/examples/crm.rs @@ -12,7 +12,7 @@ use dioxus::prelude::*; fn main() { - LaunchBuilder::new() + dioxus::launch_builder() .with_cfg(desktop!({ use dioxus::desktop::{LogicalSize, WindowBuilder}; dioxus::desktop::Config::default() diff --git a/examples/custom_html.rs b/examples/custom_html.rs index dbbe334f05..91ca00e110 100644 --- a/examples/custom_html.rs +++ b/examples/custom_html.rs @@ -4,7 +4,7 @@ use dioxus::prelude::*; fn main() { - LaunchBuilder::new() + dioxus::launch_builder() .with_cfg( dioxus::desktop::Config::new().with_custom_index( r#" diff --git a/examples/suspense.rs b/examples/suspense.rs index 014bb3276a..b14921113a 100644 --- a/examples/suspense.rs +++ b/examples/suspense.rs @@ -15,7 +15,7 @@ use dioxus::desktop::{Config, LogicalSize, WindowBuilder}; use dioxus::prelude::*; fn main() { - LaunchBuilder::new() + dioxus::launch_builder() .with_cfg(desktop! { Config::new().with_window( WindowBuilder::new() diff --git a/packages/cli/.gitignore b/packages/cli/.gitignore index f61923446c..6700b1332d 100644 --- a/packages/cli/.gitignore +++ b/packages/cli/.gitignore @@ -2,4 +2,3 @@ Cargo.lock .DS_Store .idea/ -ignore/ diff --git a/packages/dioxus/src/launch.rs b/packages/dioxus/src/launch.rs index 24b31c69f6..77f47929a5 100644 --- a/packages/dioxus/src/launch.rs +++ b/packages/dioxus/src/launch.rs @@ -6,6 +6,10 @@ use std::any::Any; use crate::prelude::*; +pub fn launch_builder() -> LaunchBuilder { + LaunchBuilder::new() +} + pub fn launch(app: fn() -> Element) { #[allow(deprecated)] LaunchBuilder::new().launch(app) @@ -397,7 +401,7 @@ impl LaunchBuilder { // #[cfg_attr(docsrs, doc(cfg(feature = "fullstack")))] // /// Launch your fullstack application without any additional configuration. See [`LaunchBuilder`] for more options. // pub fn launch_fullstack(app: fn() -> Element) { -// LaunchBuilder::fullstack().launch(app) +// LaunchBuilder::new().launch(app) // } // #[cfg(feature = "mobile")] diff --git a/packages/dioxus/src/lib.rs b/packages/dioxus/src/lib.rs index f745b0c01f..7db7524b15 100644 --- a/packages/dioxus/src/lib.rs +++ b/packages/dioxus/src/lib.rs @@ -41,7 +41,7 @@ pub mod events { #[cfg(feature = "launch")] #[cfg_attr(docsrs, doc(cfg(feature = "launch")))] #[allow(deprecated)] -pub use launch::launch; +pub use launch::{launch, launch_builder}; #[cfg(feature = "hooks")] #[cfg_attr(docsrs, doc(cfg(feature = "hooks")))] diff --git a/packages/fullstack/src/document/mod.rs b/packages/fullstack/src/document/mod.rs index 49b332628a..0c380994f6 100644 --- a/packages/fullstack/src/document/mod.rs +++ b/packages/fullstack/src/document/mod.rs @@ -2,9 +2,7 @@ #[cfg(feature = "server")] pub(crate) mod server; - #[cfg(feature = "server")] pub use server::ServerDocument; - #[cfg(all(feature = "web", feature = "document"))] pub(crate) mod web; diff --git a/packages/playwright-tests/fullstack/src/main.rs b/packages/playwright-tests/fullstack/src/main.rs index 8cd8960882..cbd3d0ef88 100644 --- a/packages/playwright-tests/fullstack/src/main.rs +++ b/packages/playwright-tests/fullstack/src/main.rs @@ -8,7 +8,7 @@ use dioxus::{prelude::*, CapturedError}; fn main() { - LaunchBuilder::fullstack().launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/packages/playwright-tests/nested-suspense/src/main.rs b/packages/playwright-tests/nested-suspense/src/main.rs index c60efbdbd9..d859948485 100644 --- a/packages/playwright-tests/nested-suspense/src/main.rs +++ b/packages/playwright-tests/nested-suspense/src/main.rs @@ -12,7 +12,7 @@ use dioxus::prelude::*; use serde::{Deserialize, Serialize}; fn main() { - LaunchBuilder::fullstack().launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/packages/static-generation/examples/github-pages/src/main.rs b/packages/static-generation/examples/github-pages/src/main.rs index ba80f9983b..0b3aa4ef11 100644 --- a/packages/static-generation/examples/github-pages/src/main.rs +++ b/packages/static-generation/examples/github-pages/src/main.rs @@ -6,7 +6,7 @@ use dioxus::prelude::*; // Generate all routes and output them to the static path fn main() { - LaunchBuilder::new() + dioxus::launch_builder() .with_cfg(dioxus::static_site_generation::Config::new().github_pages()) .launch(|| { rsx! { From 374dd438e2794683ae8c235054dfbddaf778a711 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 5 Sep 2024 05:38:58 -0700 Subject: [PATCH 081/139] remove old manual websocket receiver --- packages/devtools/src/lib.rs | 42 +----------------------------------- 1 file changed, 1 insertion(+), 41 deletions(-) diff --git a/packages/devtools/src/lib.rs b/packages/devtools/src/lib.rs index 3e372feee7..ab5779f4f1 100644 --- a/packages/devtools/src/lib.rs +++ b/packages/devtools/src/lib.rs @@ -29,13 +29,8 @@ pub fn apply_changes(dom: &VirtualDom, msg: &HotReloadMsg) { /// This doesn't use any form of security or protocol, so it's not safe to expose to the internet. #[cfg(not(target_arch = "wasm32"))] pub fn connect(addr: String, mut callback: impl FnMut(DevserverMsg) + Send + 'static) { - use std::{ - io::{self, BufRead}, - net::{SocketAddr, TcpListener, ToSocketAddrs}, - }; - std::thread::spawn(move || { - let (mut websocket, req) = match tungstenite::connect(addr.clone()) { + let (mut websocket, _req) = match tungstenite::connect(addr.clone()) { Ok((websocket, req)) => (websocket, req), Err(err) => { eprintln!("Failed to connect to devserver at {} because {}", addr, err); @@ -43,8 +38,6 @@ pub fn connect(addr: String, mut callback: impl FnMut(DevserverMsg) + Send + 'st } }; - println!("Connected to devserver at {}", addr); - while let Ok(msg) = websocket.read() { match msg { tungstenite::Message::Text(text) => { @@ -59,38 +52,5 @@ pub fn connect(addr: String, mut callback: impl FnMut(DevserverMsg) + Send + 'st } } } - - eprintln!("Disconnected from devserver"); - - // let connect = std::net::TcpStream::connect(addr); - // let Ok(mut stream) = connect else { - // return; - // }; - - // // Wrap the stream in a BufReader, so we can use the BufRead methods - // let mut reader = io::BufReader::new(&mut stream); - - // // Loop and read lines from the stream - // loop { - // let mut buf = String::new(); - // let msg = reader.read_line(&mut buf); - - // let Ok(amt) = msg else { - // break; - // }; - - // // eof received - connection closed - // if amt == 0 { - // break; - // } - - // reader.consume(amt); - - // if let Ok(msg) = serde_json::from_str(&buf) { - // callback(msg); - // } else { - // eprintln!("Failed to parse message from devserver: {:?}", buf); - // } - // } }); } From e9e409b795b018a09558a2b916e3c14435bae183 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 5 Sep 2024 05:45:51 -0700 Subject: [PATCH 082/139] doctor command --- packages/cli/src/builder/platform.rs | 10 ---------- packages/cli/src/cli/doctor.rs | 10 ++++++++++ packages/cli/src/cli/mod.rs | 6 ++++++ packages/cli/src/main.rs | 2 ++ 4 files changed, 18 insertions(+), 10 deletions(-) create mode 100644 packages/cli/src/cli/doctor.rs diff --git a/packages/cli/src/builder/platform.rs b/packages/cli/src/builder/platform.rs index 36d48a7ab3..daa4db9cad 100644 --- a/packages/cli/src/builder/platform.rs +++ b/packages/cli/src/builder/platform.rs @@ -88,16 +88,6 @@ impl Display for Platform { } impl Platform { - /// All platforms the dioxus CLI supports - pub(crate) const ALL: &'static [Self] = &[ - Platform::Web, - Platform::Desktop, - Platform::Ios, - Platform::Android, - Platform::Liveview, - Platform::Server, - ]; - /// Get the feature name for the platform in the dioxus crate pub(crate) fn feature_name(&self) -> &str { match self { diff --git a/packages/cli/src/cli/doctor.rs b/packages/cli/src/cli/doctor.rs new file mode 100644 index 0000000000..7c89c076d4 --- /dev/null +++ b/packages/cli/src/cli/doctor.rs @@ -0,0 +1,10 @@ +use clap::Parser; + +#[derive(Clone, Debug, Parser)] +pub struct Doctor {} + +impl Doctor { + pub async fn run(self) -> anyhow::Result<()> { + Ok(()) + } +} diff --git a/packages/cli/src/cli/mod.rs b/packages/cli/src/cli/mod.rs index 4d604a019e..8bd108cb06 100644 --- a/packages/cli/src/cli/mod.rs +++ b/packages/cli/src/cli/mod.rs @@ -5,6 +5,7 @@ pub(crate) mod check; pub(crate) mod clean; pub(crate) mod config; pub(crate) mod create; +pub(crate) mod doctor; pub(crate) mod httpserver; pub(crate) mod init; pub(crate) mod link; @@ -89,6 +90,10 @@ pub(crate) enum Commands { #[clap(name = "run")] Run(run::RunArgs), + /// Ensure all the tooling is installed and configured correctly + #[clap(name = "doctor")] + Doctor(doctor::Doctor), + /// Dioxus config file controls. #[clap(subcommand)] Config(config::Config), @@ -109,6 +114,7 @@ impl Display for Commands { Commands::Bundle(_) => write!(f, "bundle"), Commands::HttpServer(_) => write!(f, "http-server"), Commands::Run(_) => write!(f, "run"), + Commands::Doctor(_) => write!(f, "doctor"), } } } diff --git a/packages/cli/src/main.rs b/packages/cli/src/main.rs index b73db904af..9657b6eaec 100644 --- a/packages/cli/src/main.rs +++ b/packages/cli/src/main.rs @@ -62,5 +62,7 @@ async fn main() -> anyhow::Result<()> { Run(opts) => opts.run().await.context("🚫 Running project failed:"), HttpServer(opts) => opts.serve().await.context("🚫 Serving project failed:"), + + Doctor(opts) => opts.run().await.context("🚫 Checking project failed:"), } } From d411ec96aa561c0d0a57c859811c4866f81d8b40 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 5 Sep 2024 05:48:40 -0700 Subject: [PATCH 083/139] allow desktop to scroll --- packages/desktop/headless_tests/utils.rs | 1 - packages/desktop/src/index.html | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/desktop/headless_tests/utils.rs b/packages/desktop/headless_tests/utils.rs index 11b28f2068..9367bd4236 100644 --- a/packages/desktop/headless_tests/utils.rs +++ b/packages/desktop/headless_tests/utils.rs @@ -1,7 +1,6 @@ #![allow(unused)] // for whatever reason, the compiler is not recognizing the use of these functions use dioxus::prelude::*; -use dioxus_core::Element; pub fn check_app_exits(app: fn() -> Element) { use dioxus_desktop::tao::window::WindowBuilder; diff --git a/packages/desktop/src/index.html b/packages/desktop/src/index.html index 423d42bc94..f70b8149eb 100644 --- a/packages/desktop/src/index.html +++ b/packages/desktop/src/index.html @@ -2,7 +2,8 @@ Dioxus app - + + From 781262125b316b96c9ef7edc863bc15977b98814 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 6 Sep 2024 12:17:13 -0700 Subject: [PATCH 084/139] cut apart router crate --- .vscode/settings.json | 17 +- Cargo.lock | 14 +- Cargo.toml | 18 +- example-projects/basic-liveview/.gitignore | 4 + example-projects/basic-liveview/Cargo.toml | 14 + example-projects/basic-liveview/src/main.rs | 13 + example-projects/fullstack-mobile/src/main.rs | 44 +- packages/cli/src/builder/cargo.rs | 107 ++- packages/cli/src/builder/platform.rs | 3 + packages/cli/src/builder/progress.rs | 152 +--- packages/cli/src/builder/web.rs | 65 +- packages/cli/src/bundler/app.rs | 96 +-- packages/cli/src/cli/run.rs | 7 +- packages/cli/src/cli/serve.rs | 2 +- packages/cli/src/dioxus_crate.rs | 26 +- packages/cli/src/main.rs | 1 + packages/cli/src/serve/server.rs | 99 ++- packages/cli/src/serve/watcher.rs | 8 +- packages/cli/src/tooling/android.rs | 0 packages/cli/src/tooling/ios.rs | 1 + packages/cli/src/tooling/mac.rs | 0 packages/cli/src/tooling/mod.rs | 5 + packages/cli/src/tooling/web.rs | 63 ++ packages/cli/src/tooling/wsl.rs | 0 packages/core/src/runtime.rs | 7 + packages/desktop/headless_tests/utils.rs | 2 +- packages/desktop/src/document.rs | 20 + packages/dioxus/Cargo.toml | 21 +- packages/dioxus/src/launch.rs | 15 +- packages/document/src/document.rs | 260 ++++++- packages/fullstack/Cargo.toml | 43 +- packages/fullstack/src/document/mod.rs | 2 + packages/fullstack/src/document/web.rs | 20 + .../fullstack/src/html_storage/serialize.rs | 5 +- packages/fullstack/src/serve_config.rs | 4 +- packages/lazy-js-bundle/src/lib.rs | 57 +- packages/liveview/Cargo.toml | 5 +- packages/liveview/build.rs | 7 + packages/liveview/src/config.rs | 1 - .../liveview/src/{eval.rs => document.rs} | 20 + .../liveview.rs => liveview/src/history.rs} | 0 packages/liveview/src/js/hash.txt | 1 + packages/liveview/src/js/main.js | 1 + packages/liveview/src/lib.rs | 17 +- packages/liveview/src/pool.rs | 4 +- packages/liveview/src/{main.js => ts/main.ts} | 33 +- packages/liveview/tsconfig.json | 19 + packages/router-macro/src/lib.rs | 8 + packages/router/Cargo.toml | 37 +- packages/router/examples/manual.rs | 68 ++ packages/router/examples/simple_routes.rs | 24 +- packages/router/src/components/link.rs | 106 +-- packages/router/src/components/mod.rs | 15 + packages/router/src/components/outlet.rs | 2 +- packages/router/src/components/router.rs | 52 +- .../router/src/contexts/generic_router.rs | 102 +++ packages/router/src/contexts/outlet.rs | 2 +- packages/router/src/contexts/router.rs | 416 ++++------ packages/router/src/history/memory.rs | 54 +- packages/router/src/history/mod.rs | 388 ---------- packages/router/src/history/web_history.rs | 42 - packages/router/src/hooks/use_route.rs | 2 +- packages/router/src/into_routable.rs | 76 ++ packages/router/src/lib.rs | 26 +- packages/router/src/navigation.rs | 72 +- packages/router/src/routable.rs | 727 +++++------------- packages/router/src/routable/hash.rs | 89 +++ packages/router/src/routable/query.rs | 147 ++++ packages/router/src/routable/segments.rs | 178 +++++ packages/router/src/routable/sitemap.rs | 0 packages/router/src/router_cfg.rs | 136 ++-- packages/router/tests/site_map.rs | 3 + packages/router/tests/via_ssr/link.rs | 55 +- packages/router/tests/via_ssr/outlet.rs | 30 +- packages/router/tests/via_ssr/redirect.rs | 6 +- .../router/tests/via_ssr/without_index.rs | 2 +- packages/server/Cargo.toml | 6 + packages/server/src/lib.rs | 14 + packages/static-generation/Cargo.toml | 19 +- packages/web/src/document.rs | 20 + .../src/history/web.rs => web/src/history.rs} | 43 ++ .../{router => web}/src/history/web_hash.rs | 0 .../{router => web}/src/history/web_scroll.rs | 0 83 files changed, 2280 insertions(+), 2010 deletions(-) create mode 100644 example-projects/basic-liveview/.gitignore create mode 100644 example-projects/basic-liveview/Cargo.toml create mode 100644 example-projects/basic-liveview/src/main.rs create mode 100644 packages/cli/src/tooling/android.rs create mode 100644 packages/cli/src/tooling/ios.rs create mode 100644 packages/cli/src/tooling/mac.rs create mode 100644 packages/cli/src/tooling/mod.rs create mode 100644 packages/cli/src/tooling/web.rs create mode 100644 packages/cli/src/tooling/wsl.rs create mode 100644 packages/liveview/build.rs rename packages/liveview/src/{eval.rs => document.rs} (77%) rename packages/{router/src/history/liveview.rs => liveview/src/history.rs} (100%) create mode 100644 packages/liveview/src/js/hash.txt create mode 100644 packages/liveview/src/js/main.js rename packages/liveview/src/{main.js => ts/main.ts} (78%) create mode 100644 packages/liveview/tsconfig.json create mode 100644 packages/router/examples/manual.rs create mode 100644 packages/router/src/components/mod.rs create mode 100644 packages/router/src/contexts/generic_router.rs delete mode 100644 packages/router/src/history/web_history.rs create mode 100644 packages/router/src/into_routable.rs create mode 100644 packages/router/src/routable/hash.rs create mode 100644 packages/router/src/routable/query.rs create mode 100644 packages/router/src/routable/segments.rs create mode 100644 packages/router/src/routable/sitemap.rs create mode 100644 packages/server/Cargo.toml create mode 100644 packages/server/src/lib.rs rename packages/{router/src/history/web.rs => web/src/history.rs} (88%) rename packages/{router => web}/src/history/web_hash.rs (100%) rename packages/{router => web}/src/history/web_scroll.rs (100%) diff --git a/.vscode/settings.json b/.vscode/settings.json index df398f22be..ab0a85d73b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,13 +1,18 @@ { - "editor.formatOnSave": true, + // Disable toml formatting since everyone uses it differently "[toml]": { "editor.formatOnSave": false }, - // "rust-analyzer.check.workspace": true, - // "rust-analyzer.check.workspace": false, - // "rust-analyzer.check.features": "all", - // "rust-analyzer.cargo.features": "all", + // // Make sure we turn on features so we actually get intellisense + // "rust-analyzer.cargo.features": [ + // "server", + // "web", + // "fullstack", + // "desktop", + // "mobile", + // ], + // don't check tests since that adds lots of comp time "rust-analyzer.check.allTargets": false, - // we don't want the formatter to kick in while we're working on dioxus itself + // don't format with dioxus since we want the autoformatting tests to take invalid inputs "dioxus.formatOnSave": "disabled", } diff --git a/Cargo.lock b/Cargo.lock index 1b3a43e05f..aa7c2dd776 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -798,6 +798,13 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "basic-liveview" +version = "0.1.0" +dependencies = [ + "dioxus", +] + [[package]] name = "bcder" version = "0.7.4" @@ -2788,6 +2795,7 @@ dependencies = [ "futures-channel", "futures-util", "generational-box", + "lazy-js-bundle", "rustc-hash 1.1.0", "serde", "serde_json", @@ -2865,10 +2873,10 @@ dependencies = [ "console_error_panic_hook", "criterion", "dioxus", + "dioxus-document", "dioxus-fullstack", "dioxus-lib", "dioxus-liveview", - "dioxus-router", "dioxus-router-macro", "dioxus-ssr", "gloo", @@ -2918,6 +2926,10 @@ dependencies = [ name = "dioxus-runtime-config" version = "0.6.0-alpha.2" +[[package]] +name = "dioxus-server" +version = "0.6.0-alpha.2" + [[package]] name = "dioxus-signals" version = "0.6.0-alpha.2" diff --git a/Cargo.toml b/Cargo.toml index 579d545f70..ca2021ef53 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -69,6 +69,8 @@ members = [ "example-projects/fullstack-mobile", "example-projects/tailwind", "example-projects/PWA-example", + "example-projects/basic-liveview", + "packages/server", ] [workspace.package] @@ -99,15 +101,16 @@ dioxus-check = { path = "packages/check", version = "0.6.0-alpha.2" } dioxus-rsx = { path = "packages/rsx", version = "0.6.0-alpha.2" } rsx-rosetta = { path = "packages/rsx-rosetta", version = "0.6.0-alpha.2" } dioxus-signals = { path = "packages/signals", version = "0.6.0-alpha.2" } -generational-box = { path = "packages/generational-box", version = "0.6.0-alpha.2" } dioxus-devtools = { path = "packages/devtools", version = "0.6.0-alpha.2" } dioxus-devtools-types = { path = "packages/devtools-types", version = "0.6.0-alpha.2" } -dioxus-fullstack = { path = "packages/fullstack", version = "0.6.0-alpha.2" } +dioxus-server = { path = "packages/server", version = "0.6.0-alpha.2" } +dioxus-fullstack = { path = "packages/fullstack", version = "0.6.0-alpha.2", default-features = false } dioxus-static-site-generation = { path = "packages/static-generation", version = "0.6.0-alpha.2" } dioxus_server_macro = { path = "packages/server-macro", version = "0.6.0-alpha.2", default-features = false } dioxus-isrg = { path = "packages/isrg", version = "0.6.0-alpha.2" } lazy-js-bundle = { path = "packages/lazy-js-bundle", version = "0.6.0-alpha.2" } dioxus-runtime-config = { path = "packages/runtime-config", version = "0.6.0-alpha.2" } +generational-box = { path = "packages/generational-box", version = "0.6.0-alpha.2" } manganis = { path = "packages/manganis", version = "0.6.0-alpha.2" } manganis-macro = { path = "packages/manganis-macro", version = "0.6.0-alpha.2" } @@ -173,6 +176,9 @@ proc-macro2-diagnostics = { version = "0.10", default-features = false } env_logger = "0.11.0" tracing-subscriber = "0.3.17" chrono = { version = "0.4.34" } +gloo = { version = "0.8.0" } +gloo-utils = { version = "0.1.6" } +rustversion = "1.0.17" # desktop wry = { version = "0.42.0", default-features = false } @@ -201,14 +207,6 @@ inherits = "release" opt-level = 1 debug = true -# [profile.cli-dev] -# inherits = "dev" -# opt-level = 1 - -# # Enable high optimizations for dependencies (incl. Bevy), but not for our code: -# [profile.cli-dev.package."*"] -# opt-level = 3 - # Disable debug assertions to check the released path of core and other packages, but build without optimizations to keep build times quick [profile.release-unoptimized] inherits = "dev" diff --git a/example-projects/basic-liveview/.gitignore b/example-projects/basic-liveview/.gitignore new file mode 100644 index 0000000000..21fff11dd3 --- /dev/null +++ b/example-projects/basic-liveview/.gitignore @@ -0,0 +1,4 @@ +dist +target +static +.dioxus \ No newline at end of file diff --git a/example-projects/basic-liveview/Cargo.toml b/example-projects/basic-liveview/Cargo.toml new file mode 100644 index 0000000000..8fde99c483 --- /dev/null +++ b/example-projects/basic-liveview/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "basic-liveview" +version = "0.1.0" +edition = "2021" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +dioxus = { workspace = true, features = ["liveview"] } + +[features] +default = [] +liveview = ["dioxus/liveview"] diff --git a/example-projects/basic-liveview/src/main.rs b/example-projects/basic-liveview/src/main.rs new file mode 100644 index 0000000000..bd334a0189 --- /dev/null +++ b/example-projects/basic-liveview/src/main.rs @@ -0,0 +1,13 @@ +use dioxus::prelude::*; + +fn main() { + println!("Launching app..."); + + dioxus::launch(app); +} + +fn app() -> Element { + rsx! { + div { "Hello, world!" } + } +} diff --git a/example-projects/fullstack-mobile/src/main.rs b/example-projects/fullstack-mobile/src/main.rs index 8582bb927d..ad57565f07 100644 --- a/example-projects/fullstack-mobile/src/main.rs +++ b/example-projects/fullstack-mobile/src/main.rs @@ -7,7 +7,7 @@ fn main() { } fn app() -> Element { - let mut favorite_dog = use_signal(|| "Fido".to_string()); + let mut favorite_dog = use_signal(|| None); rsx! { document::Stylesheet { href: asset!("/assets/style.css") } @@ -15,15 +15,20 @@ fn app() -> Element { div { class: "w-full h-full text-center my-20", h1 { class: "text-4xl font-bold", "Dioxus iOS apps!" } - h3 { class: "sparkles", "Favorite dog: {favorite_dog}" } - button { onclick: move |_| async move { - let dog = get_random_dog().await.unwrap_or_else(|err| format!("Error: {err}")); - favorite_dog.set(dog); - }, "New favorite dog!" } + button { + onclick: move |_| async move { + let dog = get_random_dog().await.unwrap_or_else(|err| format!("Error: {err}")); + favorite_dog.set(Some(dog)); + }, + "New favorite dog!" + } } div { class: "w-full h-full flex flex-col mx-auto space-y-12", - ImageList { dogs: 3, style: "fluffy" } + if let Some(favorite_dog) = favorite_dog() { + img { src: "{favorite_dog}", max_width: "500px", height: "500px", margin: "auto" } + } + ImageList { dogs: 3, style: "cute" } } } } @@ -45,22 +50,19 @@ fn ImageList(dogs: ReadOnlySignal, style: String) -> Element { #[server(endpoint = "get_random_dog")] async fn get_random_dog() -> Result { - println!("Getting a random dog from the server!"); - tracing::info!("Getting a random dog from the server!"); let breed = "husky".to_string(); + println!("Getting a random dog from the server!"); - Ok(breed) - - // #[derive(serde::Deserialize, Debug)] - // struct DogApi { - // message: String, - // } + #[derive(serde::Deserialize, Debug)] + struct DogApi { + message: String, + } - // let dog = reqwest::get(format!("https://dog.ceo/api/breed/{breed}/images/random")) - // .await - // .unwrap() - // .json::() - // .await?; + let dog = reqwest::get(format!("https://dog.ceo/api/breed/{breed}/images/random")) + .await + .unwrap() + .json::() + .await?; - // Ok(dog.message) + Ok(dog.message) } diff --git a/packages/cli/src/builder/cargo.rs b/packages/cli/src/builder/cargo.rs index e7edbf86b3..85db7a7931 100644 --- a/packages/cli/src/builder/cargo.rs +++ b/packages/cli/src/builder/cargo.rs @@ -3,8 +3,9 @@ use crate::{assets::AssetManifest, link::LINK_OUTPUT_ENV_VAR}; use crate::{builder::Platform, bundler::AppBundle}; use crate::{link::InterceptedArgs, Result}; use anyhow::Context; -use std::process::Stdio; -use tokio::process::Command; +use serde::Deserialize; +use std::{path::PathBuf, process::Stdio}; +use tokio::{io::AsyncBufReadExt, process::Command}; impl BuildRequest { pub(crate) async fn build(self) -> Result { @@ -26,7 +27,7 @@ impl BuildRequest { pub(crate) async fn verify_tooling(&self) -> Result<()> { match self.platform() { // If this is a web, build make sure we have the web build tooling set up - Platform::Web => self.install_web_build_tooling().await?, + Platform::Web => {} // Make sure we have mobile tooling if need be Platform::Ios => {} @@ -43,6 +44,106 @@ impl BuildRequest { Ok(()) } + /// Run `cargo`, returning the location of the final exectuable + /// + /// todo: add some stats here, like timing reports, crate-graph optimizations, etc + pub(crate) async fn build_cargo(&self) -> anyhow::Result { + // Extract the unit count of the crate graph so build_cargo has more accurate data + let crate_count = self.get_unit_count_estimate().await; + + self.status_starting_build(); + + let mut child = Command::new("cargo") + .arg("rustc") + .envs( + self.custom_target_dir + .as_ref() + .map(|dir| ("CARGO_TARGET_DIR", dir)), + ) + .current_dir(self.krate.crate_dir()) + .arg("--message-format") + .arg("json-diagnostic-rendered-ansi") + .args(&self.build_arguments()) + .arg("--") + .args(self.rust_flags.clone()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .context("Failed to spawn cargo build")?; + + let stdout = tokio::io::BufReader::new(child.stdout.take().unwrap()); + let stderr = tokio::io::BufReader::new(child.stderr.take().unwrap()); + + let mut output_location = None; + let mut stdout = stdout.lines(); + let mut stderr = stderr.lines(); + let mut units_compiled = 0; + let mut errors = Vec::new(); + + loop { + use cargo_metadata::Message; + + let line = tokio::select! { + Ok(Some(line)) = stdout.next_line() => line, + Ok(Some(line)) = stderr.next_line() => line, + else => break, + }; + + let mut deserializer = serde_json::Deserializer::from_str(line.trim()); + deserializer.disable_recursion_limit(); + + let message = + Message::deserialize(&mut deserializer).unwrap_or(Message::TextLine(line)); + + match message { + Message::BuildScriptExecuted(_) => units_compiled += 1, + Message::TextLine(line) => self.status_build_message(line), + Message::CompilerMessage(msg) => { + let message = msg.message; + self.status_build_diagnostic(&message); + const WARNING_LEVELS: &[cargo_metadata::diagnostic::DiagnosticLevel] = &[ + cargo_metadata::diagnostic::DiagnosticLevel::Help, + cargo_metadata::diagnostic::DiagnosticLevel::Note, + cargo_metadata::diagnostic::DiagnosticLevel::Warning, + cargo_metadata::diagnostic::DiagnosticLevel::Error, + cargo_metadata::diagnostic::DiagnosticLevel::FailureNote, + cargo_metadata::diagnostic::DiagnosticLevel::Ice, + ]; + const FATAL_LEVELS: &[cargo_metadata::diagnostic::DiagnosticLevel] = &[ + cargo_metadata::diagnostic::DiagnosticLevel::Error, + cargo_metadata::diagnostic::DiagnosticLevel::FailureNote, + cargo_metadata::diagnostic::DiagnosticLevel::Ice, + ]; + if WARNING_LEVELS.contains(&message.level) { + if let Some(rendered) = message.rendered { + errors.push(rendered); + } + } + if FATAL_LEVELS.contains(&message.level) { + return Err(anyhow::anyhow!(errors.join("\n"))); + } + } + Message::CompilerArtifact(artifact) => { + units_compiled += 1; + match artifact.executable { + Some(executable) => output_location = Some(executable.into()), + None => { + self.status_build_progress(units_compiled as f64 / crate_count as f64) + } + } + } + Message::BuildFinished(finished) => { + if !finished.success { + return Err(anyhow::anyhow!("Build failed")); + } + } + _ => {} + } + } + + output_location.context("Build did not return an executable") + } + /// Run the linker intercept and then fill in our AssetManifest from the incremental artifacts /// /// This will execute `dx` with an env var set to force `dx` to operate as a linker, and then diff --git a/packages/cli/src/builder/platform.rs b/packages/cli/src/builder/platform.rs index daa4db9cad..d72fcbe119 100644 --- a/packages/cli/src/builder/platform.rs +++ b/packages/cli/src/builder/platform.rs @@ -75,6 +75,9 @@ impl FromStr for Platform { "web" => Ok(Self::Web), "desktop" => Ok(Self::Desktop), "liveview" => Ok(Self::Liveview), + "server" => Ok(Self::Server), + "ios" => Ok(Self::Ios), + "android" => Ok(Self::Android), _ => Err(UnknownPlatformError), } } diff --git a/packages/cli/src/builder/progress.rs b/packages/cli/src/builder/progress.rs index 68cc5050a7..b97c142459 100644 --- a/packages/cli/src/builder/progress.rs +++ b/packages/cli/src/builder/progress.rs @@ -12,140 +12,44 @@ use tokio::{io::AsyncBufReadExt, process::Command}; use tracing::Level; impl BuildRequest { - /// Run `cargo`, returning the location of the final exectuable - /// - /// todo: add some stats here, like timing reports, crate-graph optimizations, etc - pub(crate) async fn build_cargo(&self) -> anyhow::Result { - // Extract the unit count of the crate graph so build_cargo has more accurate data - let crate_count = self.get_unit_count_estimate().await; - + pub(crate) fn status_build_diagnostic(&self, message: &Diagnostic) { _ = self.progress.unbounded_send(BuildUpdateProgress { stage: Stage::Compiling, - update: UpdateStage::Start, + update: UpdateStage::AddMessage(message.clone().into()), platform: self.platform(), }); + } - let mut cmd = Command::new("cargo"); - - cmd.arg("rustc") - .envs( - self.custom_target_dir - .as_ref() - .map(|dir| ("CARGO_TARGET_DIR", dir)), - ) - .current_dir(self.krate.crate_dir()) - .arg("--message-format") - .arg("json-diagnostic-rendered-ansi") - .args(&self.build_arguments()) - .arg("--") - .args(self.rust_flags.clone()); - - let mut child = cmd - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .spawn() - .context("Failed to spawn cargo build")?; - - let stdout = child.stdout.take().unwrap(); - let stderr = child.stderr.take().unwrap(); - let stdout = tokio::io::BufReader::new(stdout); - let stderr = tokio::io::BufReader::new(stderr); - let mut output_location = None; - - let mut stdout = stdout.lines(); - let mut stderr = stderr.lines(); - let mut units_compiled = 0; - let mut errors = Vec::new(); - - loop { - let line = tokio::select! { - line = stdout.next_line() => line, - line = stderr.next_line() => line, - }; - - let Some(line) = line? else { - break; - }; - - let mut deserializer = serde_json::Deserializer::from_str(line.trim()); - deserializer.disable_recursion_limit(); - - let message = - Message::deserialize(&mut deserializer).unwrap_or(Message::TextLine(line)); + pub(crate) fn status_build_message(&self, line: String) { + _ = self.progress.unbounded_send(BuildUpdateProgress { + platform: self.platform(), + stage: Stage::Compiling, + update: UpdateStage::AddMessage(BuildMessage { + level: Level::DEBUG, + message: MessageType::Text(line), + source: MessageSource::Build, + }), + }); + } - match message { - Message::CompilerMessage(msg) => { - let message = msg.message; - _ = self.progress.unbounded_send(BuildUpdateProgress { - stage: Stage::Compiling, - update: UpdateStage::AddMessage(message.clone().into()), - platform: self.platform(), - }); - const WARNING_LEVELS: &[cargo_metadata::diagnostic::DiagnosticLevel] = &[ - cargo_metadata::diagnostic::DiagnosticLevel::Help, - cargo_metadata::diagnostic::DiagnosticLevel::Note, - cargo_metadata::diagnostic::DiagnosticLevel::Warning, - cargo_metadata::diagnostic::DiagnosticLevel::Error, - cargo_metadata::diagnostic::DiagnosticLevel::FailureNote, - cargo_metadata::diagnostic::DiagnosticLevel::Ice, - ]; - const FATAL_LEVELS: &[cargo_metadata::diagnostic::DiagnosticLevel] = &[ - cargo_metadata::diagnostic::DiagnosticLevel::Error, - cargo_metadata::diagnostic::DiagnosticLevel::FailureNote, - cargo_metadata::diagnostic::DiagnosticLevel::Ice, - ]; - if WARNING_LEVELS.contains(&message.level) { - if let Some(rendered) = message.rendered { - errors.push(rendered); - } - } - if FATAL_LEVELS.contains(&message.level) { - return Err(anyhow::anyhow!(errors.join("\n"))); - } - } - Message::CompilerArtifact(artifact) => { - units_compiled += 1; - if let Some(executable) = artifact.executable { - output_location = Some(executable.into()); - } else { - let build_progress = units_compiled as f64 / crate_count as f64; - _ = self.progress.unbounded_send(BuildUpdateProgress { - platform: self.platform(), - stage: Stage::Compiling, - update: UpdateStage::SetProgress((build_progress).clamp(0.0, 1.00)), - }); - } - } - Message::BuildScriptExecuted(_) => { - units_compiled += 1; - } - Message::BuildFinished(finished) => { - if !finished.success { - return Err(anyhow::anyhow!("Build failed")); - } - } - Message::TextLine(line) => { - _ = self.progress.unbounded_send(BuildUpdateProgress { - platform: self.platform(), - stage: Stage::Compiling, - update: UpdateStage::AddMessage(BuildMessage { - level: Level::DEBUG, - message: MessageType::Text(line), - source: MessageSource::Build, - }), - }); - } - _ => { - // Unknown message - } - } - } + pub(crate) fn status_build_progress(&self, build_progress: f64) { + _ = self.progress.unbounded_send(BuildUpdateProgress { + platform: self.platform(), + stage: Stage::Compiling, + update: UpdateStage::SetProgress((build_progress).clamp(0.0, 1.00)), + }); + } - output_location.context("Build did not return an executable") + pub(crate) fn status_starting_build(&self) { + _ = self.progress.unbounded_send(BuildUpdateProgress { + stage: Stage::Compiling, + update: UpdateStage::Start, + platform: self.platform(), + }); } /// Try to get the unit graph for the crate. This is a nightly only feature which may not be available with the current version of rustc the user has installed. - async fn get_unit_count(&self) -> Option { + pub(crate) async fn get_unit_count(&self) -> Option { #[derive(Debug, Deserialize)] struct UnitGraph { units: Vec, diff --git a/packages/cli/src/builder/web.rs b/packages/cli/src/builder/web.rs index b550585e16..f881a285ff 100644 --- a/packages/cli/src/builder/web.rs +++ b/packages/cli/src/builder/web.rs @@ -1,6 +1,6 @@ use super::{BuildRequest, Platform}; use crate::builder::progress::{ - BuildMessage, MessageSource, MessageType, Stage, BuildUpdateProgress, UpdateStage, + BuildMessage, BuildUpdateProgress, MessageSource, MessageType, Stage, UpdateStage, }; use crate::error::{Error, Result}; use anyhow::Context; @@ -14,7 +14,11 @@ const DEFAULT_HTML: &str = include_str!("../../assets/index.html"); const TOAST_HTML: &str = include_str!("../../assets/toast.html"); impl BuildRequest { - pub(crate) async fn run_wasm_bindgen(&self, input_path: &Path, bindgen_outdir: &Path) -> Result<()> { + pub(crate) async fn run_wasm_bindgen( + &self, + input_path: &Path, + bindgen_outdir: &Path, + ) -> Result<()> { tracing::info!("Running wasm-bindgen"); let input_path = input_path.to_path_buf(); @@ -93,63 +97,6 @@ impl BuildRequest { Ok(()) } - /// Check if the wasm32-unknown-unknown target is installed and try to install it if not - pub(crate) async fn install_web_build_tooling(&self) -> Result<()> { - // If the user has rustup, we can check if the wasm32-unknown-unknown target is installed - // Otherwise we can just assume it is installed - which is not great... - // Eventually we can poke at the errors and let the user know they need to install the target - if let Ok(wasm_check_command) = Command::new("rustup").args(["show"]).output().await { - let wasm_check_output = String::from_utf8(wasm_check_command.stdout).unwrap(); - if !wasm_check_output.contains("wasm32-unknown-unknown") { - _ = self.progress.unbounded_send(BuildUpdateProgress { - stage: Stage::InstallingWasmTooling, - update: UpdateStage::Start, - platform: self.platform(), - }); - tracing::info!("wasm32-unknown-unknown target not detected, installing.."); - let _ = Command::new("rustup") - .args(["target", "add", "wasm32-unknown-unknown"]) - .output() - .await?; - } - } - - Ok(()) - } - - // Attempt to automatically recover from a bindgen failure by updating the wasm-bindgen version - pub(crate) async fn update_wasm_bindgen_version() -> Result<()> { - let cli_bindgen_version = wasm_bindgen_shared::version(); - tracing::info!("Attempting to recover from bindgen failure by setting the wasm-bindgen version to {cli_bindgen_version}..."); - - let output = Command::new("cargo") - .args([ - "update", - "-p", - "wasm-bindgen", - "--precise", - &cli_bindgen_version, - ]) - .output() - .await; - - let mut error_message = None; - if let Ok(output) = output { - if output.status.success() { - tracing::info!("Successfully updated wasm-bindgen to {cli_bindgen_version}"); - return Ok(()); - } else { - error_message = Some(output); - } - } - - if let Some(output) = error_message { - tracing::error!("Failed to update wasm-bindgen: {:#?}", output); - } - - Err(Error::BuildFailed(format!("WASM bindgen build failed!\nThis is probably due to the Bindgen version, dioxus-cli is using `{cli_bindgen_version}` which is not compatible with your crate.\nPlease reinstall the dioxus cli to fix this issue.\nYou can reinstall the dioxus cli by running `cargo install dioxus-cli --force` and then rebuild your project"))) - } - pub(crate) fn prepare_html(&self) -> Result { let mut html = { let crate_root: &Path = &self.krate.crate_dir(); diff --git a/packages/cli/src/bundler/app.rs b/packages/cli/src/bundler/app.rs index ae54ea6546..cf8f53c8eb 100644 --- a/packages/cli/src/bundler/app.rs +++ b/packages/cli/src/bundler/app.rs @@ -19,7 +19,7 @@ impl AppBundle { executable: PathBuf, ) -> Result { let bundle = Self { - workdir: build.krate.out_dir(), + workdir: build.krate.workdir(build.build.platform()), build, executable, assets, @@ -34,46 +34,6 @@ impl AppBundle { Ok(bundle) } - /// Take the workdir and copy it to the output location, returning the path to final bundle - /// - /// Perform any finishing steps here: - /// - Signing the bundle - pub(crate) async fn finish(&self, destination: PathBuf) -> Result { - // std::fs::create_dir_all(&destination.join(self.build.app_name()))?; - - match self.build.platform() { - // Nothing special to do - just copy the workdir to the output location - Platform::Web => { - let output_location = destination.join(self.build.app_name()); - Ok(output_location) - } - - // Create a final .app/.exe/etc depending on the host platform, not dependent on the host - Platform::Desktop => { - // for now, until we have bundled hotreload, just copy the executable to the output location - // let output_location = destination.join(self.build.app_name()); - Ok(self.executable.clone()) - // Ok(output_location) - } - - Platform::Server => { - std::fs::copy( - self.executable.clone(), - destination.join(self.build.app_name()), - )?; - - Ok(destination.join(self.build.app_name())) - } - Platform::Liveview => Ok(self.executable.clone()), - - // Create a .ipa, only from macOS - Platform::Ios => todo!(), - - // Create a .exe, from linux/mac/windows - Platform::Android => todo!(), - } - } - // Create the workdir and then clean its contents, in case it already exists fn prepare_workdir(&self) -> Result<()> { // _ = std::fs::remove_dir_all(&self.workdir); @@ -134,14 +94,15 @@ impl AppBundle { Ok(()) } - fn bindgen_dir(&self) -> PathBuf { - self.workdir.join("wasm") - } - /// Copy the assets out of the manifest and into the target location /// /// Should be the same on all platforms - just copy over the assets from the manifest into the output directory async fn write_assets(&self) -> Result<()> { + // Server doesn't need assets - web will provide them + if self.build.platform() == Platform::Server { + return Ok(()); + } + let asset_dir = self.asset_dir(); let assets = self.all_source_assets(); @@ -178,6 +139,51 @@ impl AppBundle { Ok(()) } + /// Take the workdir and copy it to the output location, returning the path to final bundle + /// + /// Perform any finishing steps here: + /// - Signing the bundle + pub(crate) async fn finish(&self, destination: PathBuf) -> Result { + // std::fs::create_dir_all(&destination.join(self.build.app_name()))?; + + match self.build.platform() { + // Nothing special to do - just copy the workdir to the output location + Platform::Web => { + std::fs::create_dir_all(&destination.join("web"))?; + crate::fastfs::copy_asset(&self.workdir, &destination.join("web"))?; + Ok(destination.join("web")) + } + + // Create a final .app/.exe/etc depending on the host platform, not dependent on the host + Platform::Desktop => { + // for now, until we have bundled hotreload, just copy the executable to the output location + // let output_location = destination.join(self.build.app_name()); + Ok(self.executable.clone()) + // Ok(output_location) + } + + Platform::Server => { + std::fs::copy( + self.executable.clone(), + destination.join(self.build.app_name()), + )?; + + Ok(destination.join(self.build.app_name())) + } + Platform::Liveview => Ok(self.executable.clone()), + + // Create a .ipa, only from macOS + Platform::Ios => todo!(), + + // Create a .exe, from linux/mac/windows + Platform::Android => todo!(), + } + } + + fn bindgen_dir(&self) -> PathBuf { + self.workdir.join("wasm") + } + pub(crate) fn all_source_assets(&self) -> Vec { // Merge the legacy asset dir assets with the assets from the manifest // Legacy assets need to retain their name in case they're referenced in the manifest diff --git a/packages/cli/src/cli/run.rs b/packages/cli/src/cli/run.rs index ce2bb8a173..d3f678f7e5 100644 --- a/packages/cli/src/cli/run.rs +++ b/packages/cli/src/cli/run.rs @@ -1,10 +1,7 @@ -use crate::{build::TargetArgs, builder::Builder, serve::ServeUpdate}; +use crate::DioxusCrate; +use crate::{builder::Builder, serve::ServeUpdate}; use anyhow::Context; use build::BuildArgs; -use futures_util::{stream::FuturesUnordered, StreamExt}; -use std::{path::Path, process::exit}; - -use crate::DioxusCrate; use super::*; diff --git a/packages/cli/src/cli/serve.rs b/packages/cli/src/cli/serve.rs index 59fe025e12..7d373ea960 100644 --- a/packages/cli/src/cli/serve.rs +++ b/packages/cli/src/cli/serve.rs @@ -115,7 +115,7 @@ impl ServeArgs { std::io::stdout().is_tty() && self.interactive.unwrap_or(true) } - pub(crate) fn should_boot_default_server(&self) -> bool { + pub(crate) fn should_proxy_build(&self) -> bool { match self.build_arguments.platform() { Platform::Server => true, Platform::Liveview => true, diff --git a/packages/cli/src/dioxus_crate.rs b/packages/cli/src/dioxus_crate.rs index c4e07c293f..af4fcc4a2b 100644 --- a/packages/cli/src/dioxus_crate.rs +++ b/packages/cli/src/dioxus_crate.rs @@ -91,7 +91,31 @@ impl DioxusCrate { /// is "distributed" after building an application (configurable in the /// `Dioxus.toml`). pub(crate) fn out_dir(&self) -> PathBuf { - self.workspace_dir().join("target").join("dx-dist") + let dir = self.workspace_dir().join("target").join("dx-dist"); + std::fs::create_dir_all(&dir).unwrap(); + dir + } + + /// Create a workdir for the given platform + /// This can be used as a temporary directory for the build, but in an observable way such that + /// you can see the files in the directory via `target` + pub(crate) fn workdir(&self, platform: Platform) -> PathBuf { + let plat_name = match platform { + Platform::Web => "web", + Platform::Desktop => "desktop", + Platform::Ios => "ios", + Platform::Android => "android", + Platform::Server => "server", + Platform::Liveview => "liveview", + }; + let dir = self + .workspace_dir() + .join("target") + .join("dx-workdir") + .join(self.dioxus_config.application.name.clone()) + .join(plat_name); + std::fs::create_dir_all(&dir).unwrap(); + dir } /// Get the workspace directory for the crate diff --git a/packages/cli/src/main.rs b/packages/cli/src/main.rs index 9657b6eaec..e098684526 100644 --- a/packages/cli/src/main.rs +++ b/packages/cli/src/main.rs @@ -15,6 +15,7 @@ pub(crate) mod fastfs; pub(crate) mod metadata; pub(crate) mod serve; pub(crate) mod settings; +pub(crate) mod tooling; pub(crate) use cli::*; pub(crate) use dioxus_crate::*; diff --git a/packages/cli/src/serve/server.rs b/packages/cli/src/serve/server.rs index 5cbd56f1af..c405db613c 100644 --- a/packages/cli/src/serve/server.rs +++ b/packages/cli/src/serve/server.rs @@ -75,20 +75,20 @@ impl DevServer { let start_browser = args.open.unwrap_or_default(); // If we're serving a fullstack app, we need to find a port to proxy to - let fullstack_port = if args.should_boot_default_server() { + let proxied_port = if args.should_proxy_build() { get_available_port(addr.ip()) } else { None }; - let fullstack_address = fullstack_port.map(|port| SocketAddr::new(addr.ip(), port)); + let proxied_address = proxied_port.map(|port| SocketAddr::new(addr.ip(), port)); let router = Self::setup_router( args, cfg, hot_reload_sockets_tx, build_status_sockets_tx, - fullstack_address, + proxied_address, build_status.clone(), ) .unwrap(); @@ -100,6 +100,7 @@ impl DevServer { let listener = std::net::TcpListener::bind(addr).expect("Failed to bind port"); _ = listener.set_nonblocking(true); + let addr = listener.local_addr().unwrap(); let _server_task = tokio::spawn(async move { @@ -139,7 +140,7 @@ impl DevServer { new_build_status_sockets: build_status_sockets_rx, _server_task, ip: addr, - fullstack_port, + fullstack_port: proxied_port, build_status, application_name: cfg.dioxus_config.application.name.clone(), @@ -327,36 +328,16 @@ impl DevServer { build_status: SharedStatus, ) -> Result { let mut router = Router::new(); - let platform = args.build_arguments.platform(); // Setup proxy for the endpoint specified in the config for proxy_config in krate.dioxus_config.web.proxy.iter() { router = super::proxy::add_proxy(router, proxy_config)?; } - // server the dir if it's web, otherwise let the fullstack server itself handle it - match platform { - Platform::Web => { - // Route file service to output the .wasm and assets if this is a web build - let base_path = format!( - "/{}", - krate - .dioxus_config - .web - .app - .base_path - .as_deref() - .unwrap_or_default() - .trim_matches('/') - ); - - router = router.nest_service(&base_path, build_serve_dir(args, krate)); - } - Platform::Liveview | Platform::Server => { - // For fullstack and static generation, forward all requests to the server - let address = fullstack_address.unwrap(); - - router = router.nest_service("/",super::proxy::proxy_to( + if args.should_proxy_build() { + // For fullstack, liveview, and server, forward all requests to the inner server + let address = fullstack_address.unwrap(); + router = router.nest_service("/",super::proxy::proxy_to( format!("http://{address}").parse().unwrap(), true, |error| { @@ -369,8 +350,22 @@ impl DevServer { .unwrap() }, )); - } - _ => {} + } else { + // Otherwise, just serve the dir ourselves + // Route file service to output the .wasm and assets if this is a web build + let base_path = format!( + "/{}", + krate + .dioxus_config + .web + .app + .base_path + .as_deref() + .unwrap_or_default() + .trim_matches('/') + ); + + router = router.nest_service(&base_path, build_serve_dir(args, krate)); } // Setup middleware to intercept html requests if the build status is "Building" @@ -381,27 +376,27 @@ impl DevServer { // Setup websocket endpoint - and pass in the extension layer immediately after router = router.nest( - "/_dioxus", - Router::new() - .route( - "/", - get( - |ws: WebSocketUpgrade, ext: Extension>| async move { - ws.on_upgrade(move |socket| async move { _ = ext.0.unbounded_send(socket) }) - }, - ), - ) - .layer(Extension(hot_reload_sockets)) - .route( - "/build_status", - get( - |ws: WebSocketUpgrade, ext: Extension>| async move { - ws.on_upgrade(move |socket| async move { _ = ext.0.unbounded_send(socket) }) - }, - ), - ) - .layer(Extension(build_status_sockets)), - ); + "/_dioxus", + Router::new() + .route( + "/", + get( + |ws: WebSocketUpgrade, ext: Extension>| async move { + ws.on_upgrade(move |socket| async move { _ = ext.0.unbounded_send(socket) }) + }, + ), + ) + .layer(Extension(hot_reload_sockets)) + .route( + "/build_status", + get( + |ws: WebSocketUpgrade, ext: Extension>| async move { + ws.on_upgrade(move |socket| async move { _ = ext.0.unbounded_send(socket) }) + }, + ), + ) + .layer(Extension(build_status_sockets)), + ); // Setup cors router = router.layer( @@ -433,7 +428,7 @@ fn build_serve_dir(args: &ServeArgs, cfg: &DioxusCrate) -> axum::routing::Method false => CORS_UNSAFE.clone(), }; - let out_dir = cfg.out_dir(); + let out_dir = cfg.workdir(Platform::Web); let index_on_404 = cfg.dioxus_config.web.watcher.index_on_404; get_service( diff --git a/packages/cli/src/serve/watcher.rs b/packages/cli/src/serve/watcher.rs index badc3b1e42..59810ace89 100644 --- a/packages/cli/src/serve/watcher.rs +++ b/packages/cli/src/serve/watcher.rs @@ -323,7 +323,7 @@ fn is_backup_file(path: PathBuf) -> bool { /// Tests if the provided [`notify::Event`] is something we listen to so we can avoid unescessary hot reloads. fn is_allowed_notify_event(event: ¬ify::Event) -> bool { - match event.kind { + let allowed = match event.kind { EventKind::Modify(ModifyKind::Data(_)) => true, EventKind::Modify(ModifyKind::Name(_)) => true, EventKind::Create(_) => true, @@ -334,7 +334,11 @@ fn is_allowed_notify_event(event: ¬ify::Event) -> bool { EventKind::Modify(ModifyKind::Any) => true, // Don't care about anything else. _ => false, - } + }; + + tracing::info!("is_allowed_notify_event: {allowed:?} for {event:#?}"); + + allowed } #[test] diff --git a/packages/cli/src/tooling/android.rs b/packages/cli/src/tooling/android.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/cli/src/tooling/ios.rs b/packages/cli/src/tooling/ios.rs new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/packages/cli/src/tooling/ios.rs @@ -0,0 +1 @@ + diff --git a/packages/cli/src/tooling/mac.rs b/packages/cli/src/tooling/mac.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/cli/src/tooling/mod.rs b/packages/cli/src/tooling/mod.rs new file mode 100644 index 0000000000..1bf3888d05 --- /dev/null +++ b/packages/cli/src/tooling/mod.rs @@ -0,0 +1,5 @@ +mod android; +mod ios; +mod mac; +mod web; +mod wsl; diff --git a/packages/cli/src/tooling/web.rs b/packages/cli/src/tooling/web.rs new file mode 100644 index 0000000000..6d3004304e --- /dev/null +++ b/packages/cli/src/tooling/web.rs @@ -0,0 +1,63 @@ +use crate::{Error, Result}; +use tokio::process::Command; + +pub struct ToolingProvider {} + +impl ToolingProvider { + /// Check if the wasm32-unknown-unknown target is installed and try to install it if not + pub(crate) async fn install_web_build_tooling(&self) -> Result<()> { + // If the user has rustup, we can check if the wasm32-unknown-unknown target is installed + // Otherwise we can just assume it is installed - which is not great... + // Eventually we can poke at the errors and let the user know they need to install the target + if let Ok(wasm_check_command) = Command::new("rustup").args(["show"]).output().await { + let wasm_check_output = String::from_utf8(wasm_check_command.stdout).unwrap(); + if !wasm_check_output.contains("wasm32-unknown-unknown") { + // _ = self.progress.unbounded_send(BuildUpdateProgress { + // stage: Stage::InstallingWasmTooling, + // update: UpdateStage::Start, + // platform: self.platform(), + // }); + tracing::info!("wasm32-unknown-unknown target not detected, installing.."); + let _ = Command::new("rustup") + .args(["target", "add", "wasm32-unknown-unknown"]) + .output() + .await?; + } + } + + Ok(()) + } + + // Attempt to automatically recover from a bindgen failure by updating the wasm-bindgen version + pub(crate) async fn update_wasm_bindgen_version() -> Result<()> { + let cli_bindgen_version = wasm_bindgen_shared::version(); + tracing::info!("Attempting to recover from bindgen failure by setting the wasm-bindgen version to {cli_bindgen_version}..."); + + let output = Command::new("cargo") + .args([ + "update", + "-p", + "wasm-bindgen", + "--precise", + &cli_bindgen_version, + ]) + .output() + .await; + + let mut error_message = None; + if let Ok(output) = output { + if output.status.success() { + tracing::info!("Successfully updated wasm-bindgen to {cli_bindgen_version}"); + return Ok(()); + } else { + error_message = Some(output); + } + } + + if let Some(output) = error_message { + tracing::error!("Failed to update wasm-bindgen: {:#?}", output); + } + + Err(Error::BuildFailed(format!("WASM bindgen build failed!\nThis is probably due to the Bindgen version, dioxus-cli is using `{cli_bindgen_version}` which is not compatible with your crate.\nPlease reinstall the dioxus cli to fix this issue.\nYou can reinstall the dioxus cli by running `cargo install dioxus-cli --force` and then rebuild your project"))) + } +} diff --git a/packages/cli/src/tooling/wsl.rs b/packages/cli/src/tooling/wsl.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/core/src/runtime.rs b/packages/core/src/runtime.rs index de9877e0ff..9c7d2cc98b 100644 --- a/packages/core/src/runtime.rs +++ b/packages/core/src/runtime.rs @@ -107,6 +107,13 @@ impl Runtime { } } + /// Mark a scope as dirty. This will cause it to re-render. + pub fn mark_dirty(&self, id: ScopeId) { + self.sender + .unbounded_send(SchedulerMsg::Immediate(id)) + .unwrap(); + } + /// Create a scope context. This slab is synchronized with the scope slab. pub(crate) fn create_scope(&self, context: Scope) { let id = context.id; diff --git a/packages/desktop/headless_tests/utils.rs b/packages/desktop/headless_tests/utils.rs index 9367bd4236..6e7e73d1f7 100644 --- a/packages/desktop/headless_tests/utils.rs +++ b/packages/desktop/headless_tests/utils.rs @@ -16,7 +16,7 @@ pub fn check_app_exits(app: fn() -> Element) { } }); - LaunchBuilder::desktop() + LaunchBuilder::new() .with_cfg(Config::new().with_window(WindowBuilder::new().with_visible(false))) .launch(app); diff --git a/packages/desktop/src/document.rs b/packages/desktop/src/document.rs index 68f993d174..38c6c4e64e 100644 --- a/packages/desktop/src/document.rs +++ b/packages/desktop/src/document.rs @@ -61,4 +61,24 @@ impl Document for DesktopService { "#, )); } + + fn current_route(&self) -> String { + todo!() + } + + fn go_back(&self) { + todo!() + } + + fn go_forward(&self) { + todo!() + } + + fn push_route(&self, route: String) { + todo!() + } + + fn replace_route(&self, path: String) { + todo!() + } } diff --git a/packages/dioxus/Cargo.toml b/packages/dioxus/Cargo.toml index ae829d5645..11e4592740 100644 --- a/packages/dioxus/Cargo.toml +++ b/packages/dioxus/Cargo.toml @@ -23,7 +23,7 @@ dioxus-router = { workspace = true, optional = true } dioxus-web = { workspace = true, default-features = false, optional = true } dioxus-mobile = { workspace = true, optional = true } dioxus-desktop = { workspace = true, default-features = true, optional = true } -dioxus-fullstack = { workspace = true, default-features = true, optional = true } +dioxus-fullstack = { workspace = true, default-features = true, optional = true, features = ["base"] } dioxus-static-site-generation = { workspace = true, optional = true } dioxus-liveview = { workspace = true, optional = true } dioxus-ssr = { workspace = true, optional = true } @@ -61,37 +61,40 @@ asset = ["dep:manganis"] document = ["dioxus-web?/document"] launch = ["dep:dioxus-config-macro"] -router = ["dep:dioxus-router"] +router = [ + "dep:dioxus-router" +] # Platforms fullstack = [ "dep:dioxus-fullstack", "dioxus-config-macro/fullstack", "dep:serde", - "dioxus-router?/fullstack", + # "dioxus-router?/fullstack", ] desktop = [ "dep:dioxus-desktop", - "dioxus-fullstack?/desktop", + # "dioxus-fullstack?/desktop", "dioxus-config-macro/desktop", ] mobile = [ "dep:dioxus-mobile", - "dioxus-fullstack?/mobile", + # "dioxus-fullstack?/mobile", "dioxus-config-macro/mobile", ] web = [ "dep:dioxus-web", - "dioxus-fullstack?/web", + # "dioxus-fullstack?/web", "dioxus-static-site-generation?/web", "dioxus-config-macro/web", - "dioxus-router?/web", + # "dioxus-router?/web", ] -ssr = ["dep:dioxus-ssr", "dioxus-router?/ssr", "dioxus-config-macro/ssr"] +ssr = ["dep:dioxus-ssr", "dioxus-config-macro/ssr"] +# ssr = ["dep:dioxus-ssr", "dioxus-router?/ssr", "dioxus-config-macro/ssr"] liveview = [ "dep:dioxus-liveview", "dioxus-config-macro/liveview", - "dioxus-router?/liveview", + # "dioxus-router?/liveview", ] static-generation = [ "dep:dioxus-static-site-generation", diff --git a/packages/dioxus/src/launch.rs b/packages/dioxus/src/launch.rs index 77f47929a5..e882f6ce8a 100644 --- a/packages/dioxus/src/launch.rs +++ b/packages/dioxus/src/launch.rs @@ -180,15 +180,22 @@ impl LaunchBuilder { // #[cfg(any(feature = "static-generation", feature = "web"))] /// Launch your application. pub fn launch(self, app: fn() -> Element) { - #[cfg(feature = "fullstack")] + #[cfg(feature = "web")] { - dioxus_fullstack::launch::launch(app, Default::default(), Default::default()); + dioxus_web::launch::launch(app, Default::default(), Default::default()); } - #[cfg(feature = "web")] + #[cfg(feature = "liveview")] { - dioxus_web::launch::launch(app, Default::default(), Default::default()); + dioxus_liveview::launch::launch(app, Default::default(), Default::default()); } + + #[cfg(feature = "fullstack")] + { + dioxus_fullstack::launch::launch(app, Default::default(), Default::default()); + } + + panic!("No platform feature enabled. Please enable one of the following features: liveview, desktop, mobile, web, fullstack to use the launch API.") } } diff --git a/packages/document/src/document.rs b/packages/document/src/document.rs index ab45381762..fcc2dd2cdf 100644 --- a/packages/document/src/document.rs +++ b/packages/document/src/document.rs @@ -1,7 +1,24 @@ +use std::sync::Arc; + use super::*; -/// A provider for document-related functionality. By default most methods are driven through [`eval`]. -pub trait Document { +/// A context for the document +pub type DocumentContext = Arc; + +/// A provider for document-related functionality. +/// +/// Provides things like a history API, a title, a way to run JS, and some other basics/essentials used +/// by nearly every platform. +/// +/// An integration with some kind of navigation history. +/// +/// Depending on your use case, your implementation may deviate from the described procedure. This +/// is fine, as long as both `current_route` and `current_query` match the described format. +/// +/// However, you should document all deviations. Also, make sure the navigation is user-friendly. +/// The described behaviors are designed to mimic a web browser, which most users should already +/// know. Deviations might confuse them. +pub trait Document: 'static { /// Get a reference to the document as `dyn Any` fn as_any(&self) -> &dyn std::any::Any; @@ -38,4 +55,243 @@ pub trait Document { fn create_link(&self, props: LinkProps) { self.create_head_element("link", props.attributes(), None); } + + /// Get the path of the current URL. + /// + /// **Must start** with `/`. **Must _not_ contain** the prefix. + /// + /// ```rust + /// # use dioxus_router::prelude::*; + /// # use dioxus::prelude::*; + /// # #[component] + /// # fn Index() -> Element { VNode::empty() } + /// # #[component] + /// # fn OtherPage() -> Element { VNode::empty() } + /// #[derive(Clone, Routable, Debug, PartialEq)] + /// enum Route { + /// #[route("/")] + /// Index {}, + /// #[route("/some-other-page")] + /// OtherPage {}, + /// } + /// let mut history = MemoryHistory::::default(); + /// assert_eq!(history.current_route().to_string(), "/"); + /// + /// history.push(Route::OtherPage {}); + /// assert_eq!(history.current_route().to_string(), "/some-other-page"); + /// ``` + #[must_use] + fn current_route(&self) -> String; + + /// Get the current path prefix of the URL. + /// + /// Not all [`HistoryProvider`]s need a prefix feature. It is meant for environments where a + /// dioxus-router-core-routed application is not running on `/`. The [`HistoryProvider`] is responsible + /// for removing the prefix from the dioxus-router-core-internal path, and also for adding it back in + /// during navigation. This functions value is only used for creating `href`s (e.g. for SSR or + /// display (but not navigation) in a web app). + fn base_route(&self) -> Option { + None + } + + /// Check whether there is a previous page to navigate back to. + /// + /// If a [`HistoryProvider`] cannot know this, it should return [`true`]. + /// + /// ```rust + /// # use dioxus_router::prelude::*; + /// # use dioxus::prelude::*; + /// # #[component] + /// # fn Index() -> Element { VNode::empty() } + /// # fn Other() -> Element { VNode::empty() } + /// #[derive(Clone, Routable, Debug, PartialEq)] + /// enum Route { + /// #[route("/")] + /// Index {}, + /// #[route("/other")] + /// Other {}, + /// } + /// let mut history = MemoryHistory::::default(); + /// assert_eq!(history.can_go_back(), false); + /// + /// history.push(Route::Other {}); + /// assert_eq!(history.can_go_back(), true); + /// ``` + #[must_use] + fn can_go_back(&self) -> bool { + true + } + + /// Go back to a previous page. + /// + /// If a [`HistoryProvider`] cannot go to a previous page, it should do nothing. This method + /// might be called, even if `can_go_back` returns [`false`]. + /// + /// ```rust + /// # use dioxus_router::prelude::*; + /// # use dioxus::prelude::*; + /// # #[component] + /// # fn Index() -> Element { VNode::empty() } + /// # #[component] + /// # fn OtherPage() -> Element { VNode::empty() } + /// #[derive(Clone, Routable, Debug, PartialEq)] + /// enum Route { + /// #[route("/")] + /// Index {}, + /// #[route("/some-other-page")] + /// OtherPage {}, + /// } + /// let mut history = MemoryHistory::::default(); + /// assert_eq!(history.current_route().to_string(), "/"); + /// + /// history.go_back(); + /// assert_eq!(history.current_route().to_string(), "/"); + /// + /// history.push(Route::OtherPage {}); + /// assert_eq!(history.current_route().to_string(), "/some-other-page"); + /// + /// history.go_back(); + /// assert_eq!(history.current_route().to_string(), "/"); + /// ``` + fn go_back(&self); + + /// Check whether there is a future page to navigate forward to. + /// + /// If a [`HistoryProvider`] cannot know this, it should return [`true`]. + /// + /// ```rust + /// # use dioxus_router::prelude::*; + /// # use dioxus::prelude::*; + /// # #[component] + /// # fn Index() -> Element { VNode::empty() } + /// # #[component] + /// # fn OtherPage() -> Element { VNode::empty() } + /// #[derive(Clone, Routable, Debug, PartialEq)] + /// enum Route { + /// #[route("/")] + /// Index {}, + /// #[route("/some-other-page")] + /// OtherPage {}, + /// } + /// let mut history = MemoryHistory::::default(); + /// assert_eq!(history.can_go_forward(), false); + /// + /// history.push(Route::OtherPage {}); + /// assert_eq!(history.can_go_forward(), false); + /// + /// history.go_back(); + /// assert_eq!(history.can_go_forward(), true); + /// ``` + #[must_use] + fn can_go_forward(&self) -> bool { + true + } + + /// Go forward to a future page. + /// + /// If a [`HistoryProvider`] cannot go to a previous page, it should do nothing. This method + /// might be called, even if `can_go_forward` returns [`false`]. + /// + /// ```rust + /// # use dioxus_router::prelude::*; + /// # use dioxus::prelude::*; + /// # #[component] + /// # fn Index() -> Element { VNode::empty() } + /// # #[component] + /// # fn OtherPage() -> Element { VNode::empty() } + /// #[derive(Clone, Routable, Debug, PartialEq)] + /// enum Route { + /// #[route("/")] + /// Index {}, + /// #[route("/some-other-page")] + /// OtherPage {}, + /// } + /// let mut history = MemoryHistory::::default(); + /// history.push(Route::OtherPage {}); + /// assert_eq!(history.current_route(), Route::OtherPage {}); + /// + /// history.go_back(); + /// assert_eq!(history.current_route(), Route::Index {}); + /// + /// history.go_forward(); + /// assert_eq!(history.current_route(), Route::OtherPage {}); + /// ``` + fn go_forward(&self); + + /// Go to another page. + /// + /// This should do three things: + /// 1. Merge the current URL with the `path` parameter (which may also include a query part). + /// 2. Remove the previous URL to the navigation history. + /// 3. Clear the navigation future. + /// + /// ```rust + /// # use dioxus_router::prelude::*; + /// # use dioxus::prelude::*; + /// # #[component] + /// # fn Index() -> Element { VNode::empty() } + /// # #[component] + /// # fn OtherPage() -> Element { VNode::empty() } + /// #[derive(Clone, Routable, Debug, PartialEq)] + /// enum Route { + /// #[route("/")] + /// Index {}, + /// #[route("/some-other-page")] + /// OtherPage {}, + /// } + /// let mut history = MemoryHistory::::default(); + /// assert_eq!(history.current_route(), Route::Index {}); + /// + /// history.push(Route::OtherPage {}); + /// assert_eq!(history.current_route(), Route::OtherPage {}); + /// assert!(history.can_go_back()); + /// ``` + fn push_route(&self, route: String); + + /// Replace the current page with another one. + /// + /// This should merge the current URL with the `path` parameter (which may also include a query + /// part). In contrast to the `push` function, the navigation history and future should stay + /// untouched. + /// + /// ```rust + /// # use dioxus_router::prelude::*; + /// # use dioxus::prelude::*; + /// # #[component] + /// # fn Index() -> Element { VNode::empty() } + /// # #[component] + /// # fn OtherPage() -> Element { VNode::empty() } + /// #[derive(Clone, Routable, Debug, PartialEq)] + /// enum Route { + /// #[route("/")] + /// Index {}, + /// #[route("/some-other-page")] + /// OtherPage {}, + /// } + /// let mut history = MemoryHistory::::default(); + /// assert_eq!(history.current_route(), Route::Index {}); + /// + /// history.replace(Route::OtherPage {}); + /// assert_eq!(history.current_route(), Route::OtherPage {}); + /// assert!(!history.can_go_back()); + /// ``` + fn replace_route(&self, path: String); + + /// Navigate to an external URL. + /// + /// This should navigate to an external URL, which isn't controlled by the router. If a + /// [`HistoryProvider`] cannot do that, it should return [`false`], otherwise [`true`]. + /// + /// Returning [`false`] will cause the router to handle the external navigation failure. + #[allow(unused_variables)] + fn navigate_external(&self, url: String) -> bool { + false + } + + /// Provide the [`HistoryProvider`] with an update callback. + /// + /// Some [`HistoryProvider`]s may receive URL updates from outside the router. When such + /// updates are received, they should call `callback`, which will cause the router to update. + #[allow(unused_variables)] + fn updater(&self, callback: Arc) {} } diff --git a/packages/fullstack/Cargo.toml b/packages/fullstack/Cargo.toml index 722efb1094..5bee1386e1 100644 --- a/packages/fullstack/Cargo.toml +++ b/packages/fullstack/Cargo.toml @@ -12,7 +12,11 @@ resolver = "2" [dependencies] # server functions -server_fn = { version = "0.6.5", features = ["json", "url", "browser"], default-features = false } +server_fn = { version = "0.6.5", features = [ + "json", + "url", + "browser", +], default-features = false } dioxus_server_macro = { workspace = true } # axum @@ -30,7 +34,9 @@ hyper = { workspace = true, optional = true } http = { workspace = true, optional = true } # Web Integration -dioxus-web = { workspace = true, features = ["hydrate"], default-features = false, optional = true } +dioxus-web = { workspace = true, features = [ + "hydrate", +], default-features = false, optional = true } dioxus-interpreter-js = { workspace = true, optional = true } # Desktop Integration @@ -62,7 +68,14 @@ bytes = "1.4.0" tower = { workspace = true, features = ["util"], optional = true } tower-layer = { version = "0.3.2", optional = true } parking_lot = { version = "0.12.1", features = ["send_guard"], optional = true } -web-sys = { version = "0.3.61", optional = true, features = ["Window", "Document", "Element", "HtmlDocument", "Storage", "console"] } +web-sys = { version = "0.3.61", optional = true, features = [ + "Window", + "Document", + "Element", + "HtmlDocument", + "Storage", + "console", +] } aws-lc-rs = { version = "1.8.1", optional = true } dioxus-devtools = { workspace = true, optional = true } @@ -70,23 +83,35 @@ dioxus-devtools = { workspace = true, optional = true } tokio = { workspace = true, features = ["rt", "sync"], optional = true } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] -tokio = { workspace = true, features = ["rt", "sync", "rt-multi-thread"], optional = true } +tokio = { workspace = true, features = [ + "rt", + "sync", + "rt-multi-thread", +], optional = true } [dev-dependencies] dioxus = { workspace = true, features = ["fullstack"] } [features] -default = ["panic_hook", "document", "mounted", "devtools"] +default = ["base"] +base = ["panic_hook", "document", "mounted", "devtools"] panic_hook = ["dioxus-web?/panic_hook"] mounted = ["dioxus-web?/mounted"] devtools = ["dep:dioxus-devtools", "dioxus-web?/devtools"] document = ["dioxus-web?/document"] -web = ["dep:dioxus-web", "dep:web-sys"] -desktop = ["dep:dioxus-desktop", "server_fn/reqwest", "dioxus_server_macro/reqwest", "server_fn/browser"] -mobile = ["dep:dioxus-mobile"] +# web = ["dep:dioxus-web", "dep:web-sys"] +# desktop = ["dep:dioxus-desktop", "server_fn/reqwest", "dioxus_server_macro/reqwest", "server_fn/browser"] +# mobile = ["dep:dioxus-mobile"] default-tls = ["server_fn/default-tls"] rustls = ["server_fn/rustls", "dep:rustls", "dep:hyper-rustls"] -axum = ["dep:axum", "dep:tower-http", "server", "server_fn/axum", "dioxus_server_macro/axum", "default-tls"] +axum = [ + "dep:axum", + "dep:tower-http", + "server", + "server_fn/axum", + "dioxus_server_macro/axum", + "default-tls", +] server = [ "server_fn/ssr", "dioxus_server_macro/server", diff --git a/packages/fullstack/src/document/mod.rs b/packages/fullstack/src/document/mod.rs index 0c380994f6..49b332628a 100644 --- a/packages/fullstack/src/document/mod.rs +++ b/packages/fullstack/src/document/mod.rs @@ -2,7 +2,9 @@ #[cfg(feature = "server")] pub(crate) mod server; + #[cfg(feature = "server")] pub use server::ServerDocument; + #[cfg(all(feature = "web", feature = "document"))] pub(crate) mod web; diff --git a/packages/fullstack/src/document/web.rs b/packages/fullstack/src/document/web.rs index fe87b724f7..28726284fd 100644 --- a/packages/fullstack/src/document/web.rs +++ b/packages/fullstack/src/document/web.rs @@ -48,4 +48,24 @@ impl Document for FullstackWebDocument { fn as_any(&self) -> &dyn std::any::Any { self } + + fn current_route(&self) -> String { + todo!() + } + + fn go_back(&mut self) { + todo!() + } + + fn go_forward(&mut self) { + todo!() + } + + fn push_route(&mut self, route: String) { + todo!() + } + + fn replace(&mut self, path: String) { + todo!() + } } diff --git a/packages/fullstack/src/html_storage/serialize.rs b/packages/fullstack/src/html_storage/serialize.rs index 535c368285..1c2528448b 100644 --- a/packages/fullstack/src/html_storage/serialize.rs +++ b/packages/fullstack/src/html_storage/serialize.rs @@ -1,3 +1,5 @@ +use base64::engine::general_purpose::STANDARD; +use base64::Engine; use dioxus_lib::prelude::dioxus_core::DynamicNode; use dioxus_lib::prelude::{ has_context, try_consume_context, ErrorContext, ScopeId, SuspenseBoundaryProps, @@ -5,9 +7,6 @@ use dioxus_lib::prelude::{ }; use serde::Serialize; -use base64::engine::general_purpose::STANDARD; -use base64::Engine; - use super::SerializeContext; #[allow(unused)] diff --git a/packages/fullstack/src/serve_config.rs b/packages/fullstack/src/serve_config.rs index 3f1482b95f..8bdf2093c3 100644 --- a/packages/fullstack/src/serve_config.rs +++ b/packages/fullstack/src/serve_config.rs @@ -77,12 +77,12 @@ impl ServeConfigBuilder { /// Get the path to the public assets directory to serve static files from pub(crate) fn public_path() -> PathBuf { - // The CLI always bundles static assets into the exe/public directory + // The CLI always bundles static assets into the exe/web directory std::env::current_exe() .expect("Failed to get current executable path") .parent() .unwrap() - .join("public") + .join("web") } /// An error that can occur when loading the index.html file diff --git a/packages/lazy-js-bundle/src/lib.rs b/packages/lazy-js-bundle/src/lib.rs index 36dae8da44..05ff0f8ba4 100644 --- a/packages/lazy-js-bundle/src/lib.rs +++ b/packages/lazy-js-bundle/src/lib.rs @@ -80,31 +80,38 @@ impl LazyTypeScriptBindings { let hashes = hash_files(watching_paths); // Try to find a common prefix for the output files and put the hash in there otherwise, write it to src/binding_hash.txt - let mut hash_location: Option = None; - for path in &self.binding { - match hash_location { - Some(current_hash_location) => { - let mut common_path = PathBuf::new(); - for component in path - .output_path - .components() - .zip(current_hash_location.components()) - { - if component.0 != component.1 { - break; - } - common_path.push(component.0); - } - hash_location = - (common_path.components().next().is_some()).then_some(common_path); - } - None => { - hash_location = Some(path.output_path.clone()); - } - }; - } - let hash_location = hash_location.unwrap_or_else(|| PathBuf::from("./src/js")); - std::fs::create_dir_all(&hash_location).unwrap(); + // let mut hash_location: Option = None; + // for path in &self.binding { + // match hash_location { + // Some(current_hash_location) => { + // let mut common_path = PathBuf::new(); + // for component in path + // .output_path + // .components() + // .zip(current_hash_location.components()) + // { + // if component.0 != component.1 { + // break; + // } + // common_path.push(component.0); + // } + // hash_location = + // (common_path.components().next().is_some()).then_some(common_path); + // } + // None => { + // hash_location = Some(path.output_path.clone()); + // } + // }; + // } + let hash_location = None; + let hash_location = hash_location.unwrap_or_else(|| PathBuf::from("./src/js/")); + std::fs::create_dir_all(&hash_location).unwrap_or_else(|err| { + panic!( + "Failed to create directory for hash file: {} in {}", + err, + hash_location.display() + ) + }); let hash_location = hash_location.join("hash.txt"); // If the hash matches the one on disk, we're good and don't need to update bindings diff --git a/packages/liveview/Cargo.toml b/packages/liveview/Cargo.toml index 6ed232269c..d4d52dc62d 100644 --- a/packages/liveview/Cargo.toml +++ b/packages/liveview/Cargo.toml @@ -43,8 +43,11 @@ axum = { workspace = true, features = ["ws"] } tower = { workspace = true } dioxus = { workspace = true } +[build-dependencies] +lazy-js-bundle = { workspace = true } + [features] -default = ["devtools", "multi-thread"] +default = ["devtools", "multi-thread", "axum"] axum = ["dep:axum"] multi-thread = ["tokio/rt-multi-thread"] devtools = ["dep:dioxus-devtools"] diff --git a/packages/liveview/build.rs b/packages/liveview/build.rs new file mode 100644 index 0000000000..d218f25627 --- /dev/null +++ b/packages/liveview/build.rs @@ -0,0 +1,7 @@ +fn main() { + // If any TS files change, re-run the build script + lazy_js_bundle::LazyTypeScriptBindings::new() + .with_watching("./src/ts/") + .with_binding("./src/ts/main.ts", "./src/js/main.js") + .run(); +} diff --git a/packages/liveview/src/config.rs b/packages/liveview/src/config.rs index 94f6f119ac..178b689379 100644 --- a/packages/liveview/src/config.rs +++ b/packages/liveview/src/config.rs @@ -56,7 +56,6 @@ impl Config { /// Launch the LiveView server. pub async fn launch(self) { - println!("Listening on http://{}", self.address); self.router.start(self.address).await } } diff --git a/packages/liveview/src/eval.rs b/packages/liveview/src/document.rs similarity index 77% rename from packages/liveview/src/eval.rs rename to packages/liveview/src/document.rs index 4b92e4381f..b198456b35 100644 --- a/packages/liveview/src/eval.rs +++ b/packages/liveview/src/document.rs @@ -37,4 +37,24 @@ impl Document for LiveviewDocument { ) { todo!() } + + fn current_route(&self) -> String { + todo!() + } + + fn go_back(&self) { + todo!() + } + + fn go_forward(&self) { + todo!() + } + + fn push_route(&self, route: String) { + todo!() + } + + fn replace_route(&self, path: String) { + todo!() + } } diff --git a/packages/router/src/history/liveview.rs b/packages/liveview/src/history.rs similarity index 100% rename from packages/router/src/history/liveview.rs rename to packages/liveview/src/history.rs diff --git a/packages/liveview/src/js/hash.txt b/packages/liveview/src/js/hash.txt new file mode 100644 index 0000000000..cf43478351 --- /dev/null +++ b/packages/liveview/src/js/hash.txt @@ -0,0 +1 @@ +[9100446950368490988] \ No newline at end of file diff --git a/packages/liveview/src/js/main.js b/packages/liveview/src/js/main.js new file mode 100644 index 0000000000..6bd9d2ed54 --- /dev/null +++ b/packages/liveview/src/js/main.js @@ -0,0 +1 @@ +function main(addr){let root=window.document.getElementById("main");if(root!=null)window.ipc=new IPC(root,addr)}class IPC{ws;constructor(root,addr){window.interpreter.initialize(root),window.interpreter.liveview=!0,window.interpreter.ipc=this;const ws=new WebSocket(addr);ws.binaryType="arraybuffer";function ping(){ws.send("__ping__")}ws.onopen=()=>{setInterval(ping,30000),ws.send(window.interpreter.serializeIpcMessage("initialize"))},ws.onerror=(err)=>{},ws.onmessage=(message)=>{const binaryFrame=new Uint8Array(message.data)[0]==1,messageData=message.data.slice(1);if(binaryFrame)window.interpreter.run_from_bytes(messageData);else{let str=new TextDecoder("utf-8").decode(messageData);if(str!="__pong__"){const event=JSON.parse(str);switch(event.type){case"query":Function("Eval",`"use strict";${event.data};`)();break}}}},this.ws=ws}postMessage(msg){this.ws.send(msg)}}export{main}; diff --git a/packages/liveview/src/lib.rs b/packages/liveview/src/lib.rs index ebe0c03578..84efd00a43 100644 --- a/packages/liveview/src/lib.rs +++ b/packages/liveview/src/lib.rs @@ -3,19 +3,20 @@ #![doc(html_favicon_url = "https://avatars.githubusercontent.com/u/79236386")] mod adapters; -#[allow(unused_imports)] -pub use adapters::*; - +mod config; +mod document; mod element; +mod events; pub mod pool; mod query; + +#[allow(unused_imports)] +pub use adapters::*; +pub use config::*; use dioxus_interpreter_js::NATIVE_JS; use futures_util::{SinkExt, StreamExt}; pub use pool::*; -mod config; -mod eval; -mod events; -pub use config::*; + #[cfg(feature = "axum")] pub mod launch; @@ -90,7 +91,7 @@ fn handle_edits_code() -> String { .unwrap_or_else(|| interpreter.len()); interpreter.replace_range(import_start..import_end, ""); } - let main_js = include_str!("./main.js"); + let main_js = include_str!("./js/main.js"); let js = format!("{interpreter}\n{main_js}"); js } diff --git a/packages/liveview/src/pool.rs b/packages/liveview/src/pool.rs index f7b5f3097c..8bc01b0252 100644 --- a/packages/liveview/src/pool.rs +++ b/packages/liveview/src/pool.rs @@ -1,6 +1,6 @@ use crate::{ + document::init_eval, element::LiveviewElement, - eval::init_eval, events::SerializedHtmlEventConverter, query::{QueryEngine, QueryResult}, LiveViewError, @@ -235,9 +235,7 @@ pub async fn run(mut vdom: VirtualDom, ws: impl LiveViewSocket) -> Result<(), Li } } - // wait for suspense to resolve in a 10ms window tokio::select! { - _ = tokio::time::sleep(Duration::from_millis(10)) => {} _ = vdom.wait_for_suspense() => {} } diff --git a/packages/liveview/src/main.js b/packages/liveview/src/ts/main.ts similarity index 78% rename from packages/liveview/src/main.js rename to packages/liveview/src/ts/main.ts index e8f5c58338..f67282ad6e 100644 --- a/packages/liveview/src/main.js +++ b/packages/liveview/src/ts/main.ts @@ -1,19 +1,23 @@ -const intercept_link_redirects = false; +export { }; -function main() { - let root = window.document.getElementById("main"); - if (root != null) { - window.ipc = new IPC(root); +declare global { + interface Window { + ipc: IPC; + interpreter: NativeInterpreter; } } +type NativeInterpreter = any; class IPC { - constructor(root) { - window.interpreter = new NativeInterpreter(); + ws: WebSocket; + + constructor(root: Element, addr: string) { + // window.interpreter = new NativeInterpreter(); window.interpreter.initialize(root); window.interpreter.liveview = true; window.interpreter.ipc = this; - const ws = new WebSocket(WS_ADDR); + + const ws = new WebSocket(addr); ws.binaryType = "arraybuffer"; function ping() { @@ -34,18 +38,19 @@ class IPC { const u8view = new Uint8Array(message.data); const binaryFrame = u8view[0] == 1; const messageData = message.data.slice(1); + // The first byte tells the shim if this is a binary of text frame if (binaryFrame) { // binary frame window.interpreter.run_from_bytes(messageData); } else { // text frame - let decoder = new TextDecoder("utf-8"); // Using decode method to get string output let str = decoder.decode(messageData); // Ignore pongs + if (str != "__pong__") { const event = JSON.parse(str); switch (event.type) { @@ -60,9 +65,15 @@ class IPC { this.ws = ws; } - postMessage(msg) { + postMessage(msg: string) { this.ws.send(msg); } } -main(); +export function main(addr: string) { + let root = window.document.getElementById("main"); + + if (root != null) { + window.ipc = new IPC(root, addr); + } +} diff --git a/packages/liveview/tsconfig.json b/packages/liveview/tsconfig.json new file mode 100644 index 0000000000..82dceb08ec --- /dev/null +++ b/packages/liveview/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "module": "CommonJS", + "lib": [ + "ES2015", + "DOM", + "dom", + "dom.iterable", + "ESNext" + ], + "noImplicitAny": true, + "removeComments": true, + "preserveConstEnums": true, + "typeRoots": [".src/ts/types"] + }, + "exclude": [ + "**/*.spec.ts" + ] +} diff --git a/packages/router-macro/src/lib.rs b/packages/router-macro/src/lib.rs index 0ed7d26259..889f118992 100644 --- a/packages/router-macro/src/lib.rs +++ b/packages/router-macro/src/lib.rs @@ -721,6 +721,14 @@ impl RouteEnum { _ => VNode::empty() } } + + fn serialize(&self) -> String { + self.to_string() + } + + fn deserialize(input: &str) -> Result> { + std::str::FromStr::from_str(input).map_err(|err| Box::new(err) as _) + } } } } diff --git a/packages/router/Cargo.toml b/packages/router/Cargo.toml index 6693619a2f..20cfa335d9 100644 --- a/packages/router/Cargo.toml +++ b/packages/router/Cargo.toml @@ -11,42 +11,43 @@ keywords = ["dom", "ui", "gui", "react", "wasm"] [dependencies] dioxus-lib = { workspace = true } +dioxus-document = { workspace = true } dioxus-router-macro = { workspace = true } -gloo = { version = "0.8.0", optional = true } +gloo = { workspace = true, optional = true } tracing = { workspace = true } -urlencoding = "2.1.3" -serde = { version = "1", features = ["derive"], optional = true } -serde_json = { version = "1.0.91", optional = true } +urlencoding = { workspace = true } +serde = { workspace = true, features = ["derive"], optional = true } +serde_json = { workspace = true, optional = true } url = "2.3.1" + wasm-bindgen = { workspace = true, optional = true } -web-sys = { version = "0.3.60", optional = true, features = [ - "ScrollRestoration", -] } +web-sys = { version = "0.3.60", optional = true, features = ["ScrollRestoration"] } js-sys = { version = "0.3.63", optional = true } -gloo-utils = { version = "0.1.6", optional = true } +gloo-utils = { workspace = true, optional = true } dioxus-liveview = { workspace = true, optional = true } dioxus-ssr = { workspace = true, optional = true } http = { workspace = true, optional = true } dioxus-fullstack = { workspace = true, optional = true } -tokio = { workspace = true, features = ["full"], optional = true } -rustversion = "1.0.17" +tokio = { workspace = true, optional = true, features = ["full"] } +rustversion = { workspace = true } # you need to comment this out when publishing since cargo workspaces is not smart enough to wipe this when dropping # dev-dependncey crates [target.'cfg(target_family = "wasm")'.dev-dependencies] console_error_panic_hook = "0.1.7" -dioxus-router = { workspace = true, features = ["web"] } -# dioxus-web = { workspace = true } -gloo = "0.8.0" +gloo = { workspace = true } wasm-bindgen-test = "0.3.33" +# dioxus-router = { workspace = true } +# dioxus-router = { workspace = true, features = ["web"] } +# dioxus-web = { workspace = true } [features] default = [] -ssr = [] -liveview = ["dioxus-liveview", "dep:tokio", "dep:serde", "dep:serde_json"] -wasm_test = [] -web = ["dep:gloo", "dep:web-sys", "dep:wasm-bindgen", "dep:gloo-utils", "dep:js-sys", "dioxus-router-macro/web"] -fullstack = ["dep:dioxus-fullstack"] +# ssr = [] +# wasm_test = [] +# liveview = ["dioxus-liveview", "dep:tokio", "dep:serde", "dep:serde_json"] +# web = ["dep:gloo", "dep:web-sys", "dep:wasm-bindgen", "dep:gloo-utils", "dep:js-sys", "dioxus-router-macro/web"] +# fullstack = ["dep:dioxus-fullstack"] [dev-dependencies] axum = { workspace = true, features = ["ws"] } diff --git a/packages/router/examples/manual.rs b/packages/router/examples/manual.rs new file mode 100644 index 0000000000..925d1d7351 --- /dev/null +++ b/packages/router/examples/manual.rs @@ -0,0 +1,68 @@ +use std::num::ParseIntError; + +use dioxus::prelude::*; + +fn main() { + dioxus::launch(|| { + rsx! { + Router:: {} + } + }); +} + +#[derive(Clone, PartialEq, Debug)] +enum Route { + Home, + About, + Blog { id: usize }, +} + +impl Routable for Route { + const SITE_MAP: &'static [SiteMapSegment] = &[]; + + fn render(&self, _level: usize) -> Element { + rsx! { + div { + nav { + Link { to: Route::Home, "Home" } + Link { to: Route::About, "About" } + Link { to: "/home", "About" } + Link { to: "https://example.com/about", "Other about" } + } + match self { + Route::Home => rsx! { + h1 { "Home" } + }, + Route::About => rsx! { + h1 { "About" } + }, + Route::Blog { id } => rsx! { + h1 { "Blog" } + p { "Id: {id}" } + }, + } + } + } + } + + fn serialize(&self) -> String { + match self { + Route::Home => "/".to_string(), + Route::About => "/about".to_string(), + Route::Blog { id } => format!("/blog/{id}"), + } + } + + fn deserialize(route: &str) -> Result> { + match route { + "/" => Ok(Route::Home), + "/about" => Ok(Route::About), + blah if blah.starts_with("/blog/") => blah + .strip_prefix("/blog/") + .ok_or_else(|| format!("Failed to parse route: {}", route).into()) + .and_then(|s| s.parse().map_err(|e: ParseIntError| e.to_string().into())) + .map(|id| Route::Blog { id }), + _ => Err(format!("Failed to parse route: {}", route).into()), + } + } +} diff --git a/packages/router/examples/simple_routes.rs b/packages/router/examples/simple_routes.rs index 3ee695dee6..d95f91a8bd 100644 --- a/packages/router/examples/simple_routes.rs +++ b/packages/router/examples/simple_routes.rs @@ -67,9 +67,7 @@ fn UserFrame(user_id: usize) -> Element { #[component] fn Route1(user_id: usize, dynamic: usize, query: String) -> Element { rsx! { - pre { - "Route1{{\n\tuser_id:{user_id},\n\tdynamic:{dynamic},\n\tquery:{query}\n}}" - } + pre { "Route1{{\n\tuser_id:{user_id},\n\tdynamic:{dynamic},\n\tquery:{query}\n}}" } Link { to: Route::Route1 { user_id, @@ -92,7 +90,9 @@ fn Route1(user_id: usize, dynamic: usize, query: String) -> Element { fn Route2(user_id: usize) -> Element { rsx! { pre { "Route2{{\n\tuser_id:{user_id}\n}}" } - {(0..user_id).map(|i| rsx! { p { "{i}" } })} + {(0..user_id).map(|i| rsx! { + p { "{i}" } + })} p { "Footer" } Link { to: Route::Route3 { @@ -122,7 +122,7 @@ fn Route3(dynamic: String) -> Element { oninput: move |evt: FormEvent| { *current_route_str.write() = evt.value(); }, - value: "{current_route_str}" + value: "{current_route_str}", } "dynamic: {dynamic}" Link { to: Route::Route2 { user_id: 8888 }, "hello world link" } @@ -153,22 +153,15 @@ fn Route3(dynamic: String) -> Element { Ok(route) => { if route != current_route { rsx! { - Link { - to: route.clone(), - "{route}" - } + Link { to: route.clone(), "{route}" } } - } - else { + } else { VNode::empty() } } Err(err) => { rsx! { - pre { - color: "red", - "Invalid route:\n{err}" - } + pre { color: "red", "Invalid route:\n{err}" } } } } @@ -199,6 +192,7 @@ enum Route { #[end_nest] #[end_nest] #[redirect("/:id/user", |id: usize| Route::Route3 { dynamic: id.to_string()})] + #[route("/:dynamic")] Route3 { dynamic: String }, } diff --git a/packages/router/src/components/link.rs b/packages/router/src/components/link.rs index c8054cfcde..5b0ae4524c 100644 --- a/packages/router/src/components/link.rs +++ b/packages/router/src/components/link.rs @@ -1,82 +1,11 @@ #![allow(clippy::type_complexity)] -use std::any::Any; -use std::fmt::Debug; -use std::rc::Rc; - -use dioxus_lib::prelude::*; - -use tracing::error; - -use crate::navigation::NavigationTarget; use crate::prelude::Routable; use crate::utils::use_router_internal::use_router_internal; - -use url::Url; - -/// Something that can be converted into a [`NavigationTarget`]. -#[derive(Clone)] -pub enum IntoRoutable { - /// A raw string target. - FromStr(String), - /// A internal target. - Route(Rc), -} - -impl PartialEq for IntoRoutable { - fn eq(&self, other: &Self) -> bool { - match (self, other) { - (IntoRoutable::FromStr(a), IntoRoutable::FromStr(b)) => a == b, - (IntoRoutable::Route(a), IntoRoutable::Route(b)) => Rc::ptr_eq(a, b), - _ => false, - } - } -} - -impl From for IntoRoutable { - fn from(value: R) -> Self { - IntoRoutable::Route(Rc::new(value) as Rc) - } -} - -impl From> for IntoRoutable { - fn from(value: NavigationTarget) -> Self { - match value { - NavigationTarget::Internal(route) => IntoRoutable::Route(Rc::new(route) as Rc), - NavigationTarget::External(url) => IntoRoutable::FromStr(url), - } - } -} - -impl From for IntoRoutable { - fn from(value: String) -> Self { - IntoRoutable::FromStr(value) - } -} - -impl From<&String> for IntoRoutable { - fn from(value: &String) -> Self { - IntoRoutable::FromStr(value.to_string()) - } -} - -impl From<&str> for IntoRoutable { - fn from(value: &str) -> Self { - IntoRoutable::FromStr(value.to_string()) - } -} - -impl From for IntoRoutable { - fn from(url: Url) -> Self { - IntoRoutable::FromStr(url.to_string()) - } -} - -impl From<&Url> for IntoRoutable { - fn from(url: &Url) -> Self { - IntoRoutable::FromStr(url.to_string()) - } -} +use crate::{navigation::NavigationTarget, prelude::IntoRoutable}; +use dioxus_lib::prelude::*; +use std::fmt::Debug; +use tracing::error; /// The properties for a [`Link`]. #[derive(Props, Clone, PartialEq)] @@ -228,11 +157,14 @@ pub fn Link(props: LinkProps) -> Element { }; let current_url = router.current_route_string(); - let href = match &to { - IntoRoutable::FromStr(url) => url.to_string(), - IntoRoutable::Route(route) => router.any_route_to_string(&**route), - }; - let parsed_route: NavigationTarget> = router.resolve_into_routable(to.clone()); + let href: String = todo!(); + // let parsed_route: NavigationTarget<_> = todo!(); + // let href = router.any_route_to_string(&to); + // let href = match &to { + // // IntoRoutable::FromStr(url) => url.to_string(), + // // IntoRoutable::Route(route) => router.any_route_to_string(&**route), + // }; + // let parsed_route: NavigationTarget = router.resolve_into_routable(to.clone()); let mut class_ = String::new(); if let Some(c) = class { @@ -257,7 +189,8 @@ pub fn Link(props: LinkProps) -> Element { let tag_target = new_tab.then_some("_blank"); - let is_external = matches!(parsed_route, NavigationTarget::External(_)); + let is_external = to.is_external(); + // let is_external = matches!(parsed_route, NavigationTarget::External(_)); let is_router_nav = !is_external && !new_tab; let rel = rel.or_else(|| is_external.then_some("noopener noreferrer".to_string())); @@ -274,9 +207,10 @@ pub fn Link(props: LinkProps) -> Element { } event.prevent_default(); - if do_default && is_router_nav { - router.push_any(router.resolve_into_routable(to.clone())); - } + todo!(); + // if do_default && is_router_nav { + // router.push_any(router.resolve_into_routable(to.clone())); + // } if let Some(handler) = onclick { handler.call(event); @@ -296,7 +230,7 @@ pub fn Link(props: LinkProps) -> Element { // If the event is a click with the left mouse button and no modifiers, prevent the default action // and navigate to the href with client side routing router.is_liveview().then_some( - "if (event.button === 0 && !event.ctrlKey && !event.metaKey && !event.shiftKey && !event.altKey) { event.preventDefault() }" + "if (event.button === 0 && !event.ctrlKey && !event.metaKey && !event.shiftKey && !event.altKey) { event.preventDefault() }" ) }; @@ -305,7 +239,7 @@ pub fn Link(props: LinkProps) -> Element { onclick: action, "onclick": liveview_prevent_default, href, - onmounted: onmounted, + onmounted, class, rel, target: tag_target, diff --git a/packages/router/src/components/mod.rs b/packages/router/src/components/mod.rs new file mode 100644 index 0000000000..e13972810a --- /dev/null +++ b/packages/router/src/components/mod.rs @@ -0,0 +1,15 @@ +/// Components interacting with the router. +mod default_errors; +pub use default_errors::*; + +mod history_buttons; +pub use history_buttons::*; + +mod link; +pub use link::*; + +mod outlet; +pub use outlet::*; + +mod router; +pub use router::*; diff --git a/packages/router/src/components/outlet.rs b/packages/router/src/components/outlet.rs index e7ffd9c9a1..e79ab00d31 100644 --- a/packages/router/src/components/outlet.rs +++ b/packages/router/src/components/outlet.rs @@ -68,6 +68,6 @@ use dioxus_lib::prelude::*; /// # vdom.rebuild_in_place(); /// # assert_eq!(dioxus_ssr::render(&vdom), "

    App

    Child

    "); /// ``` -pub fn Outlet() -> Element { +pub fn Outlet() -> Element { OutletContext::::render() } diff --git a/packages/router/src/components/router.rs b/packages/router/src/components/router.rs index 1a8c066fa6..231e3d6281 100644 --- a/packages/router/src/components/router.rs +++ b/packages/router/src/components/router.rs @@ -1,29 +1,26 @@ use dioxus_lib::prelude::*; -use std::str::FromStr; - use crate::{ - prelude::{provide_router_context, Outlet}, + prelude::{Outlet, RouterContext}, routable::Routable, router_cfg::RouterConfig, }; /// The props for [`Router`]. #[derive(Props)] -pub struct RouterProps { +pub struct RouterProps { #[props(default, into)] config: Callback<(), RouterConfig>, } -impl Clone for RouterProps { +impl Clone for RouterProps { fn clone(&self) -> Self { *self } } -impl Copy for RouterProps {} - -impl Default for RouterProps { +impl Copy for RouterProps {} +impl Default for RouterProps { fn default() -> Self { Self { config: Callback::new(|_| RouterConfig::default()), @@ -31,7 +28,7 @@ impl Default for RouterProps { } } -impl PartialEq for RouterProps { +impl PartialEq for RouterProps { fn eq(&self, _: &Self) -> bool { // prevent the router from re-rendering when the initial url or config changes true @@ -39,17 +36,18 @@ impl PartialEq for RouterProps { } /// A component that renders the current route. -pub fn Router(props: RouterProps) -> Element -where - ::Err: std::fmt::Display, -{ +pub fn Router(props: RouterProps) -> Element { use crate::prelude::{outlet::OutletContext, RouterContext}; use_hook(|| { - provide_router_context(RouterContext::new( - props.config.call(()), - schedule_update_any(), - )); + let ctx = RouterContext::new(props.config.call(())); + if root_router().is_none() { + ScopeId::ROOT.provide_context(RootRouterContext(Signal::new_in_scope( + Some(ctx), + ScopeId::ROOT, + ))); + } + provide_context(ctx); provide_context(OutletContext:: { current_level: 0, @@ -57,5 +55,23 @@ where }); }); - rsx! { Outlet:: {} } + rsx! { + Outlet:: {} + } +} + +/// This context is set in the root of the virtual dom if there is a router present. +#[derive(Clone, Copy)] +pub(crate) struct RootRouterContext(pub(crate) Signal>); + +/// Try to get the router that was created closest to the root of the virtual dom. This may be called outside of the router. +/// +/// This will return `None` if there is no router present or the router has not been created yet. +pub fn root_router() -> Option { + if let Some(ctx) = ScopeId::ROOT.consume_context::() { + ctx.0.cloned() + } else { + ScopeId::ROOT.provide_context(RootRouterContext(Signal::new_in_scope(None, ScopeId::ROOT))); + None + } } diff --git a/packages/router/src/contexts/generic_router.rs b/packages/router/src/contexts/generic_router.rs new file mode 100644 index 0000000000..26e9e085d8 --- /dev/null +++ b/packages/router/src/contexts/generic_router.rs @@ -0,0 +1,102 @@ +use core::panic; +use std::{ + any::Any, + collections::HashSet, + rc::Rc, + sync::{Arc, RwLock}, +}; + +use dioxus_document::DocumentContext; +use dioxus_lib::prelude::*; + +use crate::{ + navigation::NavigationTarget, + prelude::{IntoRoutable, SiteMapSegment}, + routable::Routable, + router_cfg::RouterConfig, +}; + +use super::{ExternalNavigationFailure, RouterContext}; + +pub struct GenericRouterContext { + inner: RouterContext, + _marker: std::marker::PhantomData, +} + +impl GenericRouterContext { + /// Check whether there is a previous page to navigate back to. + #[must_use] + pub fn can_go_back(&self) -> bool { + self.inner.can_go_back() + } + + /// Check whether there is a future page to navigate forward to. + #[must_use] + pub fn can_go_forward(&self) -> bool { + self.inner.can_go_forward() + } + + /// Go back to the previous location. + /// + /// Will fail silently if there is no previous location to go to. + pub fn go_back(&self) { + self.inner.go_back(); + } + + /// Go back to the next location. + /// + /// Will fail silently if there is no next location to go to. + pub fn go_forward(&self) { + self.inner.go_forward(); + } + + /// Push a new location. + /// + /// The previous location will be available to go back to. + pub fn push( + &self, + target: impl Into>, + ) -> Option { + todo!() + // self.inner.push(target.into()) + } + + /// Replace the current location. + /// + /// The previous location will **not** be available to go back to. + pub fn replace( + &self, + target: impl Into>, + ) -> Option { + todo!() + // self.inner.replace(target.into()) + } + + /// The route that is currently active. + pub fn current(&self) -> R + where + R: Clone, + { + self.inner.current() + } + + /// The prefix that is currently active. + pub fn prefix(&self) -> Option { + self.inner.prefix() + } + + /// Manually subscribe to the current route + pub fn subscribe(&self, id: ScopeId) { + self.inner.subscribe(id) + } + + /// Manually unsubscribe from the current route + pub fn unsubscribe(&self, id: ScopeId) { + self.inner.unsubscribe(id) + } + + /// Clear any unresolved errors + pub fn clear_error(&self) { + self.inner.clear_error() + } +} diff --git a/packages/router/src/contexts/outlet.rs b/packages/router/src/contexts/outlet.rs index 664ea19f5b..c64d0bbfc7 100644 --- a/packages/router/src/contexts/outlet.rs +++ b/packages/router/src/contexts/outlet.rs @@ -28,7 +28,7 @@ pub(crate) fn use_outlet_context() -> OutletContext { impl OutletContext { pub(crate) fn render() -> Element where - R: Routable + Clone, + R: Routable, { let router = use_router_internal().expect("Outlet must be inside of a router"); let outlet: OutletContext = use_outlet_context(); diff --git a/packages/router/src/contexts/router.rs b/packages/router/src/contexts/router.rs index 26341b07e2..89a9dd8b3d 100644 --- a/packages/router/src/contexts/router.rs +++ b/packages/router/src/contexts/router.rs @@ -1,3 +1,4 @@ +use core::panic; use std::{ any::Any, collections::HashSet, @@ -5,157 +6,100 @@ use std::{ sync::{Arc, RwLock}, }; +use dioxus_document::DocumentContext; use dioxus_lib::prelude::*; use crate::{ navigation::NavigationTarget, - prelude::{AnyHistoryProvider, IntoRoutable, SiteMapSegment}, + prelude::{IntoRoutable, SiteMapSegment}, routable::Routable, router_cfg::RouterConfig, }; -/// This context is set in the root of the virtual dom if there is a router present. -#[derive(Clone, Copy)] -struct RootRouterContext(Signal>); - -/// Try to get the router that was created closest to the root of the virtual dom. This may be called outside of the router. -/// -/// This will return `None` if there is no router present or the router has not been created yet. -pub fn root_router() -> Option { - if let Some(ctx) = ScopeId::ROOT.consume_context::() { - ctx.0.cloned() - } else { - ScopeId::ROOT.provide_context(RootRouterContext(Signal::new_in_scope(None, ScopeId::ROOT))); - None - } -} +use super::generic_router::GenericRouterContext; -pub(crate) fn provide_router_context(ctx: RouterContext) { - if root_router().is_none() { - ScopeId::ROOT.provide_context(RootRouterContext(Signal::new_in_scope( - Some(ctx), - ScopeId::ROOT, - ))); - } - provide_context(ctx); +/// A collection of router data that manages all routing functionality. +#[derive(Clone, Copy)] +pub struct RouterContext { + inner: CopyValue, } -/// An error that can occur when navigating. -#[derive(Debug, Clone)] -pub struct ExternalNavigationFailure(pub String); - -/// A function the router will call after every routing update. -pub(crate) type RoutingCallback = - Arc) -> Option>>; -pub(crate) type AnyRoutingCallback = - Arc Option>>>; - struct RouterContextInner { - /// The current prefix. - prefix: Option, + basepath: Option, + + runtime: Rc, - history: Box, + history: DocumentContext, unresolved_error: Option, subscribers: Arc>>, - subscriber_update: Arc, - routing_callback: Option, failure_external_navigation: fn() -> Element, any_route_to_string: fn(&dyn Any) -> String, site_map: &'static [SiteMapSegment], -} - -impl RouterContextInner { - fn update_subscribers(&self) { - let update = &self.subscriber_update; - for &id in self.subscribers.read().unwrap().iter() { - update(id); - } - } - - fn external(&mut self, external: String) -> Option { - match self.history.external(external.clone()) { - true => None, - false => { - let failure = ExternalNavigationFailure(external); - self.unresolved_error = Some(failure.clone()); - - self.update_subscribers(); - - Some(failure) - } - } - } -} - -/// A collection of router data that manages all routing functionality. -#[derive(Clone, Copy)] -pub struct RouterContext { - inner: CopyValue, + // routing_callback: Option, } impl RouterContext { - pub(crate) fn new( - mut cfg: RouterConfig, - mark_dirty: Arc, - ) -> Self - where - ::Err: std::fmt::Display, - { - let subscriber_update = mark_dirty.clone(); + pub(crate) fn new(mut cfg: RouterConfig) -> Self { let subscribers = Arc::new(RwLock::new(HashSet::new())); let mut myself = RouterContextInner { - prefix: Default::default(), - history: cfg.take_history(), + basepath: Default::default(), unresolved_error: None, subscribers: subscribers.clone(), - subscriber_update, - - routing_callback: cfg.on_update.map(|update| { - Arc::new(move |ctx| { - let ctx = GenericRouterContext { - inner: ctx, - _marker: std::marker::PhantomData, - }; - update(ctx).map(|t| match t { - NavigationTarget::Internal(r) => { - NavigationTarget::Internal(Rc::new(r) as Rc) - } - NavigationTarget::External(s) => NavigationTarget::External(s), - }) - }) - as Arc Option>>> - }), - + history: todo!(), + // history: cfg.take_history(), + + // subscriber_update, + + // routing_callback: cfg.on_update.map(|update| { + // Arc::new(move |ctx| { + // let ctx = GenericRouterContext { + // inner: ctx, + // _marker: std::marker::PhantomData, + // }; + // update(ctx).map(|t| match t { + // NavigationTarget::Internal(r) => { + // NavigationTarget::Internal(Rc::new(r) as Rc) + // } + // NavigationTarget::External(s) => NavigationTarget::External(s), + // }) + // }) + // as Arc Option>>> + // }), failure_external_navigation: cfg.failure_external_navigation, any_route_to_string: |route| { - route - .downcast_ref::() - .unwrap_or_else(|| { - panic!( - "Route is not of the expected type: {}\n found typeid: {:?}\n expected typeid: {:?}", - std::any::type_name::(), - route.type_id(), - std::any::TypeId::of::() - ) - }) - .to_string() + todo!() + // route + // .downcast_ref::() + // .unwrap_or_else(|| { + // panic!( + // "Route is not of the expected type: {}\n found typeid: {:?}\n expected typeid: {:?}", + // std::any::type_name::(), + // route.type_id(), + // std::any::TypeId::of::() + // ) + // }) + // .to_string() }, - site_map: R::SITE_MAP, + site_map: todo!(), + // site_map: R::SITE_MAP, + runtime: todo!(), + // routing_callback: todo!(), }; // set the updater { + let rt = myself.runtime.clone(); myself.history.updater(Arc::new(move || { for &id in subscribers.read().unwrap().iter() { - (mark_dirty)(id); + rt.mark_dirty(id); } })); } @@ -178,8 +122,9 @@ impl RouterContext { } } - pub(crate) fn route_from_str(&self, route: &str) -> Result, String> { - self.inner.read().history.parse_route(route) + pub(crate) fn route_from_str(&self, route: &str) -> Result { + todo!() + // self.inner.read().history.parse_route(route) } /// Check whether there is a previous page to navigate back to. @@ -216,16 +161,17 @@ impl RouterContext { self.change_route(); } - pub(crate) fn push_any( + pub(crate) fn push_any( &self, - target: NavigationTarget>, + target: NavigationTarget, ) -> Option { - { - let mut write = self.inner.write_unchecked(); - match target { - NavigationTarget::Internal(p) => write.history.push(p), - NavigationTarget::External(e) => return write.external(e), - } + match target { + NavigationTarget::Internal(p) => self + .inner + .write_unchecked() + .history + .push_route(p.serialize()), + NavigationTarget::External(e) => return self.navigate_external(e), } self.change_route() @@ -235,75 +181,77 @@ impl RouterContext { /// /// The previous location will be available to go back to. pub fn push(&self, target: impl Into) -> Option { - let target = self.resolve_into_routable(target.into()); - { - let mut write = self.inner.write_unchecked(); - match target { - NavigationTarget::Internal(p) => write.history.push(p), - NavigationTarget::External(e) => return write.external(e), - } - } + todo!() + // let target = self.resolve_into_routable(target.into()); + // { + // let mut write = self.inner.write_unchecked(); + // match target { + // NavigationTarget::Internal(p) => write.history.push_route(p), + // NavigationTarget::External(e) => return write.external(e), + // } + // } - self.change_route() + // self.change_route() } /// Replace the current location. /// /// The previous location will **not** be available to go back to. pub fn replace(&self, target: impl Into) -> Option { - let target = self.resolve_into_routable(target.into()); + todo!() + // let target = self.resolve_into_routable(target.into()); - { - let mut state = self.inner.write_unchecked(); - match target { - NavigationTarget::Internal(p) => state.history.replace(p), - NavigationTarget::External(e) => return state.external(e), - } - } + // { + // let mut state = self.inner.write_unchecked(); + // match target { + // NavigationTarget::Internal(p) => state.history.replace_route(p), + // NavigationTarget::External(e) => return state.external(e), + // } + // } - self.change_route() + // self.change_route() } /// The route that is currently active. pub fn current(&self) -> R { - self.inner - .read() - .history - .current_route() - .downcast::() - .unwrap() - .as_ref() - .clone() + todo!() + // self.inner + // .read() + // .history + // .current_route() + // .parse() + // .unwrap_or_else(|err| panic!("Failed to parse route")) } /// The route that is currently active. pub fn current_route_string(&self) -> String { - self.any_route_to_string(&*self.inner.read().history.current_route()) + self.inner.read_unchecked().history.current_route() } pub(crate) fn any_route_to_string(&self, route: &dyn Any) -> String { (self.inner.read().any_route_to_string)(route) } - pub(crate) fn resolve_into_routable( + pub(crate) fn resolve_into_routable( &self, into_routable: IntoRoutable, - ) -> NavigationTarget> { - match into_routable { - IntoRoutable::FromStr(url) => { - let parsed_route: NavigationTarget> = match self.route_from_str(&url) { - Ok(route) => NavigationTarget::Internal(route), - Err(_) => NavigationTarget::External(url), - }; - parsed_route - } - IntoRoutable::Route(route) => NavigationTarget::Internal(route), - } + ) -> NavigationTarget { + todo!() + // match into_routable { + // IntoRoutable::FromStr(url) => { + // let parsed_route: NavigationTarget> = match self.route_from_str(&url) { + // Ok(route) => NavigationTarget::Internal(route), + // Err(_) => NavigationTarget::External(url), + // }; + // parsed_route + // } + // IntoRoutable::Route(route) => NavigationTarget::Internal(route), + // } } /// The prefix that is currently active. pub fn prefix(&self) -> Option { - self.inner.read().prefix.clone() + self.inner.read().basepath.clone() } /// Manually subscribe to the current route @@ -318,10 +266,11 @@ impl RouterContext { /// Clear any unresolved errors pub fn clear_error(&self) { - let mut write_inner = self.inner.write_unchecked(); - write_inner.unresolved_error = None; - - write_inner.update_subscribers(); + { + let mut write_inner = self.inner.write_unchecked(); + write_inner.unresolved_error = None; + } + self.update_subscribers(); } /// Get the site map of the router. @@ -338,106 +287,61 @@ impl RouterContext { } fn change_route(&self) -> Option { - let self_read = self.inner.read(); - if let Some(callback) = &self_read.routing_callback { - let myself = *self; - let callback = callback.clone(); - drop(self_read); - if let Some(new) = callback(myself) { - let mut self_write = self.inner.write_unchecked(); - match new { - NavigationTarget::Internal(p) => self_write.history.replace(p), - NavigationTarget::External(e) => return self_write.external(e), - } - } - } - - self.inner.read().update_subscribers(); - - None + todo!() + // let self_read = self.inner.read(); + // if let Some(callback) = &self_read.routing_callback { + // let myself = *self; + // let callback = callback.clone(); + // drop(self_read); + // if let Some(new) = callback(myself) { + // let mut self_write = self.inner.write_unchecked(); + // match new { + // NavigationTarget::Internal(p) => self_write.history.replace_route(p), + // NavigationTarget::External(e) => return self_write.external(e), + // } + // } + // } + + // self.inner.read().update_subscribers(); + + // None } -} -pub struct GenericRouterContext { - inner: RouterContext, - _marker: std::marker::PhantomData, -} - -impl GenericRouterContext -where - R: Routable, -{ - /// Check whether there is a previous page to navigate back to. - #[must_use] - pub fn can_go_back(&self) -> bool { - self.inner.can_go_back() - } - - /// Check whether there is a future page to navigate forward to. - #[must_use] - pub fn can_go_forward(&self) -> bool { - self.inner.can_go_forward() - } - - /// Go back to the previous location. - /// - /// Will fail silently if there is no previous location to go to. - pub fn go_back(&self) { - self.inner.go_back(); - } - - /// Go back to the next location. - /// - /// Will fail silently if there is no next location to go to. - pub fn go_forward(&self) { - self.inner.go_forward(); - } - - /// Push a new location. - /// - /// The previous location will be available to go back to. - pub fn push( - &self, - target: impl Into>, - ) -> Option { - self.inner.push(target.into()) + fn update_subscribers(&self) { + let inner = self.inner.read_unchecked(); + for &id in inner.subscribers.read().unwrap().iter() { + inner.runtime.mark_dirty(id) + } } - /// Replace the current location. - /// - /// The previous location will **not** be available to go back to. - pub fn replace( - &self, - target: impl Into>, - ) -> Option { - self.inner.replace(target.into()) - } + fn navigate_external(&self, external: String) -> Option { + let failure = { + let mut myself = self.inner.write_unchecked(); + match myself.history.navigate_external(external.clone()) { + true => None, + false => { + let failure = ExternalNavigationFailure(external); + myself.unresolved_error = Some(failure.clone()); + Some(failure) + } + } + }; - /// The route that is currently active. - pub fn current(&self) -> R - where - R: Clone, - { - self.inner.current() - } + if failure.is_some() { + self.update_subscribers(); + } - /// The prefix that is currently active. - pub fn prefix(&self) -> Option { - self.inner.prefix() + failure } +} - /// Manually subscribe to the current route - pub fn subscribe(&self, id: ScopeId) { - self.inner.subscribe(id) - } +/// An error that can occur when navigating. +#[derive(Debug, Clone)] +pub struct ExternalNavigationFailure(pub String); - /// Manually unsubscribe from the current route - pub fn unsubscribe(&self, id: ScopeId) { - self.inner.unsubscribe(id) - } +/// A function the router will call after every routing update. +pub(crate) type RoutingCallback = + Arc) -> Option>>; - /// Clear any unresolved errors - pub fn clear_error(&self) { - self.inner.clear_error() - } -} +pub(crate) type AnyRoutingCallback = Arc Option>>; +// Arc Option>>>; diff --git a/packages/router/src/history/memory.rs b/packages/router/src/history/memory.rs index fd4b43dd5d..61f7f4eb42 100644 --- a/packages/router/src/history/memory.rs +++ b/packages/router/src/history/memory.rs @@ -1,9 +1,5 @@ -use std::str::FromStr; - use crate::routable::Routable; -use super::HistoryProvider; - /// A [`HistoryProvider`] that stores all navigation information in memory. pub struct MemoryHistory { current: R, @@ -11,10 +7,7 @@ pub struct MemoryHistory { future: Vec, } -impl MemoryHistory -where - ::Err: std::fmt::Display, -{ +impl MemoryHistory { /// Create a [`MemoryHistory`] starting at `path`. /// /// ```rust @@ -43,53 +36,36 @@ where future: Vec::new(), } } -} -impl Default for MemoryHistory -where - ::Err: std::fmt::Display, -{ - fn default() -> Self { - Self { - current: "/".parse().unwrap_or_else(|err| { - panic!("index route does not exist:\n{err}\n use MemoryHistory::with_initial_path to set a custom path") - }), - history: Vec::new(), - future: Vec::new(), - } - } -} - -impl HistoryProvider for MemoryHistory { - fn current_route(&self) -> R { + pub fn current_route(&self) -> R { self.current.clone() } - fn can_go_back(&self) -> bool { + pub fn can_go_back(&self) -> bool { !self.history.is_empty() } - fn go_back(&mut self) { + pub fn go_back(&mut self) { if let Some(last) = self.history.pop() { let old = std::mem::replace(&mut self.current, last); self.future.push(old); } } - fn can_go_forward(&self) -> bool { + pub fn can_go_forward(&self) -> bool { !self.future.is_empty() } - fn go_forward(&mut self) { + pub fn go_forward(&mut self) { if let Some(next) = self.future.pop() { let old = std::mem::replace(&mut self.current, next); self.history.push(old); } } - fn push(&mut self, new: R) { + pub fn push(&mut self, new: R) { // don't push the same route twice - if self.current.to_string() == new.to_string() { + if self.current.serialize() == new.serialize() { return; } let old = std::mem::replace(&mut self.current, new); @@ -97,7 +73,19 @@ impl HistoryProvider for MemoryHistory { self.future.clear(); } - fn replace(&mut self, path: R) { + pub fn replace(&mut self, path: R) { self.current = path; } } + +impl Default for MemoryHistory { + fn default() -> Self { + Self { + current: R::deserialize("/").unwrap_or_else(|err| { + panic!("index route does not exist:\n{err}\n use MemoryHistory::with_initial_path to set a custom path") + }), + history: Vec::new(), + future: Vec::new(), + } + } +} diff --git a/packages/router/src/history/mod.rs b/packages/router/src/history/mod.rs index 1b5d186126..e498b36558 100644 --- a/packages/router/src/history/mod.rs +++ b/packages/router/src/history/mod.rs @@ -10,393 +10,5 @@ //! 1) [`MemoryHistory`] for desktop/mobile/ssr platforms //! 2) [`WebHistory`] for web platforms -use std::{any::Any, rc::Rc, sync::Arc}; - mod memory; pub use memory::*; - -#[cfg(feature = "web")] -mod web; -#[cfg(feature = "web")] -pub use web::*; -#[cfg(feature = "web")] -pub(crate) mod web_history; - -#[cfg(feature = "liveview")] -mod liveview; -#[cfg(feature = "liveview")] -pub use liveview::*; - -// #[cfg(feature = "web")] -// mod web_hash; -// #[cfg(feature = "web")] -// pub use web_hash::*; - -use crate::routable::Routable; - -#[cfg(feature = "web")] -pub(crate) mod web_scroll; - -/// An integration with some kind of navigation history. -/// -/// Depending on your use case, your implementation may deviate from the described procedure. This -/// is fine, as long as both `current_route` and `current_query` match the described format. -/// -/// However, you should document all deviations. Also, make sure the navigation is user-friendly. -/// The described behaviors are designed to mimic a web browser, which most users should already -/// know. Deviations might confuse them. -pub trait HistoryProvider { - /// Get the path of the current URL. - /// - /// **Must start** with `/`. **Must _not_ contain** the prefix. - /// - /// ```rust - /// # use dioxus_router::prelude::*; - /// # use dioxus::prelude::*; - /// # #[component] - /// # fn Index() -> Element { VNode::empty() } - /// # #[component] - /// # fn OtherPage() -> Element { VNode::empty() } - /// #[derive(Clone, Routable, Debug, PartialEq)] - /// enum Route { - /// #[route("/")] - /// Index {}, - /// #[route("/some-other-page")] - /// OtherPage {}, - /// } - /// let mut history = MemoryHistory::::default(); - /// assert_eq!(history.current_route().to_string(), "/"); - /// - /// history.push(Route::OtherPage {}); - /// assert_eq!(history.current_route().to_string(), "/some-other-page"); - /// ``` - #[must_use] - fn current_route(&self) -> R; - - /// Get the current path prefix of the URL. - /// - /// Not all [`HistoryProvider`]s need a prefix feature. It is meant for environments where a - /// dioxus-router-core-routed application is not running on `/`. The [`HistoryProvider`] is responsible - /// for removing the prefix from the dioxus-router-core-internal path, and also for adding it back in - /// during navigation. This functions value is only used for creating `href`s (e.g. for SSR or - /// display (but not navigation) in a web app). - fn current_prefix(&self) -> Option { - None - } - - /// Check whether there is a previous page to navigate back to. - /// - /// If a [`HistoryProvider`] cannot know this, it should return [`true`]. - /// - /// ```rust - /// # use dioxus_router::prelude::*; - /// # use dioxus::prelude::*; - /// # #[component] - /// # fn Index() -> Element { VNode::empty() } - /// # fn Other() -> Element { VNode::empty() } - /// #[derive(Clone, Routable, Debug, PartialEq)] - /// enum Route { - /// #[route("/")] - /// Index {}, - /// #[route("/other")] - /// Other {}, - /// } - /// let mut history = MemoryHistory::::default(); - /// assert_eq!(history.can_go_back(), false); - /// - /// history.push(Route::Other {}); - /// assert_eq!(history.can_go_back(), true); - /// ``` - #[must_use] - fn can_go_back(&self) -> bool { - true - } - - /// Go back to a previous page. - /// - /// If a [`HistoryProvider`] cannot go to a previous page, it should do nothing. This method - /// might be called, even if `can_go_back` returns [`false`]. - /// - /// ```rust - /// # use dioxus_router::prelude::*; - /// # use dioxus::prelude::*; - /// # #[component] - /// # fn Index() -> Element { VNode::empty() } - /// # #[component] - /// # fn OtherPage() -> Element { VNode::empty() } - /// #[derive(Clone, Routable, Debug, PartialEq)] - /// enum Route { - /// #[route("/")] - /// Index {}, - /// #[route("/some-other-page")] - /// OtherPage {}, - /// } - /// let mut history = MemoryHistory::::default(); - /// assert_eq!(history.current_route().to_string(), "/"); - /// - /// history.go_back(); - /// assert_eq!(history.current_route().to_string(), "/"); - /// - /// history.push(Route::OtherPage {}); - /// assert_eq!(history.current_route().to_string(), "/some-other-page"); - /// - /// history.go_back(); - /// assert_eq!(history.current_route().to_string(), "/"); - /// ``` - fn go_back(&mut self); - - /// Check whether there is a future page to navigate forward to. - /// - /// If a [`HistoryProvider`] cannot know this, it should return [`true`]. - /// - /// ```rust - /// # use dioxus_router::prelude::*; - /// # use dioxus::prelude::*; - /// # #[component] - /// # fn Index() -> Element { VNode::empty() } - /// # #[component] - /// # fn OtherPage() -> Element { VNode::empty() } - /// #[derive(Clone, Routable, Debug, PartialEq)] - /// enum Route { - /// #[route("/")] - /// Index {}, - /// #[route("/some-other-page")] - /// OtherPage {}, - /// } - /// let mut history = MemoryHistory::::default(); - /// assert_eq!(history.can_go_forward(), false); - /// - /// history.push(Route::OtherPage {}); - /// assert_eq!(history.can_go_forward(), false); - /// - /// history.go_back(); - /// assert_eq!(history.can_go_forward(), true); - /// ``` - #[must_use] - fn can_go_forward(&self) -> bool { - true - } - - /// Go forward to a future page. - /// - /// If a [`HistoryProvider`] cannot go to a previous page, it should do nothing. This method - /// might be called, even if `can_go_forward` returns [`false`]. - /// - /// ```rust - /// # use dioxus_router::prelude::*; - /// # use dioxus::prelude::*; - /// # #[component] - /// # fn Index() -> Element { VNode::empty() } - /// # #[component] - /// # fn OtherPage() -> Element { VNode::empty() } - /// #[derive(Clone, Routable, Debug, PartialEq)] - /// enum Route { - /// #[route("/")] - /// Index {}, - /// #[route("/some-other-page")] - /// OtherPage {}, - /// } - /// let mut history = MemoryHistory::::default(); - /// history.push(Route::OtherPage {}); - /// assert_eq!(history.current_route(), Route::OtherPage {}); - /// - /// history.go_back(); - /// assert_eq!(history.current_route(), Route::Index {}); - /// - /// history.go_forward(); - /// assert_eq!(history.current_route(), Route::OtherPage {}); - /// ``` - fn go_forward(&mut self); - - /// Go to another page. - /// - /// This should do three things: - /// 1. Merge the current URL with the `path` parameter (which may also include a query part). - /// 2. Remove the previous URL to the navigation history. - /// 3. Clear the navigation future. - /// - /// ```rust - /// # use dioxus_router::prelude::*; - /// # use dioxus::prelude::*; - /// # #[component] - /// # fn Index() -> Element { VNode::empty() } - /// # #[component] - /// # fn OtherPage() -> Element { VNode::empty() } - /// #[derive(Clone, Routable, Debug, PartialEq)] - /// enum Route { - /// #[route("/")] - /// Index {}, - /// #[route("/some-other-page")] - /// OtherPage {}, - /// } - /// let mut history = MemoryHistory::::default(); - /// assert_eq!(history.current_route(), Route::Index {}); - /// - /// history.push(Route::OtherPage {}); - /// assert_eq!(history.current_route(), Route::OtherPage {}); - /// assert!(history.can_go_back()); - /// ``` - fn push(&mut self, route: R); - - /// Replace the current page with another one. - /// - /// This should merge the current URL with the `path` parameter (which may also include a query - /// part). In contrast to the `push` function, the navigation history and future should stay - /// untouched. - /// - /// ```rust - /// # use dioxus_router::prelude::*; - /// # use dioxus::prelude::*; - /// # #[component] - /// # fn Index() -> Element { VNode::empty() } - /// # #[component] - /// # fn OtherPage() -> Element { VNode::empty() } - /// #[derive(Clone, Routable, Debug, PartialEq)] - /// enum Route { - /// #[route("/")] - /// Index {}, - /// #[route("/some-other-page")] - /// OtherPage {}, - /// } - /// let mut history = MemoryHistory::::default(); - /// assert_eq!(history.current_route(), Route::Index {}); - /// - /// history.replace(Route::OtherPage {}); - /// assert_eq!(history.current_route(), Route::OtherPage {}); - /// assert!(!history.can_go_back()); - /// ``` - fn replace(&mut self, path: R); - - /// Navigate to an external URL. - /// - /// This should navigate to an external URL, which isn't controlled by the router. If a - /// [`HistoryProvider`] cannot do that, it should return [`false`], otherwise [`true`]. - /// - /// Returning [`false`] will cause the router to handle the external navigation failure. - #[allow(unused_variables)] - fn external(&mut self, url: String) -> bool { - false - } - - /// Provide the [`HistoryProvider`] with an update callback. - /// - /// Some [`HistoryProvider`]s may receive URL updates from outside the router. When such - /// updates are received, they should call `callback`, which will cause the router to update. - #[allow(unused_variables)] - fn updater(&mut self, callback: Arc) {} -} - -pub(crate) trait AnyHistoryProvider { - fn parse_route(&self, route: &str) -> Result, String>; - - #[must_use] - fn current_route(&self) -> Rc; - - #[must_use] - fn can_go_back(&self) -> bool { - true - } - - fn go_back(&mut self); - - #[must_use] - fn can_go_forward(&self) -> bool { - true - } - - fn go_forward(&mut self); - - fn push(&mut self, route: Rc); - - fn replace(&mut self, path: Rc); - - #[allow(unused_variables)] - fn external(&mut self, url: String) -> bool { - false - } - - #[allow(unused_variables)] - fn updater(&mut self, callback: Arc) {} - - #[cfg(feature = "liveview")] - fn is_liveview(&self) -> bool; -} - -pub(crate) struct AnyHistoryProviderImplWrapper { - inner: H, - _marker: std::marker::PhantomData, -} - -impl AnyHistoryProviderImplWrapper { - pub fn new(inner: H) -> Self { - Self { - inner, - _marker: std::marker::PhantomData, - } - } -} - -impl Default for AnyHistoryProviderImplWrapper { - fn default() -> Self { - Self::new(H::default()) - } -} - -impl AnyHistoryProvider for AnyHistoryProviderImplWrapper -where - R: Routable, - ::Err: std::fmt::Display, - H: HistoryProvider, -{ - fn parse_route(&self, route: &str) -> Result, String> { - R::from_str(route) - .map_err(|err| err.to_string()) - .map(|route| Rc::new(route) as Rc) - } - - fn current_route(&self) -> Rc { - let route = self.inner.current_route(); - Rc::new(route) - } - - fn can_go_back(&self) -> bool { - self.inner.can_go_back() - } - - fn go_back(&mut self) { - self.inner.go_back() - } - - fn can_go_forward(&self) -> bool { - self.inner.can_go_forward() - } - - fn go_forward(&mut self) { - self.inner.go_forward() - } - - fn push(&mut self, route: Rc) { - self.inner - .push(route.downcast::().unwrap().as_ref().clone()) - } - - fn replace(&mut self, route: Rc) { - self.inner - .replace(route.downcast::().unwrap().as_ref().clone()) - } - - fn external(&mut self, url: String) -> bool { - self.inner.external(url) - } - - fn updater(&mut self, callback: Arc) { - self.inner.updater(callback) - } - - #[cfg(feature = "liveview")] - fn is_liveview(&self) -> bool { - use std::any::TypeId; - - TypeId::of::() == TypeId::of::>() - } -} diff --git a/packages/router/src/history/web_history.rs b/packages/router/src/history/web_history.rs deleted file mode 100644 index df52198743..0000000000 --- a/packages/router/src/history/web_history.rs +++ /dev/null @@ -1,42 +0,0 @@ -use gloo::console::error; -use wasm_bindgen::JsValue; -use web_sys::History; - -pub(crate) fn replace_state_with_url( - history: &History, - value: &[f64; 2], - url: Option<&str>, -) -> Result<(), JsValue> { - let position = js_sys::Array::new(); - position.push(&JsValue::from(value[0])); - position.push(&JsValue::from(value[1])); - - history.replace_state_with_url(&position, "", url) -} - -pub(crate) fn push_state_and_url( - history: &History, - value: &[f64; 2], - url: String, -) -> Result<(), JsValue> { - let position = js_sys::Array::new(); - position.push(&JsValue::from(value[0])); - position.push(&JsValue::from(value[1])); - - history.push_state_with_url(&position, "", Some(&url)) -} - -pub(crate) fn get_current(history: &History) -> Option<[f64; 2]> { - use wasm_bindgen::JsCast; - - let state = history.state(); - if let Err(err) = &state { - error!(err); - } - state.ok().and_then(|state| { - let state = state.dyn_into::().ok()?; - let x = state.get(0).as_f64()?; - let y = state.get(1).as_f64()?; - Some([x, y]) - }) -} diff --git a/packages/router/src/hooks/use_route.rs b/packages/router/src/hooks/use_route.rs index 3df71c3c07..4fcf1a8af5 100644 --- a/packages/router/src/hooks/use_route.rs +++ b/packages/router/src/hooks/use_route.rs @@ -42,7 +42,7 @@ use crate::utils::use_router_internal::use_router_internal; /// ``` #[doc(alias = "use_url")] #[must_use] -pub fn use_route() -> R { +pub fn use_route() -> R { match use_router_internal() { Some(r) => r.current(), None => { diff --git a/packages/router/src/into_routable.rs b/packages/router/src/into_routable.rs new file mode 100644 index 0000000000..5f063ddf4e --- /dev/null +++ b/packages/router/src/into_routable.rs @@ -0,0 +1,76 @@ +use url::Url; + +use crate::prelude::Routable; + +/// Something that can be converted into a [`NavigationTarget`]. +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct IntoRoutable(String); + +impl IntoRoutable { + fn FromStr(value: String) -> Self { + Self(value) + } + + fn Route(value: R) -> Self { + Self(value.serialize()) + } + + pub(crate) fn is_external(&self) -> bool { + todo!() + } +} + +impl From for IntoRoutable { + fn from(value: R) -> Self { + IntoRoutable::Route(value) + } +} + +// impl From> for IntoRoutable { +// fn from(value: NavigationTarget) -> Self { +// match value { +// NavigationTarget::Internal(route) => IntoRoutable::Route(Rc::new(route) as Rc), +// NavigationTarget::External(url) => IntoRoutable::FromStr(url), +// } +// } +// } + +// impl PartialEq for IntoRoutable { +// fn eq(&self, other: &Self) -> bool { +// match (self, other) { +// (IntoRoutable::FromStr(a), IntoRoutable::FromStr(b)) => a == b, +// (IntoRoutable::Route(a), IntoRoutable::Route(b)) => Rc::ptr_eq(a, b), +// _ => false, +// } +// } +// } + +impl From for IntoRoutable { + fn from(value: String) -> Self { + IntoRoutable::FromStr(value) + } +} + +impl From<&String> for IntoRoutable { + fn from(value: &String) -> Self { + IntoRoutable::FromStr(value.to_string()) + } +} + +impl From<&str> for IntoRoutable { + fn from(value: &str) -> Self { + IntoRoutable::FromStr(value.to_string()) + } +} + +impl From for IntoRoutable { + fn from(url: Url) -> Self { + IntoRoutable::FromStr(url.to_string()) + } +} + +impl From<&Url> for IntoRoutable { + fn from(url: &Url) -> Self { + IntoRoutable::FromStr(url.to_string()) + } +} diff --git a/packages/router/src/lib.rs b/packages/router/src/lib.rs index 47cf12d918..dc077a99e1 100644 --- a/packages/router/src/lib.rs +++ b/packages/router/src/lib.rs @@ -2,37 +2,22 @@ #![doc(html_logo_url = "https://avatars.githubusercontent.com/u/79236386")] #![doc(html_favicon_url = "https://avatars.githubusercontent.com/u/79236386")] // cannot use forbid, because props derive macro generates #[allow(missing_docs)] -#![deny(missing_docs)] +// #![deny(missing_docs)] #![allow(non_snake_case)] +pub mod components; +pub mod into_routable; pub mod navigation; pub mod routable; -/// Components interacting with the router. -pub mod components { - mod default_errors; - pub use default_errors::*; - - mod history_buttons; - pub use history_buttons::*; - - mod link; - pub use link::*; - - mod outlet; - pub use outlet::*; - - mod router; - pub use router::*; -} - mod contexts { + pub(crate) mod generic_router; pub(crate) mod navigator; pub(crate) mod outlet; pub(crate) mod router; pub use navigator::*; pub(crate) use router::*; - pub use router::{root_router, RouterContext}; + pub use router::*; } mod router_cfg; @@ -59,6 +44,7 @@ pub mod prelude { pub use crate::contexts::*; pub use crate::history::*; pub use crate::hooks::*; + pub use crate::into_routable::*; pub use crate::navigation::*; pub use crate::routable::*; pub use crate::router_cfg::RouterConfig; diff --git a/packages/router/src/navigation.rs b/packages/router/src/navigation.rs index 3c8a8e28de..bf902dafb4 100644 --- a/packages/router/src/navigation.rs +++ b/packages/router/src/navigation.rs @@ -11,7 +11,7 @@ use crate::routable::Routable; /// A target for the router to navigate to. #[derive(Clone, PartialEq, Eq, Debug)] -pub enum NavigationTarget { +pub enum NavigationTarget { /// An internal path that the router can navigate to by itself. /// /// ```rust @@ -32,6 +32,7 @@ pub enum NavigationTarget { /// assert_eq!(explicit, implicit); /// ``` Internal(R), + /// An external target that the router doesn't control. /// /// ```rust @@ -56,8 +57,8 @@ pub enum NavigationTarget { impl From<&str> for NavigationTarget { fn from(value: &str) -> Self { - value - .parse() + R::deserialize(value) + .map(|route| Self::Internal(route)) .unwrap_or_else(|_| Self::External(value.to_string())) } } @@ -83,43 +84,44 @@ impl From for NavigationTarget { impl Display for NavigationTarget { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - NavigationTarget::Internal(r) => write!(f, "{}", r), + NavigationTarget::Internal(r) => write!(f, "{}", r.serialize()), NavigationTarget::External(s) => write!(f, "{}", s), } } } -/// An error that can occur when parsing a [`NavigationTarget`]. -pub enum NavigationTargetParseError { - /// A URL that is not valid. - InvalidUrl(ParseError), - /// An internal URL that is not valid. - InvalidInternalURL(::Err), -} +// /// An error that can occur when parsing a [`NavigationTarget`]. +// pub enum NavigationTargetParseError { +// /// A URL that is not valid. +// InvalidUrl(ParseError), -impl Debug for NavigationTargetParseError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - NavigationTargetParseError::InvalidUrl(e) => write!(f, "Invalid URL: {}", e), - NavigationTargetParseError::InvalidInternalURL(_) => { - write!(f, "Invalid internal URL") - } - } - } -} +// /// An internal URL that is not valid. +// InvalidInternalURL(::Err), +// } -impl FromStr for NavigationTarget { - type Err = NavigationTargetParseError; +// impl Debug for NavigationTargetParseError { +// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +// match self { +// NavigationTargetParseError::InvalidUrl(e) => write!(f, "Invalid URL: {}", e), +// NavigationTargetParseError::InvalidInternalURL(_) => { +// write!(f, "Invalid internal URL") +// } +// } +// } +// } - fn from_str(s: &str) -> Result { - match Url::parse(s) { - Ok(_) => Ok(Self::External(s.to_string())), - Err(ParseError::RelativeUrlWithoutBase) => { - Ok(Self::Internal(R::from_str(s).map_err(|e| { - NavigationTargetParseError::InvalidInternalURL(e) - })?)) - } - Err(e) => Err(NavigationTargetParseError::InvalidUrl(e)), - } - } -} +// impl FromStr for NavigationTarget { +// type Err = NavigationTargetParseError; + +// fn from_str(s: &str) -> Result { +// match Url::parse(s) { +// Ok(_) => Ok(Self::External(s.to_string())), +// Err(ParseError::RelativeUrlWithoutBase) => { +// Ok(Self::Internal(R::from_str(s).map_err(|e| { +// NavigationTargetParseError::InvalidInternalURL(e) +// })?)) +// } +// Err(e) => Err(NavigationTargetParseError::InvalidUrl(e)), +// } +// } +// } diff --git a/packages/router/src/routable.rs b/packages/router/src/routable.rs index fc7acc86cf..be44a118b5 100644 --- a/packages/router/src/routable.rs +++ b/packages/router/src/routable.rs @@ -3,520 +3,18 @@ #![allow(non_snake_case)] use dioxus_lib::prelude::*; -use std::iter::FlatMap; use std::slice::Iter; +use std::{fmt::Debug, iter::FlatMap}; use std::{fmt::Display, str::FromStr}; -/// An error that occurs when parsing a route. -#[derive(Debug, PartialEq)] -pub struct RouteParseError { - /// The attempted routes that failed to match. - pub attempted_routes: Vec, -} - -impl Display for RouteParseError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Route did not match:\nAttempted Matches:\n")?; - for (i, route) in self.attempted_routes.iter().enumerate() { - writeln!(f, "{}) {route}", i + 1)?; - } - Ok(()) - } -} - -/// Something that can be created from an entire query string. This trait must be implemented for any type that is spread into the query segment like `#[route("/?:..query")]`. -/// -/// -/// **This trait is automatically implemented for any types that implement `From<&str>`.** -/// -/// ```rust -/// use dioxus::prelude::*; -/// -/// #[derive(Routable, Clone, PartialEq, Debug)] -/// enum Route { -/// // FromQuery must be implemented for any types you spread into the query segment -/// #[route("/?:..query")] -/// Home { -/// query: CustomQuery -/// }, -/// } -/// -/// #[derive(Default, Clone, PartialEq, Debug)] -/// struct CustomQuery { -/// count: i32, -/// } -/// -/// // We implement From<&str> for CustomQuery so that FromQuery is implemented automatically -/// impl From<&str> for CustomQuery { -/// fn from(query: &str) -> Self { -/// CustomQuery { -/// count: query.parse().unwrap_or(0), -/// } -/// } -/// } -/// -/// // We also need to implement Display for CustomQuery which will be used to format the query string into the URL -/// impl std::fmt::Display for CustomQuery { -/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { -/// write!(f, "{}", self.count) -/// } -/// } -/// -/// # #[component] -/// # fn Home(query: CustomQuery) -> Element { -/// # unimplemented!() -/// # } -/// ``` -#[rustversion::attr( - since(1.78.0), - diagnostic::on_unimplemented( - message = "`FromQuery` is not implemented for `{Self}`", - label = "spread query", - note = "FromQuery is automatically implemented for types that implement `From<&str>`. You need to either implement From<&str> or implement FromQuery manually." - ) -)] -pub trait FromQuery { - /// Create an instance of `Self` from a query string. - fn from_query(query: &str) -> Self; -} - -impl From<&'a str>> FromQuery for T { - fn from_query(query: &str) -> Self { - T::from(query) - } -} - -/// Something that can be created from a query argument. This trait must be implemented for any type that is used as a query argument like `#[route("/?:query")]`. -/// -/// **This trait is automatically implemented for any types that implement `FromStr` and `Default`.** -/// -/// ```rust -/// use dioxus::prelude::*; -/// -/// #[derive(Routable, Clone, PartialEq, Debug)] -/// enum Route { -/// // FromQuerySegment must be implemented for any types you use in the query segment -/// // When you don't spread the query, you can parse multiple values form the query -/// // This url will be in the format `/?query=123&other=456` -/// #[route("/?:query&:other")] -/// Home { -/// query: CustomQuery, -/// other: i32, -/// }, -/// } -/// -/// // We can derive Default for CustomQuery -/// // If the router fails to parse the query value, it will use the default value instead -/// #[derive(Default, Clone, PartialEq, Debug)] -/// struct CustomQuery { -/// count: i32, -/// } -/// -/// // We implement FromStr for CustomQuery so that FromQuerySegment is implemented automatically -/// impl std::str::FromStr for CustomQuery { -/// type Err = ::Err; -/// -/// fn from_str(query: &str) -> Result { -/// Ok(CustomQuery { -/// count: query.parse()?, -/// }) -/// } -/// } -/// -/// // We also need to implement Display for CustomQuery which will be used to format the query string into the URL -/// impl std::fmt::Display for CustomQuery { -/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { -/// write!(f, "{}", self.count) -/// } -/// } -/// -/// # #[component] -/// # fn Home(query: CustomQuery, other: i32) -> Element { -/// # unimplemented!() -/// # } -/// ``` -#[rustversion::attr( - since(1.78.0), - diagnostic::on_unimplemented( - message = "`FromQueryArgument` is not implemented for `{Self}`", - label = "query argument", - note = "FromQueryArgument is automatically implemented for types that implement `FromStr` and `Default`. You need to either implement FromStr and Default or implement FromQueryArgument manually." - ) -)] -pub trait FromQueryArgument: Default { - /// The error that can occur when parsing a query argument. - type Err; - - /// Create an instance of `Self` from a query string. - fn from_query_argument(argument: &str) -> Result; -} - -impl FromQueryArgument for T -where - ::Err: Display, -{ - type Err = ::Err; - - fn from_query_argument(argument: &str) -> Result { - match T::from_str(argument) { - Ok(result) => Ok(result), - Err(err) => { - tracing::error!("Failed to parse query argument: {}", err); - Err(err) - } - } - } -} - -/// Something that can be created from an entire hash fragment. This must be implemented for any type that is used as a hash fragment like `#[route("/#:hash_fragment")]`. -/// -/// -/// **This trait is automatically implemented for any types that implement `FromStr` and `Default`.** -/// -/// # Example -/// -/// ```rust -/// use dioxus::prelude::*; -/// -/// #[derive(Routable, Clone)] -/// #[rustfmt::skip] -/// enum Route { -/// // State is stored in the url hash -/// #[route("/#:url_hash")] -/// Home { -/// url_hash: State, -/// }, -/// } -/// -/// #[component] -/// fn Home(url_hash: State) -> Element { -/// unimplemented!() -/// } -/// -/// -/// #[derive(Clone, PartialEq, Default)] -/// struct State { -/// count: usize, -/// other_count: usize -/// } -/// -/// // The hash segment will be displayed as a string (this will be url encoded automatically) -/// impl std::fmt::Display for State { -/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { -/// write!(f, "{}-{}", self.count, self.other_count) -/// } -/// } -/// -/// // We need to parse the hash fragment into a struct from the string (this will be url decoded automatically) -/// impl FromHashFragment for State { -/// fn from_hash_fragment(hash: &str) -> Self { -/// let Some((first, second)) = hash.split_once('-') else { -/// // URL fragment parsing shouldn't fail. You can return a default value if you want -/// return Default::default(); -/// }; -/// -/// let first = first.parse().unwrap(); -/// let second = second.parse().unwrap(); -/// -/// State { -/// count: first, -/// other_count: second, -/// } -/// } -/// } -/// ``` -#[rustversion::attr( - since(1.78.0), - diagnostic::on_unimplemented( - message = "`FromHashFragment` is not implemented for `{Self}`", - label = "hash fragment", - note = "FromHashFragment is automatically implemented for types that implement `FromStr` and `Default`. You need to either implement FromStr and Default or implement FromHashFragment manually." - ) -)] -pub trait FromHashFragment { - /// Create an instance of `Self` from a hash fragment. - fn from_hash_fragment(hash: &str) -> Self; -} - -impl FromHashFragment for T -where - T: FromStr + Default, - T::Err: std::fmt::Display, -{ - fn from_hash_fragment(hash: &str) -> Self { - match T::from_str(hash) { - Ok(value) => value, - Err(err) => { - tracing::error!("Failed to parse hash fragment: {}", err); - Default::default() - } - } - } -} - -/// Something that can be created from a single route segment. This must be implemented for any type that is used as a route segment like `#[route("/:route_segment")]`. -/// -/// -/// **This trait is automatically implemented for any types that implement `FromStr` and `Default`.** -/// -/// ```rust -/// use dioxus::prelude::*; -/// -/// #[derive(Routable, Clone, PartialEq, Debug)] -/// enum Route { -/// // FromRouteSegment must be implemented for any types you use in the route segment -/// // When you don't spread the route, you can parse multiple values from the route -/// // This url will be in the format `/123/456` -/// #[route("/:route_segment_one/:route_segment_two")] -/// Home { -/// route_segment_one: CustomRouteSegment, -/// route_segment_two: i32, -/// }, -/// } -/// -/// // We can derive Default for CustomRouteSegment -/// // If the router fails to parse the route segment, it will use the default value instead -/// #[derive(Default, PartialEq, Clone, Debug)] -/// struct CustomRouteSegment { -/// count: i32, -/// } -/// -/// // We implement FromStr for CustomRouteSegment so that FromRouteSegment is implemented automatically -/// impl std::str::FromStr for CustomRouteSegment { -/// type Err = ::Err; -/// -/// fn from_str(route_segment: &str) -> Result { -/// Ok(CustomRouteSegment { -/// count: route_segment.parse()?, -/// }) -/// } -/// } -/// -/// // We also need to implement Display for CustomRouteSegment which will be used to format the route segment into the URL -/// impl std::fmt::Display for CustomRouteSegment { -/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { -/// write!(f, "{}", self.count) -/// } -/// } -/// -/// # #[component] -/// # fn Home(route_segment_one: CustomRouteSegment, route_segment_two: i32) -> Element { -/// # unimplemented!() -/// # } -/// ``` -#[rustversion::attr( - since(1.78.0), - diagnostic::on_unimplemented( - message = "`FromRouteSegment` is not implemented for `{Self}`", - label = "route segment", - note = "FromRouteSegment is automatically implemented for types that implement `FromStr` and `Default`. You need to either implement FromStr and Default or implement FromRouteSegment manually." - ) -)] -pub trait FromRouteSegment: Sized { - /// The error that can occur when parsing a route segment. - type Err; - - /// Create an instance of `Self` from a route segment. - fn from_route_segment(route: &str) -> Result; -} - -impl FromRouteSegment for T -where - ::Err: Display, -{ - type Err = ::Err; - - fn from_route_segment(route: &str) -> Result { - T::from_str(route) - } -} - -#[test] -fn full_circle() { - let route = "testing 1234 hello world"; - assert_eq!(String::from_route_segment(route).unwrap(), route); -} - -/// Something that can be converted into multiple route segments. This must be implemented for any type that is spread into the route segment like `#[route("/:..route_segments")]`. -/// -/// -/// **This trait is automatically implemented for any types that implement `IntoIterator`.** -/// -/// ```rust -/// use dioxus::prelude::*; -/// -/// #[derive(Routable, Clone, PartialEq, Debug)] -/// enum Route { -/// // FromRouteSegments must be implemented for any types you use in the route segment -/// // When you spread the route, you can parse multiple values from the route -/// // This url will be in the format `/123/456/789` -/// #[route("/:..numeric_route_segments")] -/// Home { -/// numeric_route_segments: NumericRouteSegments, -/// }, -/// } -/// -/// // We can derive Default for NumericRouteSegments -/// // If the router fails to parse the route segment, it will use the default value instead -/// #[derive(Default, PartialEq, Clone, Debug)] -/// struct NumericRouteSegments { -/// numbers: Vec, -/// } -/// -/// // Implement ToRouteSegments for NumericRouteSegments so that we can display the route segments -/// impl ToRouteSegments for NumericRouteSegments { -/// fn display_route_segments(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { -/// for number in &self.numbers { -/// write!(f, "/{}", number)?; -/// } -/// Ok(()) -/// } -/// } -/// -/// // We also need to parse the route segments with `FromRouteSegments` -/// impl FromRouteSegments for NumericRouteSegments { -/// type Err = ::Err; -/// -/// fn from_route_segments(segments: &[&str]) -> Result { -/// let mut numbers = Vec::new(); -/// for segment in segments { -/// numbers.push(segment.parse()?); -/// } -/// Ok(NumericRouteSegments { numbers }) -/// } -/// } -/// -/// # #[component] -/// # fn Home(numeric_route_segments: NumericRouteSegments) -> Element { -/// # unimplemented!() -/// # } -/// ``` -pub trait ToRouteSegments { - /// Display the route segments with each route segment separated by a `/`. This should not start with a `/`. - /// - fn display_route_segments(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result; -} - -// Implement ToRouteSegments for any type that can turn &self into an iterator of &T where T: Display -impl ToRouteSegments for I -where - for<'a> &'a I: IntoIterator, -{ - fn display_route_segments(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - for segment in self { - write!(f, "/")?; - let segment = segment.to_string(); - let encoded = urlencoding::encode(&segment); - write!(f, "{}", encoded)?; - } - Ok(()) - } -} - -#[test] -fn to_route_segments() { - struct DisplaysRoute; - - impl Display for DisplaysRoute { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let segments = vec!["hello", "world"]; - segments.display_route_segments(f) - } - } - - assert_eq!(DisplaysRoute.to_string(), "/hello/world"); -} - -/// Something that can be created from multiple route segments. This must be implemented for any type that is spread into the route segment like `#[route("/:..route_segments")]`. -/// -/// -/// **This trait is automatically implemented for any types that implement `FromIterator`.** -/// -/// ```rust -/// use dioxus::prelude::*; -/// -/// #[derive(Routable, Clone, PartialEq, Debug)] -/// enum Route { -/// // FromRouteSegments must be implemented for any types you use in the route segment -/// // When you spread the route, you can parse multiple values from the route -/// // This url will be in the format `/123/456/789` -/// #[route("/:..numeric_route_segments")] -/// Home { -/// numeric_route_segments: NumericRouteSegments, -/// }, -/// } -/// -/// // We can derive Default for NumericRouteSegments -/// // If the router fails to parse the route segment, it will use the default value instead -/// #[derive(Default, Clone, PartialEq, Debug)] -/// struct NumericRouteSegments { -/// numbers: Vec, -/// } -/// -/// // Implement ToRouteSegments for NumericRouteSegments so that we can display the route segments -/// impl ToRouteSegments for NumericRouteSegments { -/// fn display_route_segments(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { -/// for number in &self.numbers { -/// write!(f, "/{}", number)?; -/// } -/// Ok(()) -/// } -/// } -/// -/// // We also need to parse the route segments with `FromRouteSegments` -/// impl FromRouteSegments for NumericRouteSegments { -/// type Err = ::Err; -/// -/// fn from_route_segments(segments: &[&str]) -> Result { -/// let mut numbers = Vec::new(); -/// for segment in segments { -/// numbers.push(segment.parse()?); -/// } -/// Ok(NumericRouteSegments { numbers }) -/// } -/// } -/// -/// # #[component] -/// # fn Home(numeric_route_segments: NumericRouteSegments) -> Element { -/// # unimplemented!() -/// # } -/// ``` -#[rustversion::attr( - since(1.78.0), - diagnostic::on_unimplemented( - message = "`FromRouteSegments` is not implemented for `{Self}`", - label = "spread route segments", - note = "FromRouteSegments is automatically implemented for types that implement `FromIterator` with an `Item` type that implements `Display`. You need to either implement FromIterator or implement FromRouteSegments manually." - ) -)] -pub trait FromRouteSegments: Sized { - /// The error that can occur when parsing route segments. - type Err: std::fmt::Display; - - /// Create an instance of `Self` from route segments. - /// - /// NOTE: This method must parse the output of `ToRouteSegments::display_route_segments` into the type `Self`. - fn from_route_segments(segments: &[&str]) -> Result; -} - -impl> FromRouteSegments for I { - type Err = ::Err; - - fn from_route_segments(segments: &[&str]) -> Result { - segments - .iter() - .map(|s| String::from_route_segment(s)) - .collect() - } -} - -/// A flattened version of [`Routable::SITE_MAP`]. -/// This essentially represents a `Vec>`, which you can collect it into. -type SiteMapFlattened<'a> = FlatMap< - Iter<'a, SiteMapSegment>, - Vec>, - fn(&SiteMapSegment) -> Vec>, ->; +mod hash; +mod query; +mod segments; +mod sitemap; +pub use hash::*; +pub use query::*; +pub use segments::*; +pub use sitemap::*; /// The Routable trait is implemented for types that can be converted to and from a route and be rendered as a page. /// @@ -543,12 +41,15 @@ type SiteMapFlattened<'a> = FlatMap< /// // Each enum has an associated url /// #[route("/")] /// Home {}, +/// /// // Routes can include dynamic segments that are parsed from the url /// #[route("/blog/:blog_id")] /// Blog { blog_id: usize }, +/// /// // Or query segments that are parsed from the url /// #[route("/edit?:blog_id")] /// Edit { blog_id: usize }, +/// /// // Or hash segments that are parsed from the url /// #[route("/hashtag/#:hash")] /// Hash { hash: String }, @@ -592,14 +93,24 @@ type SiteMapFlattened<'a> = FlatMap< note = "Routable should generally be derived using the `#[derive(Routable)]` macro." ) )] -pub trait Routable: FromStr + Display + Clone + 'static { +pub trait Routable: Clone + Sized + 'static { /// The error that can occur when parsing a route. const SITE_MAP: &'static [SiteMapSegment]; /// Render the route at the given level fn render(&self, level: usize) -> Element; - /// Checks if this route is a child of the given route. + /// Turn this route into a string so we can parse it again later + /// + /// The format here is expected to be a Path like `/about/123` or `https://example.com/about` + fn serialize(&self) -> String; + + /// Turn a string into this route, or return an error if it can't be parsed + /// + /// The format here is expected to be a Path like `/about/123` or `https://example.com/about` + fn deserialize(route: &str) -> Result>; + + /// Get the parent route of this route. /// /// # Example /// ```rust @@ -613,35 +124,33 @@ pub trait Routable: FromStr + Display + Clone + 'static { /// /// #[derive(Routable, Clone, PartialEq, Debug)] /// enum Route { - /// #[route("/")] + /// #[route("/home")] /// Home {}, - /// #[route("/about")] + /// #[route("/home/about")] /// About {}, /// } /// /// let route = Route::About {}; - /// let parent = Route::Home {}; - /// assert!(route.is_child_of(&parent)); + /// let parent = route.parent().unwrap(); + /// assert_eq!(parent, Route::Home {}); /// ``` - fn is_child_of(&self, other: &Self) -> bool { - let self_str = self.to_string(); - let self_str = self_str.trim_matches('/'); - let other_str = other.to_string(); - let other_str = other_str.trim_matches('/'); - if other_str.is_empty() { - return true; - } - let self_segments = self_str.split('/'); - let other_segments = other_str.split('/'); - for (self_seg, other_seg) in self_segments.zip(other_segments) { - if self_seg != other_seg { - return false; - } - } - true + fn parent(&self) -> Option { + let as_str = self.serialize(); + let as_str = as_str.trim_matches('/'); + let segments = as_str.split('/'); + let segment_count = segments.clone().count(); + let new_route = segments + .take(segment_count - 1) + .fold(String::new(), |mut acc, segment| { + acc.push('/'); + acc.push_str(segment); + acc + }); + + Self::deserialize(&new_route).ok() } - /// Get the parent route of this route. + /// Checks if this route is a child of the given route. /// /// # Example /// ```rust @@ -655,30 +164,32 @@ pub trait Routable: FromStr + Display + Clone + 'static { /// /// #[derive(Routable, Clone, PartialEq, Debug)] /// enum Route { - /// #[route("/home")] + /// #[route("/")] /// Home {}, - /// #[route("/home/about")] + /// #[route("/about")] /// About {}, /// } /// /// let route = Route::About {}; - /// let parent = route.parent().unwrap(); - /// assert_eq!(parent, Route::Home {}); + /// let parent = Route::Home {}; + /// assert!(route.is_child_of(&parent)); /// ``` - fn parent(&self) -> Option { - let as_str = self.to_string(); - let as_str = as_str.trim_matches('/'); - let segments = as_str.split('/'); - let segment_count = segments.clone().count(); - let new_route = segments - .take(segment_count - 1) - .fold(String::new(), |mut acc, segment| { - acc.push('/'); - acc.push_str(segment); - acc - }); - - Self::from_str(&new_route).ok() + fn is_child_of(&self, other: &Self) -> bool { + let self_str = self.serialize(); + let self_str = self_str.trim_matches('/'); + let other_str = other.serialize(); + let other_str = other_str.trim_matches('/'); + if other_str.is_empty() { + return true; + } + let self_segments = self_str.split('/'); + let other_segments = other_str.split('/'); + for (self_seg, other_seg) in self_segments.zip(other_segments) { + if self_seg != other_seg { + return false; + } + } + true } /// Returns a flattened version of [`Self::SITE_MAP`]. @@ -703,12 +214,122 @@ pub trait Routable: FromStr + Display + Clone + 'static { } } - route.parse().ok() + Self::deserialize(&route).ok() }) .collect() } } +/// An error that occurs when parsing a route. +#[derive(Debug, PartialEq)] +pub struct RouteParseError { + /// The attempted routes that failed to match. + pub attempted_routes: Vec, +} + +impl std::error::Error for RouteParseError {} + +impl Display for RouteParseError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Route did not match:\nAttempted Matches:\n")?; + for (i, route) in self.attempted_routes.iter().enumerate() { + writeln!(f, "{}) {route}", i + 1)?; + } + Ok(()) + } +} + +/// Something that can be created from multiple route segments. This must be implemented for any type that is spread into the route segment like `#[route("/:..route_segments")]`. +/// +/// +/// **This trait is automatically implemented for any types that implement `FromIterator`.** +/// +/// ```rust +/// use dioxus::prelude::*; +/// +/// #[derive(Routable, Clone, PartialEq, Debug)] +/// enum Route { +/// // FromRouteSegments must be implemented for any types you use in the route segment +/// // When you spread the route, you can parse multiple values from the route +/// // This url will be in the format `/123/456/789` +/// #[route("/:..numeric_route_segments")] +/// Home { +/// numeric_route_segments: NumericRouteSegments, +/// }, +/// } +/// +/// // We can derive Default for NumericRouteSegments +/// // If the router fails to parse the route segment, it will use the default value instead +/// #[derive(Default, Clone, PartialEq, Debug)] +/// struct NumericRouteSegments { +/// numbers: Vec, +/// } +/// +/// // Implement ToRouteSegments for NumericRouteSegments so that we can display the route segments +/// impl ToRouteSegments for NumericRouteSegments { +/// fn display_route_segments(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +/// for number in &self.numbers { +/// write!(f, "/{}", number)?; +/// } +/// Ok(()) +/// } +/// } +/// +/// // We also need to parse the route segments with `FromRouteSegments` +/// impl FromRouteSegments for NumericRouteSegments { +/// type Err = ::Err; +/// +/// fn from_route_segments(segments: &[&str]) -> Result { +/// let mut numbers = Vec::new(); +/// for segment in segments { +/// numbers.push(segment.parse()?); +/// } +/// Ok(NumericRouteSegments { numbers }) +/// } +/// } +/// +/// # #[component] +/// # fn Home(numeric_route_segments: NumericRouteSegments) -> Element { +/// # unimplemented!() +/// # } +/// ``` +#[rustversion::attr( + since(1.78.0), + diagnostic::on_unimplemented( + message = "`FromRouteSegments` is not implemented for `{Self}`", + label = "spread route segments", + note = "FromRouteSegments is automatically implemented for types that implement `FromIterator` with an `Item` type that implements `Display`. You need to either implement FromIterator or implement FromRouteSegments manually." + ) +)] +pub trait FromRouteSegments: Sized { + /// The error that can occur when parsing route segments. + type Err: std::fmt::Display; + + /// Create an instance of `Self` from route segments. + /// + /// NOTE: This method must parse the output of `ToRouteSegments::display_route_segments` into the type `Self`. + fn from_route_segments(segments: &[&str]) -> Result; +} + +impl> FromRouteSegments for I { + type Err = ::Err; + + fn from_route_segments(segments: &[&str]) -> Result { + segments + .iter() + .map(|s| String::from_route_segment(s)) + .collect() + } +} + +/// A flattened version of [`Routable::SITE_MAP`]. +/// This essentially represents a `Vec>`, which you can collect it into. +type SiteMapFlattened<'a> = FlatMap< + Iter<'a, SiteMapSegment>, + Vec>, + fn(&SiteMapSegment) -> Vec>, +>; + /// A type erased map of the site structure. #[derive(Debug, Clone, PartialEq)] pub struct SiteMapSegment { diff --git a/packages/router/src/routable/hash.rs b/packages/router/src/routable/hash.rs new file mode 100644 index 0000000000..b4079fa194 --- /dev/null +++ b/packages/router/src/routable/hash.rs @@ -0,0 +1,89 @@ +use std::iter::FlatMap; +use std::slice::Iter; +use std::{fmt::Display, str::FromStr}; + +/// Something that can be created from an entire hash fragment. This must be implemented for any type that is used as a hash fragment like `#[route("/#:hash_fragment")]`. +/// +/// +/// **This trait is automatically implemented for any types that implement `FromStr` and `Default`.** +/// +/// # Example +/// +/// ```rust +/// use dioxus::prelude::*; +/// +/// #[derive(Routable, Clone)] +/// #[rustfmt::skip] +/// enum Route { +/// // State is stored in the url hash +/// #[route("/#:url_hash")] +/// Home { +/// url_hash: State, +/// }, +/// } +/// +/// #[component] +/// fn Home(url_hash: State) -> Element { +/// unimplemented!() +/// } +/// +/// +/// #[derive(Clone, PartialEq, Default)] +/// struct State { +/// count: usize, +/// other_count: usize +/// } +/// +/// // The hash segment will be displayed as a string (this will be url encoded automatically) +/// impl std::fmt::Display for State { +/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +/// write!(f, "{}-{}", self.count, self.other_count) +/// } +/// } +/// +/// // We need to parse the hash fragment into a struct from the string (this will be url decoded automatically) +/// impl FromHashFragment for State { +/// fn from_hash_fragment(hash: &str) -> Self { +/// let Some((first, second)) = hash.split_once('-') else { +/// // URL fragment parsing shouldn't fail. You can return a default value if you want +/// return Default::default(); +/// }; +/// +/// let first = first.parse().unwrap(); +/// let second = second.parse().unwrap(); +/// +/// State { +/// count: first, +/// other_count: second, +/// } +/// } +/// } +/// ``` +#[rustversion::attr( + since(1.78.0), + diagnostic::on_unimplemented( + message = "`FromHashFragment` is not implemented for `{Self}`", + label = "hash fragment", + note = "FromHashFragment is automatically implemented for types that implement `FromStr` and `Default`. You need to either implement FromStr and Default or implement FromHashFragment manually." + ) +)] +pub trait FromHashFragment { + /// Create an instance of `Self` from a hash fragment. + fn from_hash_fragment(hash: &str) -> Self; +} + +impl FromHashFragment for T +where + T: FromStr + Default, + T::Err: std::fmt::Display, +{ + fn from_hash_fragment(hash: &str) -> Self { + match T::from_str(hash) { + Ok(value) => value, + Err(err) => { + tracing::error!("Failed to parse hash fragment: {}", err); + Default::default() + } + } + } +} diff --git a/packages/router/src/routable/query.rs b/packages/router/src/routable/query.rs new file mode 100644 index 0000000000..44f874667d --- /dev/null +++ b/packages/router/src/routable/query.rs @@ -0,0 +1,147 @@ +use std::iter::FlatMap; +use std::slice::Iter; +use std::{fmt::Display, str::FromStr}; + +/// Something that can be created from an entire query string. This trait must be implemented for any type that is spread into the query segment like `#[route("/?:..query")]`. +/// +/// +/// **This trait is automatically implemented for any types that implement `From<&str>`.** +/// +/// ```rust +/// use dioxus::prelude::*; +/// +/// #[derive(Routable, Clone, PartialEq, Debug)] +/// enum Route { +/// // FromQuery must be implemented for any types you spread into the query segment +/// #[route("/?:..query")] +/// Home { +/// query: CustomQuery +/// }, +/// } +/// +/// #[derive(Default, Clone, PartialEq, Debug)] +/// struct CustomQuery { +/// count: i32, +/// } +/// +/// // We implement From<&str> for CustomQuery so that FromQuery is implemented automatically +/// impl From<&str> for CustomQuery { +/// fn from(query: &str) -> Self { +/// CustomQuery { +/// count: query.parse().unwrap_or(0), +/// } +/// } +/// } +/// +/// // We also need to implement Display for CustomQuery which will be used to format the query string into the URL +/// impl std::fmt::Display for CustomQuery { +/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +/// write!(f, "{}", self.count) +/// } +/// } +/// +/// # #[component] +/// # fn Home(query: CustomQuery) -> Element { +/// # unimplemented!() +/// # } +/// ``` +#[rustversion::attr( + since(1.78.0), + diagnostic::on_unimplemented( + message = "`FromQuery` is not implemented for `{Self}`", + label = "spread query", + note = "FromQuery is automatically implemented for types that implement `From<&str>`. You need to either implement From<&str> or implement FromQuery manually." + ) +)] +pub trait FromQuery { + /// Create an instance of `Self` from a query string. + fn from_query(query: &str) -> Self; +} + +impl From<&'a str>> FromQuery for T { + fn from_query(query: &str) -> Self { + T::from(query) + } +} + +/// Something that can be created from a query argument. This trait must be implemented for any type that is used as a query argument like `#[route("/?:query")]`. +/// +/// **This trait is automatically implemented for any types that implement `FromStr` and `Default`.** +/// +/// ```rust +/// use dioxus::prelude::*; +/// +/// #[derive(Routable, Clone, PartialEq, Debug)] +/// enum Route { +/// // FromQuerySegment must be implemented for any types you use in the query segment +/// // When you don't spread the query, you can parse multiple values form the query +/// // This url will be in the format `/?query=123&other=456` +/// #[route("/?:query&:other")] +/// Home { +/// query: CustomQuery, +/// other: i32, +/// }, +/// } +/// +/// // We can derive Default for CustomQuery +/// // If the router fails to parse the query value, it will use the default value instead +/// #[derive(Default, Clone, PartialEq, Debug)] +/// struct CustomQuery { +/// count: i32, +/// } +/// +/// // We implement FromStr for CustomQuery so that FromQuerySegment is implemented automatically +/// impl std::str::FromStr for CustomQuery { +/// type Err = ::Err; +/// +/// fn from_str(query: &str) -> Result { +/// Ok(CustomQuery { +/// count: query.parse()?, +/// }) +/// } +/// } +/// +/// // We also need to implement Display for CustomQuery which will be used to format the query string into the URL +/// impl std::fmt::Display for CustomQuery { +/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +/// write!(f, "{}", self.count) +/// } +/// } +/// +/// # #[component] +/// # fn Home(query: CustomQuery, other: i32) -> Element { +/// # unimplemented!() +/// # } +/// ``` +#[rustversion::attr( + since(1.78.0), + diagnostic::on_unimplemented( + message = "`FromQueryArgument` is not implemented for `{Self}`", + label = "query argument", + note = "FromQueryArgument is automatically implemented for types that implement `FromStr` and `Default`. You need to either implement FromStr and Default or implement FromQueryArgument manually." + ) +)] +pub trait FromQueryArgument: Default { + /// The error that can occur when parsing a query argument. + type Err; + + /// Create an instance of `Self` from a query string. + fn from_query_argument(argument: &str) -> Result; +} + +impl FromQueryArgument for T +where + ::Err: Display, +{ + type Err = ::Err; + + fn from_query_argument(argument: &str) -> Result { + match T::from_str(argument) { + Ok(result) => Ok(result), + Err(err) => { + tracing::error!("Failed to parse query argument: {}", err); + Err(err) + } + } + } +} diff --git a/packages/router/src/routable/segments.rs b/packages/router/src/routable/segments.rs new file mode 100644 index 0000000000..1e372443f0 --- /dev/null +++ b/packages/router/src/routable/segments.rs @@ -0,0 +1,178 @@ +use dioxus_lib::prelude::*; + +use std::iter::FlatMap; +use std::slice::Iter; +use std::{fmt::Display, str::FromStr}; + +/// Something that can be created from a single route segment. This must be implemented for any type that is used as a route segment like `#[route("/:route_segment")]`. +/// +/// +/// **This trait is automatically implemented for any types that implement `FromStr` and `Default`.** +/// +/// ```rust +/// use dioxus::prelude::*; +/// +/// #[derive(Routable, Clone, PartialEq, Debug)] +/// enum Route { +/// // FromRouteSegment must be implemented for any types you use in the route segment +/// // When you don't spread the route, you can parse multiple values from the route +/// // This url will be in the format `/123/456` +/// #[route("/:route_segment_one/:route_segment_two")] +/// Home { +/// route_segment_one: CustomRouteSegment, +/// route_segment_two: i32, +/// }, +/// } +/// +/// // We can derive Default for CustomRouteSegment +/// // If the router fails to parse the route segment, it will use the default value instead +/// #[derive(Default, PartialEq, Clone, Debug)] +/// struct CustomRouteSegment { +/// count: i32, +/// } +/// +/// // We implement FromStr for CustomRouteSegment so that FromRouteSegment is implemented automatically +/// impl std::str::FromStr for CustomRouteSegment { +/// type Err = ::Err; +/// +/// fn from_str(route_segment: &str) -> Result { +/// Ok(CustomRouteSegment { +/// count: route_segment.parse()?, +/// }) +/// } +/// } +/// +/// // We also need to implement Display for CustomRouteSegment which will be used to format the route segment into the URL +/// impl std::fmt::Display for CustomRouteSegment { +/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +/// write!(f, "{}", self.count) +/// } +/// } +/// +/// # #[component] +/// # fn Home(route_segment_one: CustomRouteSegment, route_segment_two: i32) -> Element { +/// # unimplemented!() +/// # } +/// ``` +#[rustversion::attr( + since(1.78.0), + diagnostic::on_unimplemented( + message = "`FromRouteSegment` is not implemented for `{Self}`", + label = "route segment", + note = "FromRouteSegment is automatically implemented for types that implement `FromStr` and `Default`. You need to either implement FromStr and Default or implement FromRouteSegment manually." + ) +)] +pub trait FromRouteSegment: Sized { + /// The error that can occur when parsing a route segment. + type Err; + + /// Create an instance of `Self` from a route segment. + fn from_route_segment(route: &str) -> Result; +} + +impl FromRouteSegment for T +where + ::Err: Display, +{ + type Err = ::Err; + + fn from_route_segment(route: &str) -> Result { + T::from_str(route) + } +} + +#[test] +fn full_circle() { + let route = "testing 1234 hello world"; + assert_eq!(String::from_route_segment(route).unwrap(), route); +} + +/// Something that can be converted into multiple route segments. This must be implemented for any type that is spread into the route segment like `#[route("/:..route_segments")]`. +/// +/// +/// **This trait is automatically implemented for any types that implement `IntoIterator`.** +/// +/// ```rust +/// use dioxus::prelude::*; +/// +/// #[derive(Routable, Clone, PartialEq, Debug)] +/// enum Route { +/// // FromRouteSegments must be implemented for any types you use in the route segment +/// // When you spread the route, you can parse multiple values from the route +/// // This url will be in the format `/123/456/789` +/// #[route("/:..numeric_route_segments")] +/// Home { +/// numeric_route_segments: NumericRouteSegments, +/// }, +/// } +/// +/// // We can derive Default for NumericRouteSegments +/// // If the router fails to parse the route segment, it will use the default value instead +/// #[derive(Default, PartialEq, Clone, Debug)] +/// struct NumericRouteSegments { +/// numbers: Vec, +/// } +/// +/// // Implement ToRouteSegments for NumericRouteSegments so that we can display the route segments +/// impl ToRouteSegments for NumericRouteSegments { +/// fn display_route_segments(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +/// for number in &self.numbers { +/// write!(f, "/{}", number)?; +/// } +/// Ok(()) +/// } +/// } +/// +/// // We also need to parse the route segments with `FromRouteSegments` +/// impl FromRouteSegments for NumericRouteSegments { +/// type Err = ::Err; +/// +/// fn from_route_segments(segments: &[&str]) -> Result { +/// let mut numbers = Vec::new(); +/// for segment in segments { +/// numbers.push(segment.parse()?); +/// } +/// Ok(NumericRouteSegments { numbers }) +/// } +/// } +/// +/// # #[component] +/// # fn Home(numeric_route_segments: NumericRouteSegments) -> Element { +/// # unimplemented!() +/// # } +/// ``` +pub trait ToRouteSegments { + /// Display the route segments with each route segment separated by a `/`. This should not start with a `/`. + /// + fn display_route_segments(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result; +} + +// Implement ToRouteSegments for any type that can turn &self into an iterator of &T where T: Display +impl ToRouteSegments for I +where + for<'a> &'a I: IntoIterator, +{ + fn display_route_segments(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for segment in self { + write!(f, "/")?; + let segment = segment.to_string(); + let encoded = urlencoding::encode(&segment); + write!(f, "{}", encoded)?; + } + Ok(()) + } +} + +#[test] +fn to_route_segments() { + struct DisplaysRoute; + + impl Display for DisplaysRoute { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let segments = vec!["hello", "world"]; + segments.display_route_segments(f) + } + } + + assert_eq!(DisplaysRoute.to_string(), "/hello/world"); +} diff --git a/packages/router/src/routable/sitemap.rs b/packages/router/src/routable/sitemap.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/router/src/router_cfg.rs b/packages/router/src/router_cfg.rs index 9a1851539f..3647db7a50 100644 --- a/packages/router/src/router_cfg.rs +++ b/packages/router/src/router_cfg.rs @@ -1,5 +1,7 @@ use crate::prelude::*; +use dioxus_document::DocumentContext; use dioxus_lib::prelude::*; +use generic_router::GenericRouterContext; use std::sync::Arc; /// Global configuration options for the router. @@ -19,14 +21,14 @@ use std::sync::Arc; /// } /// let cfg = RouterConfig::default().history(MemoryHistory::::default()); /// ``` -pub struct RouterConfig { +pub struct RouterConfig { pub(crate) failure_external_navigation: fn() -> Element, - pub(crate) history: Option>, + pub(crate) history: Option, pub(crate) on_update: Option>, pub(crate) initial_route: Option, } -impl Default for RouterConfig { +impl Default for RouterConfig { fn default() -> Self { Self { failure_external_navigation: FailureExternalNavigation, @@ -37,26 +39,26 @@ impl Default for RouterConfig { } } -impl RouterConfig -where - ::Err: std::fmt::Display, -{ - pub(crate) fn take_history(&mut self) -> Box { - self.history - .take() - .unwrap_or_else(|| { - let initial_route = self.initial_route.clone().unwrap_or_else(|| "/".parse().unwrap_or_else(|err| - panic!("index route does not exist:\n{}\n use MemoryHistory::with_initial_path or RouterConfig::initial_route to set a custom path", err) - )); - default_history(initial_route) - }) - } -} +// impl RouterConfig +// where +// ::Err: std::fmt::Display, +// { +// pub(crate) fn take_history(&mut self) -> Box { +// self.history +// .take() +// .unwrap_or_else(|| { +// let initial_route = self.initial_route.clone().unwrap_or_else(|| "/".parse().unwrap_or_else(|err| +// panic!("index route does not exist:\n{}\n use MemoryHistory::with_initial_path or RouterConfig::initial_route to set a custom path", err) +// )); +// default_history(initial_route) +// }) +// } +// } impl RouterConfig where R: Routable, - ::Err: std::fmt::Display, + // ::Err: std::fmt::Display, { /// A function to be called whenever the routing is updated. /// @@ -81,16 +83,23 @@ where } } - /// The [`HistoryProvider`] the router should use. - /// - /// Defaults to a different history provider depending on the target platform. - pub fn history(self, history: impl HistoryProvider + 'static) -> Self { + pub fn with_initial_path(self, initial_path: R) -> Self { Self { - history: Some(Box::new(AnyHistoryProviderImplWrapper::new(history))), + initial_route: Some(initial_path), ..self } } + // /// The [`HistoryProvider`] the router should use. + // /// + // /// Defaults to a different history provider depending on the target platform. + // pub fn history(self, history: impl HistoryProvider + 'static) -> Self { + // Self { + // history: Some(Box::new(AnyHistoryProviderImplWrapper::new(history))), + // ..self + // } + // } + /// The initial route the router should use if no history provider is set. pub fn initial_route(self, route: R) -> Self { Self { @@ -110,46 +119,47 @@ where } } -/// Get the default history provider for the current platform. -#[allow(unreachable_code, unused)] -fn default_history(initial_route: R) -> Box -where - ::Err: std::fmt::Display, -{ - // If we're on the web and have wasm, use the web history provider +// /// Get the default history provider for the current platform. +// #[allow(unreachable_code, unused)] +// fn default_history(initial_route: R) -> Box +// where +// ::Err: std::fmt::Display, +// { +// todo!() +// // If we're on the web and have wasm, use the web history provider - #[cfg(all(target_arch = "wasm32", feature = "web"))] - return Box::new(AnyHistoryProviderImplWrapper::new( - WebHistory::::default(), - )); +// #[cfg(all(target_arch = "wasm32", feature = "web"))] +// return Box::new(AnyHistoryProviderImplWrapper::new( +// WebHistory::::default(), +// )); - // If we're using fullstack and server side rendering, use the memory history provider - #[cfg(all(feature = "fullstack", feature = "ssr"))] - return Box::new(AnyHistoryProviderImplWrapper::new( - MemoryHistory::::with_initial_path( - dioxus_fullstack::prelude::server_context() - .request_parts() - .uri - .to_string() - .parse() - .unwrap_or_else(|err| { - tracing::error!("Failed to parse uri: {}", err); - "/".parse().unwrap_or_else(|err| { - panic!("Failed to parse uri: {}", err); - }) - }), - ), - )); +// // If we're using fullstack and server side rendering, use the memory history provider +// #[cfg(all(feature = "fullstack", feature = "ssr"))] +// return Box::new(AnyHistoryProviderImplWrapper::new( +// MemoryHistory::::with_initial_path( +// dioxus_fullstack::prelude::server_context() +// .request_parts() +// .uri +// .to_string() +// .parse() +// .unwrap_or_else(|err| { +// tracing::error!("Failed to parse uri: {}", err); +// "/".parse().unwrap_or_else(|err| { +// panic!("Failed to parse uri: {}", err); +// }) +// }), +// ), +// )); - // If liveview is enabled, use the liveview history provider - #[cfg(feature = "liveview")] - return Box::new(AnyHistoryProviderImplWrapper::new( - LiveviewHistory::new_with_initial_path(initial_route), - )); +// // If liveview is enabled, use the liveview history provider +// #[cfg(feature = "liveview")] +// return Box::new(AnyHistoryProviderImplWrapper::new( +// LiveviewHistory::new_with_initial_path(initial_route), +// )); - // If none of the above, use the memory history provider, which is a decent enough fallback - // Eventually we want to integrate with the mobile history provider, and other platform providers - Box::new(AnyHistoryProviderImplWrapper::new( - MemoryHistory::with_initial_path(initial_route), - )) -} +// // If none of the above, use the memory history provider, which is a decent enough fallback +// // Eventually we want to integrate with the mobile history provider, and other platform providers +// Box::new(AnyHistoryProviderImplWrapper::new( +// MemoryHistory::with_initial_path(initial_route), +// )) +// } diff --git a/packages/router/tests/site_map.rs b/packages/router/tests/site_map.rs index 3252567890..37a318e77b 100644 --- a/packages/router/tests/site_map.rs +++ b/packages/router/tests/site_map.rs @@ -6,6 +6,7 @@ fn with_class() { enum ChildRoute { #[route("/")] ChildRoot {}, + #[route("/:not_static")] NotStatic { not_static: String }, } @@ -14,8 +15,10 @@ fn with_class() { enum Route { #[route("/")] Root {}, + #[route("/test")] Test {}, + #[child("/child")] Nested { child: ChildRoute }, } diff --git a/packages/router/tests/via_ssr/link.rs b/packages/router/tests/via_ssr/link.rs index 0cdfb92eb9..b6fe9f53d2 100644 --- a/packages/router/tests/via_ssr/link.rs +++ b/packages/router/tests/via_ssr/link.rs @@ -1,10 +1,6 @@ use dioxus::prelude::*; -use std::str::FromStr; -fn prepare() -> String -where - ::Err: std::fmt::Display, -{ +fn prepare() -> String { let mut vdom = VirtualDom::new_with_props( App, AppProps:: { @@ -34,15 +30,10 @@ where } #[allow(non_snake_case)] - fn App(_props: AppProps) -> Element - where - ::Err: std::fmt::Display, - { + fn App(_props: AppProps) -> Element { rsx! { h1 { "App" } - Router:: { - config: |_| RouterConfig::default().history(MemoryHistory::default()) - } + Router:: { config: |_| RouterConfig::default() } } } } @@ -65,10 +56,7 @@ fn href_internal() { #[component] fn Root() -> Element { rsx! { - Link { - to: Route::Test {}, - "Link" - } + Link { to: Route::Test {}, "Link" } } } @@ -95,10 +83,7 @@ fn href_external() { #[component] fn Root() -> Element { rsx! { - Link { - to: "https://dioxuslabs.com/", - "Link" - } + Link { to: "https://dioxuslabs.com/", "Link" } } } @@ -129,11 +114,7 @@ fn with_class() { #[component] fn Root() -> Element { rsx! { - Link { - to: Route::Test {}, - class: "test_class", - "Link" - } + Link { to: Route::Test {}, class: "test_class", "Link" } } } @@ -230,11 +211,7 @@ fn with_id() { #[component] fn Root() -> Element { rsx! { - Link { - to: Route::Test {}, - id: "test_id", - "Link" - } + Link { to: Route::Test {}, id: "test_id", "Link" } } } @@ -265,11 +242,7 @@ fn with_new_tab() { #[component] fn Root() -> Element { rsx! { - Link { - to: Route::Test {}, - new_tab: true, - "Link" - } + Link { to: Route::Test {}, new_tab: true, "Link" } } } @@ -293,11 +266,7 @@ fn with_new_tab_external() { #[component] fn Root() -> Element { rsx! { - Link { - to: "https://dioxuslabs.com/", - new_tab: true, - "Link" - } + Link { to: "https://dioxuslabs.com/", new_tab: true, "Link" } } } @@ -329,11 +298,7 @@ fn with_rel() { #[component] fn Root() -> Element { rsx! { - Link { - to: Route::Test {}, - rel: "test_rel".to_string(), - "Link" - } + Link { to: Route::Test {}, rel: "test_rel".to_string(), "Link" } } } diff --git a/packages/router/tests/via_ssr/outlet.rs b/packages/router/tests/via_ssr/outlet.rs index f3ab2f658f..8e151e2dc9 100644 --- a/packages/router/tests/via_ssr/outlet.rs +++ b/packages/router/tests/via_ssr/outlet.rs @@ -38,53 +38,59 @@ fn prepare(path: impl Into) -> VirtualDom { fn App(path: Route) -> Element { rsx! { h1 { "App" } - Router:: { - config: move |_| { - RouterConfig::default().history(MemoryHistory::with_initial_path(path.clone())) - } - } + Router:: { config: move |_| { RouterConfig::default().with_initial_path(path.clone()) } } } } #[component] fn RootIndex() -> Element { - rsx! { h2 { "Root Index" } } + rsx! { + h2 { "Root Index" } + } } #[component] fn Fixed() -> Element { rsx! { h2 { "Fixed" } - Outlet:: { } + Outlet:: {} } } #[component] fn FixedIndex() -> Element { - rsx! { h3 { "Fixed - Index" } } + rsx! { + h3 { "Fixed - Index" } + } } #[component] fn FixedFixed() -> Element { - rsx! { h3 { "Fixed - Fixed"} } + rsx! { + h3 { "Fixed - Fixed" } + } } #[component] fn Parameter(id: u8) -> Element { rsx! { h2 { "Parameter {id}" } - Outlet:: { } + Outlet:: {} } } #[component] fn ParameterIndex(id: u8) -> Element { - rsx! { h3 { "Parameter - Index" } } + rsx! { + h3 { "Parameter - Index" } + } } #[component] fn ParameterFixed(id: u8) -> Element { - rsx! { h3 { "Parameter - Fixed" } } + rsx! { + h3 { "Parameter - Fixed" } + } } } diff --git a/packages/router/tests/via_ssr/redirect.rs b/packages/router/tests/via_ssr/redirect.rs index bd9c919ca4..4af82e0b1b 100644 --- a/packages/router/tests/via_ssr/redirect.rs +++ b/packages/router/tests/via_ssr/redirect.rs @@ -33,10 +33,6 @@ fn Home(lang: String) -> Element { #[component] fn App(path: Route) -> Element { rsx! { - Router:: { - config: { - move |_| RouterConfig::default().history(MemoryHistory::with_initial_path(path.clone())) - } - } + Router:: { config: move |_| RouterConfig::default().with_initial_path(path.clone()) } } } diff --git a/packages/router/tests/via_ssr/without_index.rs b/packages/router/tests/via_ssr/without_index.rs index 9ee85706b4..4b5be6a79c 100644 --- a/packages/router/tests/via_ssr/without_index.rs +++ b/packages/router/tests/via_ssr/without_index.rs @@ -34,7 +34,7 @@ fn App(path: Route) -> Element { rsx! { Router:: { config: { - move || RouterConfig::default().history(MemoryHistory::with_initial_path(path)) + move || RouterConfig::default().with_initial_path(path) } } } diff --git a/packages/server/Cargo.toml b/packages/server/Cargo.toml new file mode 100644 index 0000000000..16b7c0fba2 --- /dev/null +++ b/packages/server/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "dioxus-server" +edition = "2021" +version.workspace = true + +[dependencies] \ No newline at end of file diff --git a/packages/server/src/lib.rs b/packages/server/src/lib.rs new file mode 100644 index 0000000000..7d12d9af81 --- /dev/null +++ b/packages/server/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: usize, right: usize) -> usize { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/packages/static-generation/Cargo.toml b/packages/static-generation/Cargo.toml index b9ce25e28e..41dac8a500 100644 --- a/packages/static-generation/Cargo.toml +++ b/packages/static-generation/Cargo.toml @@ -13,17 +13,18 @@ resolver = "2" [dependencies] dioxus-fullstack = { workspace = true } dioxus-lib.workspace = true -dioxus-router = { workspace = true, features = ["fullstack"] } +dioxus-router = { workspace = true } dioxus-ssr = { workspace = true, optional = true } dioxus-isrg = { workspace = true, optional = true } -axum = { workspace = true, features = ["ws", "macros"], optional = true } -tower-http = { workspace = true, features = ["fs"], optional = true } dioxus-devtools = { workspace = true, optional = true } -dioxus-web = { workspace = true, features = ["hydrate"], optional = true } +dioxus-web = { workspace = true, optional = true, features = ["hydrate"]} + +axum = { workspace = true, optional = true, features = ["ws", "macros"]} +tower = { workspace = true, optional = true, features = ["util"]} +tower-http = { workspace = true, optional = true, features = ["fs"]} tokio = { workspace = true, optional = true } http = { workspace = true, optional = true } -tower = { workspace = true, features = ["util"], optional = true } -tracing.workspace = true +tracing = { workspace = true } [dev-dependencies] dioxus = { workspace = true } @@ -33,7 +34,7 @@ criterion = { workspace = true } default = [] server = [ "dioxus-fullstack/server", - "dioxus-router/ssr", + # "dioxus-router/ssr", "dep:dioxus-ssr", "dep:tokio", "dep:http", @@ -43,7 +44,9 @@ server = [ "dep:tower", ] # server = ["dioxus-fullstack/server", "dioxus-router/ssr", "dep:dioxus-ssr", "dep:tokio", "dep:http", "dep:axum", "dep:tower-http", "dep:dioxus-hot-reload", "dep:dioxus-cli-config", "dep:tower"] -web = ["dioxus-fullstack/web", "dioxus-router/web", "dep:dioxus-web"] +web = ["dep:dioxus-web"] +# web = ["dioxus-fullstack/web", "dep:dioxus-web"] +# web = ["dioxus-fullstack/web", "dioxus-router/web", "dep:dioxus-web"] # [[bench]] # name = "incremental" diff --git a/packages/web/src/document.rs b/packages/web/src/document.rs index 74eceaa62f..9226d020a5 100644 --- a/packages/web/src/document.rs +++ b/packages/web/src/document.rs @@ -71,4 +71,24 @@ impl Document for WebDocument { head.append_child(&element.into()).unwrap(); } } + + fn current_route(&self) -> String { + todo!() + } + + fn go_back(&self) { + todo!() + } + + fn go_forward(&self) { + todo!() + } + + fn push_route(&self, route: String) { + todo!() + } + + fn replace_route(&self, path: String) { + todo!() + } } diff --git a/packages/router/src/history/web.rs b/packages/web/src/history.rs similarity index 88% rename from packages/router/src/history/web.rs rename to packages/web/src/history.rs index 0554160c6a..7d8989c7ec 100644 --- a/packages/router/src/history/web.rs +++ b/packages/web/src/history.rs @@ -13,6 +13,49 @@ use super::{ HistoryProvider, }; +use gloo::console::error; +use wasm_bindgen::JsValue; +use web_sys::History; + +pub(crate) fn replace_state_with_url( + history: &History, + value: &[f64; 2], + url: Option<&str>, +) -> Result<(), JsValue> { + let position = js_sys::Array::new(); + position.push(&JsValue::from(value[0])); + position.push(&JsValue::from(value[1])); + + history.replace_state_with_url(&position, "", url) +} + +pub(crate) fn push_state_and_url( + history: &History, + value: &[f64; 2], + url: String, +) -> Result<(), JsValue> { + let position = js_sys::Array::new(); + position.push(&JsValue::from(value[0])); + position.push(&JsValue::from(value[1])); + + history.push_state_with_url(&position, "", Some(&url)) +} + +pub(crate) fn get_current(history: &History) -> Option<[f64; 2]> { + use wasm_bindgen::JsCast; + + let state = history.state(); + if let Err(err) = &state { + error!(err); + } + state.ok().and_then(|state| { + let state = state.dyn_into::().ok()?; + let x = state.get(0).as_f64()?; + let y = state.get(1).as_f64()?; + Some([x, y]) + }) +} + #[allow(dead_code)] fn base_path() -> Option<&'static str> { todo!("set basepath not through compile-time env vars!") diff --git a/packages/router/src/history/web_hash.rs b/packages/web/src/history/web_hash.rs similarity index 100% rename from packages/router/src/history/web_hash.rs rename to packages/web/src/history/web_hash.rs diff --git a/packages/router/src/history/web_scroll.rs b/packages/web/src/history/web_scroll.rs similarity index 100% rename from packages/router/src/history/web_scroll.rs rename to packages/web/src/history/web_scroll.rs From 39095bfed3a2969b3215d6fa4a021e088afa66c9 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 6 Sep 2024 12:17:21 -0700 Subject: [PATCH 085/139] fix cargotoml --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index ca2021ef53..339d2a3030 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,6 +38,7 @@ members = [ "packages/devtools-types", "packages/isrg", "packages/manganis-core", + "packages/server", # Static generation examples "packages/static-generation/examples/simple", @@ -70,7 +71,6 @@ members = [ "example-projects/tailwind", "example-projects/PWA-example", "example-projects/basic-liveview", - "packages/server", ] [workspace.package] From 84b4d735c6b61e9c13a6d0bb32c10567e39ee756 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 6 Sep 2024 12:19:32 -0700 Subject: [PATCH 086/139] dont put launch in prelude --- example-projects/PWA-example/src/main.rs | 2 +- example-projects/desktop/src/main.rs | 2 +- example-projects/hello-world/src/main.rs | 2 +- example-projects/streaming/src/main.rs | 2 +- example-projects/tailwind/src/main.rs | 2 +- examples/all_events.rs | 2 +- examples/clock.rs | 2 +- examples/counters.rs | 2 +- examples/custom_assets.rs | 2 +- examples/disabled.rs | 2 +- examples/dog_app.rs | 2 +- examples/eval.rs | 2 +- examples/file_upload.rs | 2 +- examples/form.rs | 2 +- examples/generic_component.rs | 2 +- examples/global.rs | 2 +- examples/hello_world.rs | 2 +- examples/image_generator_openai.rs | 2 +- examples/link.rs | 2 +- examples/login_form.rs | 2 +- examples/memo_chain.rs | 2 +- examples/meta.rs | 2 +- examples/multiwindow.rs | 2 +- examples/nested_listeners.rs | 2 +- examples/optional_props.rs | 2 +- examples/read_size.rs | 2 +- examples/readme.rs | 2 +- examples/reducer.rs | 2 +- examples/resize.rs | 2 +- examples/router_resource.rs | 2 +- examples/rsx_usage.rs | 2 +- examples/scroll_to_top.rs | 2 +- examples/shorthand.rs | 2 +- examples/signals.rs | 2 +- examples/simple_list.rs | 2 +- examples/title.rs | 2 +- examples/todomvc.rs | 2 +- examples/weather_app.rs | 2 +- examples/web_component.rs | 2 +- examples/xss_safety.rs | 2 +- packages/desktop/examples/stress.rs | 2 +- packages/dioxus/src/lib.rs | 6 +++--- packages/fullstack/src/axum_adapter.rs | 2 +- packages/playwright-tests/static-generation/src/main.rs | 2 +- packages/playwright-tests/suspense-carousel/src/main.rs | 2 +- packages/playwright-tests/web/src/main.rs | 2 +- packages/router/examples/simple_routes.rs | 2 +- packages/signals/examples/context.rs | 2 +- packages/signals/examples/dependencies.rs | 2 +- packages/signals/examples/map_signal.rs | 2 +- packages/signals/examples/read_only_degrade.rs | 2 +- packages/signals/examples/selector.rs | 2 +- packages/signals/examples/send.rs | 2 +- packages/signals/examples/split_subscriptions.rs | 2 +- packages/static-generation/examples/simple/src/main.rs | 2 +- 55 files changed, 57 insertions(+), 57 deletions(-) diff --git a/example-projects/PWA-example/src/main.rs b/example-projects/PWA-example/src/main.rs index 3e4d5b2361..581395e3d2 100644 --- a/example-projects/PWA-example/src/main.rs +++ b/example-projects/PWA-example/src/main.rs @@ -5,7 +5,7 @@ fn main() { wasm_logger::init(wasm_logger::Config::default()); console_error_panic_hook::set_once(); - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/example-projects/desktop/src/main.rs b/example-projects/desktop/src/main.rs index 0e39cc69d7..f56451f193 100644 --- a/example-projects/desktop/src/main.rs +++ b/example-projects/desktop/src/main.rs @@ -5,7 +5,7 @@ fn main() { // Set the url of the server where server functions are hosted. #[cfg(not(feature = "server"))] dioxus::fullstack::prelude::server_fn::client::set_server_url("http://127.0.0.1:8080"); - launch(app); + dioxus::launch(app); } pub fn app() -> Element { diff --git a/example-projects/hello-world/src/main.rs b/example-projects/hello-world/src/main.rs index 513791fa4c..b99664eb04 100644 --- a/example-projects/hello-world/src/main.rs +++ b/example-projects/hello-world/src/main.rs @@ -50,5 +50,5 @@ fn main() { #[cfg(feature = "server")] tracing_subscriber::fmt::init(); - launch(app); + dioxus::launch(app); } diff --git a/example-projects/streaming/src/main.rs b/example-projects/streaming/src/main.rs index fc6e82c020..f090a847a2 100644 --- a/example-projects/streaming/src/main.rs +++ b/example-projects/streaming/src/main.rs @@ -37,5 +37,5 @@ pub async fn test_stream() -> Result { } fn main() { - launch(app) + dioxus::launch(app) } diff --git a/example-projects/tailwind/src/main.rs b/example-projects/tailwind/src/main.rs index 3b5d52d1c8..84c65b3692 100644 --- a/example-projects/tailwind/src/main.rs +++ b/example-projects/tailwind/src/main.rs @@ -3,7 +3,7 @@ use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } pub fn app() -> Element { diff --git a/examples/all_events.rs b/examples/all_events.rs index b04b00aff1..feb2b3ba02 100644 --- a/examples/all_events.rs +++ b/examples/all_events.rs @@ -9,7 +9,7 @@ use std::{collections::VecDeque, fmt::Debug, rc::Rc}; const STYLE: Asset = asset!("/examples/assets/events.css"); fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/clock.rs b/examples/clock.rs index 6005c87100..dc40285886 100644 --- a/examples/clock.rs +++ b/examples/clock.rs @@ -8,7 +8,7 @@ use dioxus::prelude::*; const STYLE: Asset = asset!("/examples/assets/clock.css"); fn main() { - launch_desktop(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/counters.rs b/examples/counters.rs index 1ed6b60693..739778e72a 100644 --- a/examples/counters.rs +++ b/examples/counters.rs @@ -5,7 +5,7 @@ use dioxus::prelude::*; const STYLE: Asset = asset!("/examples/assets/counter.css"); fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/custom_assets.rs b/examples/custom_assets.rs index 3ae30355d7..8ed01e0cd6 100644 --- a/examples/custom_assets.rs +++ b/examples/custom_assets.rs @@ -20,7 +20,7 @@ use dioxus::prelude::*; static ASSET_PATH: ImageAsset = asset!("/examples/assets/logo.png".image().format(ImageType::Avif)); fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/disabled.rs b/examples/disabled.rs index 6aaa70b9d4..b48b1a912e 100644 --- a/examples/disabled.rs +++ b/examples/disabled.rs @@ -5,7 +5,7 @@ use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/dog_app.rs b/examples/dog_app.rs index 1c53c8731f..09bf5f0a96 100644 --- a/examples/dog_app.rs +++ b/examples/dog_app.rs @@ -11,7 +11,7 @@ use dioxus::prelude::*; use std::collections::HashMap; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/eval.rs b/examples/eval.rs index 5452920771..bde4fa1673 100644 --- a/examples/eval.rs +++ b/examples/eval.rs @@ -6,7 +6,7 @@ use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/file_upload.rs b/examples/file_upload.rs index ca8ea02dd0..ffc1858238 100644 --- a/examples/file_upload.rs +++ b/examples/file_upload.rs @@ -11,7 +11,7 @@ use dioxus::{html::HasFileData, prelude::dioxus_elements::FileEngine}; const STYLE: Asset = asset!("/examples/assets/file_upload.css"); fn main() { - launch(app); + dioxus::launch(app); } struct UploadedFile { diff --git a/examples/form.rs b/examples/form.rs index 91b09ecd34..ff6154b9d4 100644 --- a/examples/form.rs +++ b/examples/form.rs @@ -7,7 +7,7 @@ use dioxus::prelude::*; use std::collections::HashMap; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/generic_component.rs b/examples/generic_component.rs index 6af34e542b..520f257787 100644 --- a/examples/generic_component.rs +++ b/examples/generic_component.rs @@ -7,7 +7,7 @@ use dioxus::prelude::*; use std::fmt::Display; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/global.rs b/examples/global.rs index 5b5829ea32..693b912e93 100644 --- a/examples/global.rs +++ b/examples/global.rs @@ -13,7 +13,7 @@ static COUNT: GlobalSignal = Signal::global(|| 0); static DOUBLED_COUNT: GlobalMemo = Memo::global(|| COUNT() * 2); fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/hello_world.rs b/examples/hello_world.rs index ee33c22309..20c9af934a 100644 --- a/examples/hello_world.rs +++ b/examples/hello_world.rs @@ -12,7 +12,7 @@ use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/image_generator_openai.rs b/examples/image_generator_openai.rs index c8cecf6fd4..67c743c427 100644 --- a/examples/image_generator_openai.rs +++ b/examples/image_generator_openai.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; use serde_json::{json, Error}; fn main() { - launch(app) + dioxus::launch(app) } fn app() -> Element { diff --git a/examples/link.rs b/examples/link.rs index bd5274c163..7d1f4be609 100644 --- a/examples/link.rs +++ b/examples/link.rs @@ -11,7 +11,7 @@ use dioxus::prelude::*; const STYLE: Asset = asset!("/examples/assets/links.css"); fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/login_form.rs b/examples/login_form.rs index 39e874cc99..617723466c 100644 --- a/examples/login_form.rs +++ b/examples/login_form.rs @@ -9,7 +9,7 @@ use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/memo_chain.rs b/examples/memo_chain.rs index 205fe87693..ab57089397 100644 --- a/examples/memo_chain.rs +++ b/examples/memo_chain.rs @@ -6,7 +6,7 @@ use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/meta.rs b/examples/meta.rs index 8b3b4a610c..9d8e1a4ae0 100644 --- a/examples/meta.rs +++ b/examples/meta.rs @@ -3,7 +3,7 @@ use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/multiwindow.rs b/examples/multiwindow.rs index 6858f65896..d89a78ee6d 100644 --- a/examples/multiwindow.rs +++ b/examples/multiwindow.rs @@ -7,7 +7,7 @@ use dioxus::prelude::*; fn main() { - launch_desktop(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/nested_listeners.rs b/examples/nested_listeners.rs index 33cf3d4043..c2316aed37 100644 --- a/examples/nested_listeners.rs +++ b/examples/nested_listeners.rs @@ -7,7 +7,7 @@ use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/optional_props.rs b/examples/optional_props.rs index ad54ed33cd..334852bea1 100644 --- a/examples/optional_props.rs +++ b/examples/optional_props.rs @@ -6,7 +6,7 @@ use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/read_size.rs b/examples/read_size.rs index 350b397a19..dbde9353c7 100644 --- a/examples/read_size.rs +++ b/examples/read_size.rs @@ -9,7 +9,7 @@ use std::rc::Rc; use dioxus::{html::geometry::euclid::Rect, prelude::*}; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/readme.rs b/examples/readme.rs index 8f412a749d..1c36c65c00 100644 --- a/examples/readme.rs +++ b/examples/readme.rs @@ -8,7 +8,7 @@ use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/reducer.rs b/examples/reducer.rs index 954448944c..32a15368e8 100644 --- a/examples/reducer.rs +++ b/examples/reducer.rs @@ -10,7 +10,7 @@ use dioxus::prelude::*; const STYLE: Asset = asset!("/examples/assets/radio.css"); fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/resize.rs b/examples/resize.rs index 73dfd1912d..52b55bfb8c 100644 --- a/examples/resize.rs +++ b/examples/resize.rs @@ -8,7 +8,7 @@ use dioxus::prelude::*; use dioxus_elements::geometry::euclid::Size2D; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/router_resource.rs b/examples/router_resource.rs index de69f0e40b..805997d7d2 100644 --- a/examples/router_resource.rs +++ b/examples/router_resource.rs @@ -15,7 +15,7 @@ enum Route { } fn main() { - launch(App); + dioxus::launch(app); } #[component] diff --git a/examples/rsx_usage.rs b/examples/rsx_usage.rs index fb19ca8bfc..b8ae72fd68 100644 --- a/examples/rsx_usage.rs +++ b/examples/rsx_usage.rs @@ -39,7 +39,7 @@ //! - Allow top-level fragments fn main() { - launch(app) + dioxus::launch(app) } use core::{fmt, str::FromStr}; diff --git a/examples/scroll_to_top.rs b/examples/scroll_to_top.rs index 9f4f85955e..1cd91e4947 100644 --- a/examples/scroll_to_top.rs +++ b/examples/scroll_to_top.rs @@ -8,7 +8,7 @@ use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/shorthand.rs b/examples/shorthand.rs index 33905d5710..bd2047a8e6 100644 --- a/examples/shorthand.rs +++ b/examples/shorthand.rs @@ -3,7 +3,7 @@ use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/signals.rs b/examples/signals.rs index 511ed3f72c..2955aacca8 100644 --- a/examples/signals.rs +++ b/examples/signals.rs @@ -10,7 +10,7 @@ use dioxus::prelude::*; use std::time::Duration; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/simple_list.rs b/examples/simple_list.rs index 3c1b9d71a1..32e1d3eb85 100644 --- a/examples/simple_list.rs +++ b/examples/simple_list.rs @@ -5,7 +5,7 @@ use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/title.rs b/examples/title.rs index 02ea15b718..f836a7f3d6 100644 --- a/examples/title.rs +++ b/examples/title.rs @@ -3,7 +3,7 @@ use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/todomvc.rs b/examples/todomvc.rs index 66db053466..ba4b64da3c 100644 --- a/examples/todomvc.rs +++ b/examples/todomvc.rs @@ -6,7 +6,7 @@ use std::collections::HashMap; const STYLE: Asset = asset!("/examples/assets/todomvc.css"); fn main() { - launch(app); + dioxus::launch(app); } #[derive(PartialEq, Eq, Clone, Copy)] diff --git a/examples/weather_app.rs b/examples/weather_app.rs index 328f719568..7b56bbbe54 100644 --- a/examples/weather_app.rs +++ b/examples/weather_app.rs @@ -4,7 +4,7 @@ use dioxus::prelude::*; use serde::{Deserialize, Serialize}; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/web_component.rs b/examples/web_component.rs index df0515397b..a5f831912b 100644 --- a/examples/web_component.rs +++ b/examples/web_component.rs @@ -7,7 +7,7 @@ use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/xss_safety.rs b/examples/xss_safety.rs index cb8d414dd7..904a42b07b 100644 --- a/examples/xss_safety.rs +++ b/examples/xss_safety.rs @@ -5,7 +5,7 @@ use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/packages/desktop/examples/stress.rs b/packages/desktop/examples/stress.rs index 90dd7947eb..4ad317617c 100644 --- a/packages/desktop/examples/stress.rs +++ b/packages/desktop/examples/stress.rs @@ -1,7 +1,7 @@ use dioxus::prelude::*; fn main() { - launch_desktop(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/packages/dioxus/src/lib.rs b/packages/dioxus/src/lib.rs index 7db7524b15..3fb3895f50 100644 --- a/packages/dioxus/src/lib.rs +++ b/packages/dioxus/src/lib.rs @@ -100,9 +100,9 @@ pub use manganis as assets; pub use manganis; pub mod prelude { - #[cfg(feature = "launch")] - #[cfg_attr(docsrs, doc(cfg(feature = "launch")))] - pub use crate::launch::*; + // #[cfg(feature = "launch")] + // #[cfg_attr(docsrs, doc(cfg(feature = "launch")))] + // pub use crate::launch::*; #[cfg(feature = "launch")] #[cfg_attr(docsrs, doc(cfg(feature = "launch")))] diff --git a/packages/fullstack/src/axum_adapter.rs b/packages/fullstack/src/axum_adapter.rs index 4d3a1c6131..15de7b2102 100644 --- a/packages/fullstack/src/axum_adapter.rs +++ b/packages/fullstack/src/axum_adapter.rs @@ -8,7 +8,7 @@ //! fn main() { //! #[cfg(feature = "web")] //! // Hydrate the application on the client -//! launch(app); +//! dioxus::launch(app); //! #[cfg(feature = "server")] //! { //! tokio::runtime::Runtime::new() diff --git a/packages/playwright-tests/static-generation/src/main.rs b/packages/playwright-tests/static-generation/src/main.rs index 06a98c2219..7a9d2ed0ed 100644 --- a/packages/playwright-tests/static-generation/src/main.rs +++ b/packages/playwright-tests/static-generation/src/main.rs @@ -8,7 +8,7 @@ use dioxus::prelude::*; fn main() { - LaunchBuilder::static_generation().launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/packages/playwright-tests/suspense-carousel/src/main.rs b/packages/playwright-tests/suspense-carousel/src/main.rs index b3661abe61..3339334b40 100644 --- a/packages/playwright-tests/suspense-carousel/src/main.rs +++ b/packages/playwright-tests/suspense-carousel/src/main.rs @@ -115,5 +115,5 @@ async fn sleep(millis: u64) { } fn main() { - launch(app); + dioxus::launch(app); } diff --git a/packages/playwright-tests/web/src/main.rs b/packages/playwright-tests/web/src/main.rs index bbcb9bb57d..798bec26e5 100644 --- a/packages/playwright-tests/web/src/main.rs +++ b/packages/playwright-tests/web/src/main.rs @@ -78,5 +78,5 @@ fn main() { .set_max_level(tracing::Level::TRACE) .build(), ); - launch(app); + dioxus::launch(app); } diff --git a/packages/router/examples/simple_routes.rs b/packages/router/examples/simple_routes.rs index d95f91a8bd..fc91cd00b5 100644 --- a/packages/router/examples/simple_routes.rs +++ b/packages/router/examples/simple_routes.rs @@ -44,7 +44,7 @@ async fn main() { #[cfg(not(feature = "liveview"))] fn main() { - launch(app) + dioxus::launch(app) } fn app() -> Element { diff --git a/packages/signals/examples/context.rs b/packages/signals/examples/context.rs index 1e22423d0e..e769a3ea8e 100644 --- a/packages/signals/examples/context.rs +++ b/packages/signals/examples/context.rs @@ -3,7 +3,7 @@ use dioxus::prelude::*; fn main() { - launch(app) + dioxus::launch(app) } // Because signal is never read in this component, this component will not rerun when the signal changes diff --git a/packages/signals/examples/dependencies.rs b/packages/signals/examples/dependencies.rs index 70319da403..81fce00a10 100644 --- a/packages/signals/examples/dependencies.rs +++ b/packages/signals/examples/dependencies.rs @@ -1,7 +1,7 @@ use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/packages/signals/examples/map_signal.rs b/packages/signals/examples/map_signal.rs index 71d882638f..f8a5cd681c 100644 --- a/packages/signals/examples/map_signal.rs +++ b/packages/signals/examples/map_signal.rs @@ -3,7 +3,7 @@ use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/packages/signals/examples/read_only_degrade.rs b/packages/signals/examples/read_only_degrade.rs index a69101beca..52e6efe889 100644 --- a/packages/signals/examples/read_only_degrade.rs +++ b/packages/signals/examples/read_only_degrade.rs @@ -4,7 +4,7 @@ use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/packages/signals/examples/selector.rs b/packages/signals/examples/selector.rs index c9f578ddb1..6e282cbc55 100644 --- a/packages/signals/examples/selector.rs +++ b/packages/signals/examples/selector.rs @@ -1,7 +1,7 @@ use dioxus::prelude::*; fn main() { - launch(app) + dioxus::launch(app) } fn app() -> Element { diff --git a/packages/signals/examples/send.rs b/packages/signals/examples/send.rs index 5d9c5e51f0..3591d9f058 100644 --- a/packages/signals/examples/send.rs +++ b/packages/signals/examples/send.rs @@ -1,7 +1,7 @@ use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/packages/signals/examples/split_subscriptions.rs b/packages/signals/examples/split_subscriptions.rs index da02ba34f7..b041c533d7 100644 --- a/packages/signals/examples/split_subscriptions.rs +++ b/packages/signals/examples/split_subscriptions.rs @@ -4,7 +4,7 @@ use dioxus::prelude::*; use dioxus_signals::Signal; fn main() { - launch(app); + dioxus::launch(app); } #[derive(Clone, Copy, Default)] diff --git a/packages/static-generation/examples/simple/src/main.rs b/packages/static-generation/examples/simple/src/main.rs index b7816277c3..b69315c8e2 100644 --- a/packages/static-generation/examples/simple/src/main.rs +++ b/packages/static-generation/examples/simple/src/main.rs @@ -3,7 +3,7 @@ use dioxus::prelude::*; // Generate all routes and output them to the static path fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { From 66dd2cf8ae344cc1faf6884cd32cd450fcf6f0e0 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 6 Sep 2024 12:26:57 -0700 Subject: [PATCH 087/139] use dioxus::launch where possible --- example-projects/router/src/main.rs | 2 +- examples/backgrounded_futures.rs | 2 +- examples/calculator.rs | 2 +- examples/calculator_mutable.rs | 2 +- examples/control_focus.rs | 2 +- examples/crm.rs | 4 ++-- examples/custom_assets.rs | 17 +++++------------ examples/custom_html.rs | 2 +- examples/custom_menu.rs | 2 +- examples/dynamic_asset.rs | 2 +- examples/errors.rs | 2 +- examples/file_explorer.rs | 2 +- examples/flat_router.rs | 2 +- examples/future.rs | 2 +- examples/hash_fragment_state.rs | 4 ++-- examples/hydration.rs | 2 +- examples/overlay.rs | 4 +++- examples/popup.rs | 2 +- examples/query_segment_search.rs | 6 +++--- examples/router.rs | 2 +- examples/router_resource.rs | 2 +- examples/shortcut.rs | 2 +- examples/simple_router.rs | 2 +- examples/streams.rs | 2 +- examples/suspense.rs | 2 +- examples/svg.rs | 2 +- examples/window_event.rs | 2 +- examples/window_focus.rs | 2 +- examples/window_zoom.rs | 2 +- packages/dioxus/src/launch.rs | 6 ++++-- packages/dioxus/src/lib.rs | 4 ++-- packages/manganis/src/images.rs | 7 ++++++- .../examples/github-pages/src/main.rs | 2 +- 33 files changed, 52 insertions(+), 50 deletions(-) diff --git a/example-projects/router/src/main.rs b/example-projects/router/src/main.rs index 9a9d4328dc..44dca11bf5 100644 --- a/example-projects/router/src/main.rs +++ b/example-projects/router/src/main.rs @@ -7,7 +7,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch_builder() + dioxus::builder() .with_cfg(server_only!(ServeConfig::builder().incremental( IncrementalRendererConfig::default() .invalidate_after(std::time::Duration::from_secs(120)), diff --git a/examples/backgrounded_futures.rs b/examples/backgrounded_futures.rs index 160b9c122e..4a6f4fd77f 100644 --- a/examples/backgrounded_futures.rs +++ b/examples/backgrounded_futures.rs @@ -10,7 +10,7 @@ use dioxus::prelude::*; fn main() { - launch_desktop(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/calculator.rs b/examples/calculator.rs index e26dd47280..b373923f6b 100644 --- a/examples/calculator.rs +++ b/examples/calculator.rs @@ -15,7 +15,7 @@ use dioxus::prelude::*; const STYLE: Asset = asset!("/examples/assets/calculator.css"); fn main() { - LaunchBuilder::desktop() + dioxus::launch::builder() .with_cfg(desktop!({ use dioxus::desktop::{Config, LogicalSize, WindowBuilder}; Config::new().with_window( diff --git a/examples/calculator_mutable.rs b/examples/calculator_mutable.rs index 13a289bfef..9d1ec05fbc 100644 --- a/examples/calculator_mutable.rs +++ b/examples/calculator_mutable.rs @@ -13,7 +13,7 @@ use dioxus::html::MouseEvent; use dioxus::prelude::*; fn main() { - LaunchBuilder::desktop() + dioxus::launch::builder() .with_cfg( Config::new().with_window( WindowBuilder::new() diff --git a/examples/control_focus.rs b/examples/control_focus.rs index f0aa57672b..538bfc3f1b 100644 --- a/examples/control_focus.rs +++ b/examples/control_focus.rs @@ -9,7 +9,7 @@ use std::rc::Rc; const STYLE: Asset = asset!("/examples/assets/roulette.css"); fn main() { - launch_desktop(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/crm.rs b/examples/crm.rs index 7c19413b06..96f265587d 100644 --- a/examples/crm.rs +++ b/examples/crm.rs @@ -12,7 +12,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch_builder() + dioxus::builder() .with_cfg(desktop!({ use dioxus::desktop::{LogicalSize, WindowBuilder}; dioxus::desktop::Config::default() @@ -22,7 +22,7 @@ fn main() { rsx! { document::Link { rel: "stylesheet", - href: asset!("https://unpkg.com/purecss@2.0.6/build/pure-min.css"), + href: "https://unpkg.com/purecss@2.0.6/build/pure-min.css", integrity: "sha384-Uu6IeWbM+gzNVXJcM9XV3SohHtmWE+3VGi496jvgX1jyvDTXfdK+rfZc8C1Aehk5", crossorigin: "anonymous" } diff --git a/examples/custom_assets.rs b/examples/custom_assets.rs index 8ed01e0cd6..9b78fc3c27 100644 --- a/examples/custom_assets.rs +++ b/examples/custom_assets.rs @@ -7,8 +7,7 @@ //! //! You can run this example with `cargo run --example assets` or `dx serve --example assets`. //! When manganis is not active, the asset!() macro will fallback to the path of the asset on -//! your filesystem. - +//! your filesystem.a use dioxus::prelude::*; /// asset!() will mark this asset as a dependency of the app without actually including it in the @@ -29,16 +28,10 @@ fn app() -> Element { h1 { "This should show an image:" } img { src: ASSET_PATH } - // keep support for these too - img { - src: "/Users/jonkelley/Development/dioxus/examples/assets/logo.png" - } - img { - src: "/examples/assets/logo.png" - } - img { - src: "examples/assets/logo.png" - } + // temporarily keep support for these too + img { src: "/Users/jonkelley/Development/dioxus/examples/assets/logo.png" } + img { src: "/examples/assets/logo.png" } + img { src: "examples/assets/logo.png" } } } } diff --git a/examples/custom_html.rs b/examples/custom_html.rs index 91ca00e110..a77468039d 100644 --- a/examples/custom_html.rs +++ b/examples/custom_html.rs @@ -4,7 +4,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch_builder() + dioxus::builder() .with_cfg( dioxus::desktop::Config::new().with_custom_index( r#" diff --git a/examples/custom_menu.rs b/examples/custom_menu.rs index f78f33c884..da1085bfd5 100644 --- a/examples/custom_menu.rs +++ b/examples/custom_menu.rs @@ -28,7 +28,7 @@ fn main() { let config = dioxus::desktop::Config::new().with_menu(menu); // Launch the app with the custom menu - LaunchBuilder::new().with_cfg(config).launch(app) + dioxus::launch::builder().with_cfg(config).launch(app) } fn app() -> Element { diff --git a/examples/dynamic_asset.rs b/examples/dynamic_asset.rs index 0b77f86f06..cca5efb76c 100644 --- a/examples/dynamic_asset.rs +++ b/examples/dynamic_asset.rs @@ -10,7 +10,7 @@ use dioxus::prelude::*; const STYLE: Asset = asset!("/examples/assets/custom_assets.css"); fn main() { - launch_desktop(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/errors.rs b/examples/errors.rs index 4727cb5e03..a7319e0170 100644 --- a/examples/errors.rs +++ b/examples/errors.rs @@ -11,7 +11,7 @@ use dioxus::prelude::*; fn main() { - launch(|| rsx! { Router:: {} }); + dioxus::launch(|| rsx! { Router:: {} }); } /// You can use an ErrorBoundary to catch errors in children and display a warning diff --git a/examples/file_explorer.rs b/examples/file_explorer.rs index a51b1c4595..c5d5f31756 100644 --- a/examples/file_explorer.rs +++ b/examples/file_explorer.rs @@ -9,7 +9,7 @@ use dioxus::desktop::{Config, WindowBuilder}; use dioxus::prelude::*; fn main() { - LaunchBuilder::desktop() + dioxus::launch::builder() .with_cfg(Config::new().with_window(WindowBuilder::new().with_resizable(true))) .launch(app) } diff --git a/examples/flat_router.rs b/examples/flat_router.rs index 87a120f28c..d537ee4c1b 100644 --- a/examples/flat_router.rs +++ b/examples/flat_router.rs @@ -12,7 +12,7 @@ use dioxus::prelude::*; const STYLE: Asset = asset!("/examples/assets/flat_router.css"); fn main() { - launch(|| { + dioxus::launch(|| { rsx! { document::Stylesheet { href: STYLE } Router:: {} diff --git a/examples/future.rs b/examples/future.rs index 8f54dadb37..edd76bb410 100644 --- a/examples/future.rs +++ b/examples/future.rs @@ -7,7 +7,7 @@ use dioxus::prelude::*; use std::time::Duration; fn main() { - launch_desktop(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/hash_fragment_state.rs b/examples/hash_fragment_state.rs index ed77c8c46e..bc0ec2cc88 100644 --- a/examples/hash_fragment_state.rs +++ b/examples/hash_fragment_state.rs @@ -2,7 +2,7 @@ //! //! You can set up two way data binding between the url hash and signals. //! -//! Run this example on desktop with +//! Run this example on desktop with //! ```sh //! dx serve --example hash_fragment_state --features=ciborium,base64 //! ``` @@ -19,7 +19,7 @@ use dioxus::prelude::*; use serde::{Deserialize, Serialize}; fn main() { - launch(|| { + dioxus::launch(|| { rsx! { Router:: {} } diff --git a/examples/hydration.rs b/examples/hydration.rs index 1e7de2706f..cffacc86aa 100644 --- a/examples/hydration.rs +++ b/examples/hydration.rs @@ -13,7 +13,7 @@ use dioxus::desktop::Config; use dioxus::prelude::*; fn main() { - LaunchBuilder::desktop() + dioxus::launch::builder() .with_cfg(Config::new().with_prerendered({ // We build the dom a first time, then pre-render it to HTML let pre_rendered_dom = VirtualDom::prebuilt(app); diff --git a/examples/overlay.rs b/examples/overlay.rs index 8c675675d5..59a9619d6a 100644 --- a/examples/overlay.rs +++ b/examples/overlay.rs @@ -11,7 +11,9 @@ use dioxus::desktop::{ use dioxus::prelude::*; fn main() { - LaunchBuilder::desktop().with_cfg(make_config()).launch(app); + dioxus::launch::builder() + .with_cfg(make_config()) + .launch(app); } fn app() -> Element { diff --git a/examples/popup.rs b/examples/popup.rs index 131d536edb..e708b9cd9f 100644 --- a/examples/popup.rs +++ b/examples/popup.rs @@ -5,7 +5,7 @@ use dioxus::prelude::*; use std::rc::Rc; fn main() { - launch_desktop(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/query_segment_search.rs b/examples/query_segment_search.rs index 018f475e45..4366cc0118 100644 --- a/examples/query_segment_search.rs +++ b/examples/query_segment_search.rs @@ -2,7 +2,7 @@ //! //! The enum router makes it easy to use your route as state in your app. This example shows how to use the router to encode search text into the url and decode it back into a string. //! -//! Run this example on desktop with +//! Run this example on desktop with //! ```sh //! dx serve --example query_segment_search //! ``` @@ -14,7 +14,7 @@ use dioxus::prelude::*; fn main() { - launch(|| { + dioxus::launch(|| { rsx! { Router:: {} } @@ -29,7 +29,7 @@ enum Route { // The each query segment must implement and Display. // You can use multiple query segments separated by `&`s. - #[route("/search?:query&:word_count")] + #[route("/search?:query&:word_count")] Search { query: String, word_count: usize, diff --git a/examples/router.rs b/examples/router.rs index 591286d541..377c595418 100644 --- a/examples/router.rs +++ b/examples/router.rs @@ -11,7 +11,7 @@ use dioxus::prelude::*; const STYLE: Asset = asset!("/examples/assets/router.css"); fn main() { - launch(|| { + dioxus::launch(|| { rsx! { document::Stylesheet { href: STYLE } Router:: {} diff --git a/examples/router_resource.rs b/examples/router_resource.rs index 805997d7d2..6898585f3b 100644 --- a/examples/router_resource.rs +++ b/examples/router_resource.rs @@ -15,7 +15,7 @@ enum Route { } fn main() { - dioxus::launch(app); + dioxus::launch(App); } #[component] diff --git a/examples/shortcut.rs b/examples/shortcut.rs index d0192b9782..a6626c27b7 100644 --- a/examples/shortcut.rs +++ b/examples/shortcut.rs @@ -9,7 +9,7 @@ use dioxus::desktop::use_global_shortcut; use dioxus::prelude::*; fn main() { - launch_desktop(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/simple_router.rs b/examples/simple_router.rs index e674f2f30e..0ec22a001e 100644 --- a/examples/simple_router.rs +++ b/examples/simple_router.rs @@ -5,7 +5,7 @@ use dioxus::prelude::*; fn main() { // Launch the router, using our `Route` component as the generic type // This will automatically boot the app to "/" unless otherwise specified - launch(|| rsx! { Router:: {} }); + dioxus::launch(|| rsx! { Router:: {} }); } /// By default, the Routable derive will use the name of the variant as the route diff --git a/examples/streams.rs b/examples/streams.rs index 5e62c35aa6..0c18758e3f 100644 --- a/examples/streams.rs +++ b/examples/streams.rs @@ -5,7 +5,7 @@ use futures_util::{future, stream, Stream, StreamExt}; use std::time::Duration; fn main() { - launch_desktop(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/suspense.rs b/examples/suspense.rs index b14921113a..14cd2573e9 100644 --- a/examples/suspense.rs +++ b/examples/suspense.rs @@ -15,7 +15,7 @@ use dioxus::desktop::{Config, LogicalSize, WindowBuilder}; use dioxus::prelude::*; fn main() { - dioxus::launch_builder() + dioxus::builder() .with_cfg(desktop! { Config::new().with_window( WindowBuilder::new() diff --git a/examples/svg.rs b/examples/svg.rs index d4c23d1ddb..b462f30288 100644 --- a/examples/svg.rs +++ b/examples/svg.rs @@ -10,7 +10,7 @@ use dioxus::prelude::*; use rand::{thread_rng, Rng}; fn main() { - launch(|| { + dioxus::launch(|| { rsx! { div { user_select: "none", webkit_user_select: "none", margin_left: "10%", margin_right: "10%", h1 { "Click die to generate a new value" } diff --git a/examples/window_event.rs b/examples/window_event.rs index b8ce600f64..ca126f6a1d 100644 --- a/examples/window_event.rs +++ b/examples/window_event.rs @@ -13,7 +13,7 @@ use dioxus::desktop::{window, Config, WindowBuilder}; use dioxus::prelude::*; fn main() { - LaunchBuilder::desktop() + dioxus::launch::builder() .with_cfg( Config::new().with_window( WindowBuilder::new() diff --git a/examples/window_focus.rs b/examples/window_focus.rs index 447544077b..1213b3e994 100644 --- a/examples/window_focus.rs +++ b/examples/window_focus.rs @@ -12,7 +12,7 @@ use dioxus::desktop::{Config, WindowCloseBehaviour}; use dioxus::prelude::*; fn main() { - LaunchBuilder::desktop() + dioxus::launch::builder() .with_cfg(Config::new().with_close_behaviour(WindowCloseBehaviour::CloseWindow)) .launch(app) } diff --git a/examples/window_zoom.rs b/examples/window_zoom.rs index 8b10d503b0..35d25f4fde 100644 --- a/examples/window_zoom.rs +++ b/examples/window_zoom.rs @@ -5,7 +5,7 @@ use dioxus::prelude::*; fn main() { - launch_desktop(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/packages/dioxus/src/launch.rs b/packages/dioxus/src/launch.rs index e882f6ce8a..67ac1aaea2 100644 --- a/packages/dioxus/src/launch.rs +++ b/packages/dioxus/src/launch.rs @@ -6,7 +6,7 @@ use std::any::Any; use crate::prelude::*; -pub fn launch_builder() -> LaunchBuilder { +pub fn builder() -> LaunchBuilder { LaunchBuilder::new() } @@ -165,13 +165,15 @@ impl LaunchBuilder { pub trait TryIntoConfig { fn into_config(self, config: &mut Option); } + impl TryIntoConfig for () { fn into_config(self, config: &mut Option) {} } impl LaunchBuilder { /// Provide a platform-specific config to the builder. - pub fn with_cfg(mut self, config: impl TryIntoConfig) -> Self { + pub fn with_cfg(mut self, config: F) -> Self { + // pub fn with_cfg(mut self, config: impl TryIntoConfig) -> Self { // config.into_config(&mut self.platform_config); self } diff --git a/packages/dioxus/src/lib.rs b/packages/dioxus/src/lib.rs index 3fb3895f50..3aefcf8ea0 100644 --- a/packages/dioxus/src/lib.rs +++ b/packages/dioxus/src/lib.rs @@ -30,7 +30,7 @@ pub use dioxus_core::{CapturedError, Ok, Result}; #[cfg(feature = "launch")] #[cfg_attr(docsrs, doc(cfg(feature = "launch")))] -mod launch; +pub mod launch; pub mod events { #[cfg(feature = "html")] @@ -41,7 +41,7 @@ pub mod events { #[cfg(feature = "launch")] #[cfg_attr(docsrs, doc(cfg(feature = "launch")))] #[allow(deprecated)] -pub use launch::{launch, launch_builder}; +pub use launch::{builder, launch}; #[cfg(feature = "hooks")] #[cfg_attr(docsrs, doc(cfg(feature = "hooks")))] diff --git a/packages/manganis/src/images.rs b/packages/manganis/src/images.rs index b7ef40cdf2..0e251efe88 100644 --- a/packages/manganis/src/images.rs +++ b/packages/manganis/src/images.rs @@ -3,7 +3,7 @@ use dioxus_core_types::DioxusFormattable; use crate::Asset; /// An image asset that is built by the [`asset!`] macro -#[derive(Debug, PartialEq, PartialOrd, Clone, Hash)] +#[derive(Debug, PartialEq, PartialOrd, Clone, Hash, Copy)] pub struct ImageAsset { /// The path to the image asset: Asset, @@ -54,6 +54,11 @@ impl ImageAsset { pub const fn with_caption(self, caption: Option<&'static str>) -> Self { Self { caption, ..self } } + + /// Sets the format of the image + pub const fn format(self, format: ImageType) -> Self { + Self { ..self } + } } impl std::ops::Deref for ImageAsset { diff --git a/packages/static-generation/examples/github-pages/src/main.rs b/packages/static-generation/examples/github-pages/src/main.rs index 0b3aa4ef11..205dc32b00 100644 --- a/packages/static-generation/examples/github-pages/src/main.rs +++ b/packages/static-generation/examples/github-pages/src/main.rs @@ -6,7 +6,7 @@ use dioxus::prelude::*; // Generate all routes and output them to the static path fn main() { - dioxus::launch_builder() + dioxus::builder() .with_cfg(dioxus::static_site_generation::Config::new().github_pages()) .launch(|| { rsx! { From 80c88ea91fb60cb3433513fdf90c6d66f6071178 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 6 Sep 2024 12:30:10 -0700 Subject: [PATCH 088/139] remove random latent command chars --- examples/clock.rs | 2 +- examples/custom_assets.rs | 2 +- packages/html/Cargo.toml | 2 +- packages/interpreter/src/js/hash.txt | 2 +- packages/interpreter/src/ts/native.ts | 2 +- packages/rsx/src/component.rs | 2 +- packages/web/src/event.rs | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/clock.rs b/examples/clock.rs index dc40285886..2456b0156c 100644 --- a/examples/clock.rs +++ b/examples/clock.rs @@ -15,7 +15,7 @@ fn app() -> Element { let mut millis = use_signal(|| 0); use_future(move || async move { - // Save our initial timea + // Save our initial time let start = std::time::Instant::now(); loop { diff --git a/examples/custom_assets.rs b/examples/custom_assets.rs index 9b78fc3c27..c9cfe7bad7 100644 --- a/examples/custom_assets.rs +++ b/examples/custom_assets.rs @@ -7,7 +7,7 @@ //! //! You can run this example with `cargo run --example assets` or `dx serve --example assets`. //! When manganis is not active, the asset!() macro will fallback to the path of the asset on -//! your filesystem.a +//! your filesystem. use dioxus::prelude::*; /// asset!() will mark this asset as a dependency of the app without actually including it in the diff --git a/packages/html/Cargo.toml b/packages/html/Cargo.toml index 7fffe8dc79..9ac8b98dd2 100644 --- a/packages/html/Cargo.toml +++ b/packages/html/Cargo.toml @@ -28,7 +28,7 @@ futures-channel = { workspace = true } # dioxus-core = { workspace = true } # generational-box = { workspace = true } -# todo: we shouldn't have any wasm-bindgen dependencies in the html cratea +# todo: we shouldn't have any wasm-bindgen dependencies in the html crate # [target.'cfg(target_arch = "wasm32")'.dependencies] # wasm-bindgen = { workspace = true, optional = true } # js-sys = { version = "0.3.56", optional = true } diff --git a/packages/interpreter/src/js/hash.txt b/packages/interpreter/src/js/hash.txt index df1e3db408..391d8d66fa 100644 --- a/packages/interpreter/src/js/hash.txt +++ b/packages/interpreter/src/js/hash.txt @@ -1 +1 @@ -[6449103750905854967, 4461869229701639737, 13069001215487072322, 8716623267269178440, 5336385715226370016, 14456089431355876478, 7300879533173534275, 5052021921702764563, 17534315583914394253, 5638004933879392817] \ No newline at end of file +[6449103750905854967, 4461869229701639737, 13069001215487072322, 8716623267269178440, 5336385715226370016, 14456089431355876478, 640348513295264026, 5052021921702764563, 17534315583914394253, 5638004933879392817] \ No newline at end of file diff --git a/packages/interpreter/src/ts/native.ts b/packages/interpreter/src/ts/native.ts index 8fb2917ca2..98385fd209 100644 --- a/packages/interpreter/src/ts/native.ts +++ b/packages/interpreter/src/ts/native.ts @@ -450,7 +450,7 @@ function handleVirtualdomEventSync( } function getTargetId(target: EventTarget): NodeId | null { - // Ensure that the target is a node, sometimes it's nota + // Ensure that the target is a node, sometimes it's not if (!(target instanceof Node)) { return null; } diff --git a/packages/rsx/src/component.rs b/packages/rsx/src/component.rs index b39bac12c3..7a350291f5 100644 --- a/packages/rsx/src/component.rs +++ b/packages/rsx/src/component.rs @@ -209,7 +209,7 @@ impl Component { /// Create the tokens we'll use for the props of the component /// - /// todo: don't create the tokenstream from scratch and instead dump it into the existing streama + /// todo: don't create the tokenstream from scratch and instead dump it into the existing stream fn create_props(&self) -> TokenStream2 { let manual_props = self.manual_props(); diff --git a/packages/web/src/event.rs b/packages/web/src/event.rs index 1005ade623..eb48ce9f0b 100644 --- a/packages/web/src/event.rs +++ b/packages/web/src/event.rs @@ -148,7 +148,7 @@ struct GenericWebSysEvent { raw: Event, element: Element, } -/// Converts our Synthetic wrapper into the trait objects dioxus is expectinga +/// Converts our Synthetic wrapper into the trait objects dioxus is expecting trait Synthesize { fn synthesize>>(&self) -> F; } From fc2289b8186a477ae936ce86462829eda31f166a Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 6 Sep 2024 12:30:50 -0700 Subject: [PATCH 089/139] fix a few more uses of launch --- example-projects/hackernews/src/main.rs | 2 +- packages/static-generation/examples/router/src/main.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/example-projects/hackernews/src/main.rs b/example-projects/hackernews/src/main.rs index 828cb119a4..b675b6003d 100644 --- a/example-projects/hackernews/src/main.rs +++ b/example-projects/hackernews/src/main.rs @@ -17,7 +17,7 @@ fn main() { #[cfg(feature = "server")] tracing_subscriber::fmt::init(); - launch(|| rsx! { Router:: {} }); + dioxus::launch(|| rsx! { Router:: {} }); } #[derive(Clone, Routable)] diff --git a/packages/static-generation/examples/router/src/main.rs b/packages/static-generation/examples/router/src/main.rs index 2d43a264cb..b96b6c98de 100644 --- a/packages/static-generation/examples/router/src/main.rs +++ b/packages/static-generation/examples/router/src/main.rs @@ -5,7 +5,7 @@ use dioxus::prelude::*; // Generate all routes and output them to the static path fn main() { - launch(|| { + dioxus::launch(|| { rsx! { Router:: {} } From f3e3a4f14b6bb821c64161a2f3da3f1c41c767c4 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 6 Sep 2024 12:48:47 -0700 Subject: [PATCH 090/139] it all compiles again... --- Cargo.lock | 1 + example-projects/router/src/main.rs | 12 ++++++++---- packages/config-macro/src/lib.rs | 2 +- packages/desktop/headless_tests/eval.rs | 11 ++++++----- packages/desktop/headless_tests/utils.rs | 2 +- packages/desktop/src/document.rs | 19 +++++++++++-------- packages/dioxus/Cargo.toml | 1 + packages/document/src/document.rs | 3 --- packages/document/src/eval.rs | 14 ++++++++------ packages/liveview/src/document.rs | 4 ---- packages/web/src/document.rs | 4 ---- 11 files changed, 37 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aa7c2dd776..c4c0268a45 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2338,6 +2338,7 @@ dependencies = [ "dioxus-fullstack", "dioxus-hooks", "dioxus-html", + "dioxus-isrg", "dioxus-liveview", "dioxus-mobile", "dioxus-router", diff --git a/example-projects/router/src/main.rs b/example-projects/router/src/main.rs index 44dca11bf5..77933a70cb 100644 --- a/example-projects/router/src/main.rs +++ b/example-projects/router/src/main.rs @@ -7,11 +7,15 @@ use dioxus::prelude::*; fn main() { + todo!("isrg"); dioxus::builder() - .with_cfg(server_only!(ServeConfig::builder().incremental( - IncrementalRendererConfig::default() - .invalidate_after(std::time::Duration::from_secs(120)), - ))) + // .with_cfg(server_only!( + // // + // ServeConfig::builder().incremental( + // IncrementalRendererConfig::default() + // .invalidate_after(std::time::Duration::from_secs(120)), + // ) + // )) .launch(app); } diff --git a/packages/config-macro/src/lib.rs b/packages/config-macro/src/lib.rs index 0660539d2b..c803314dd3 100644 --- a/packages/config-macro/src/lib.rs +++ b/packages/config-macro/src/lib.rs @@ -27,7 +27,7 @@ macro_rules! define_config_macro { }; } -define_config_macro!(server_only if any(feature = "ssr", feature = "liveview")); +define_config_macro!(server_only if any(feature = "server", feature = "liveview")); define_config_macro!(client if any(feature = "desktop", feature = "web")); define_config_macro!(web if feature = "web"); define_config_macro!(desktop if feature = "desktop"); diff --git a/packages/desktop/headless_tests/eval.rs b/packages/desktop/headless_tests/eval.rs index 7fc230750c..c23eac17bb 100644 --- a/packages/desktop/headless_tests/eval.rs +++ b/packages/desktop/headless_tests/eval.rs @@ -47,11 +47,12 @@ fn app() -> Element { "#, ); - assert_eq!( - Vec::::deserialize(&eval.await.unwrap()).unwrap(), - vec![1, 2, 3] - ); - EVALS_RETURNED.with_mut(|x| *x += 1); + todo!() + // assert_eq!( + // Vec::::deserialize(&eval.await.unwrap()).unwrap(), + // vec![1, 2, 3] + // ); + // EVALS_RETURNED.with_mut(|x| *x += 1); }); use_memo(|| { diff --git a/packages/desktop/headless_tests/utils.rs b/packages/desktop/headless_tests/utils.rs index 6e7e73d1f7..04e6b59ba5 100644 --- a/packages/desktop/headless_tests/utils.rs +++ b/packages/desktop/headless_tests/utils.rs @@ -16,7 +16,7 @@ pub fn check_app_exits(app: fn() -> Element) { } }); - LaunchBuilder::new() + dioxus::launch::builder() .with_cfg(Config::new().with_window(WindowBuilder::new().with_visible(false))) .launch(app); diff --git a/packages/desktop/src/document.rs b/packages/desktop/src/document.rs index 38c6c4e64e..6d41c17fe9 100644 --- a/packages/desktop/src/document.rs +++ b/packages/desktop/src/document.rs @@ -1,12 +1,11 @@ use crate::DesktopService; use dioxus_document::{Document, Eval, EvalError}; -use std::sync::{Arc, Mutex}; +use std::{ + str::FromStr, + sync::{Arc, Mutex}, +}; impl Document for DesktopService { - fn as_any(&self) -> &dyn std::any::Any { - self - } - fn set_title(&self, title: String) { self.window.set_title(&title); } @@ -19,9 +18,13 @@ impl Document for DesktopService { let tx = Arc::new(Mutex::new(Some(tx))); let callback = { let tx = tx.clone(); - move |res| { - if let Some(tx) = tx.lock().unwrap().take() { - let _ = tx.send(Ok(res)); + move |res: String| { + if let Ok(res) = serde_json::from_str(&res) { + if let Some(tx) = tx.lock().unwrap().take() { + let _ = tx.send(Ok(res)); + } + } else { + tracing::error!("Failed to deserialize eval result: {res:?}"); } } }; diff --git a/packages/dioxus/Cargo.toml b/packages/dioxus/Cargo.toml index 11e4592740..6bc0c10253 100644 --- a/packages/dioxus/Cargo.toml +++ b/packages/dioxus/Cargo.toml @@ -27,6 +27,7 @@ dioxus-fullstack = { workspace = true, default-features = true, optional = true, dioxus-static-site-generation = { workspace = true, optional = true } dioxus-liveview = { workspace = true, optional = true } dioxus-ssr = { workspace = true, optional = true } +dioxus-isrg = { workspace = true, optional = true } manganis = { workspace = true, optional = true } dioxus-devtools = { workspace = true, optional = true } diff --git a/packages/document/src/document.rs b/packages/document/src/document.rs index fcc2dd2cdf..1284172982 100644 --- a/packages/document/src/document.rs +++ b/packages/document/src/document.rs @@ -19,9 +19,6 @@ pub type DocumentContext = Arc; /// The described behaviors are designed to mimic a web browser, which most users should already /// know. Deviations might confuse them. pub trait Document: 'static { - /// Get a reference to the document as `dyn Any` - fn as_any(&self) -> &dyn std::any::Any; - /// Run `eval` against this document, returning an [`Eval`] that can be used to await the result. fn eval(&self, js: String) -> Eval; diff --git a/packages/document/src/eval.rs b/packages/document/src/eval.rs index 0ebac66b4d..9d40cda64c 100644 --- a/packages/document/src/eval.rs +++ b/packages/document/src/eval.rs @@ -8,18 +8,20 @@ use std::{ #[doc = include_str!("../docs/eval.md")] pub struct Eval { - rx: futures_channel::oneshot::Receiver>, + rx: futures_channel::oneshot::Receiver>, } impl Eval { /// Create this eval from a oneshot channel that, when resolved, will return the result of the eval - pub fn new(rx: futures_channel::oneshot::Receiver>) -> Self { + pub fn new( + rx: futures_channel::oneshot::Receiver>, + ) -> Self { Self { rx } } /// Create this eval and return the tx that will be used to resolve the eval pub fn from_parts() -> ( - futures_channel::oneshot::Sender>, + futures_channel::oneshot::Sender>, Self, ) { let (tx, rx) = futures_channel::oneshot::channel(); @@ -27,7 +29,7 @@ impl Eval { } /// Poll this eval until it resolves - pub async fn recv(self) -> Result { + pub async fn recv(self) -> Result { self.rx .await .map_err(|_| EvalError::Communication("eval channel closed".to_string()))? @@ -35,12 +37,12 @@ impl Eval { pub async fn recv_as(self) -> Result { let res = self.recv().await?; - serde_json::from_str(&res).map_err(|e| EvalError::Deserialization(e)) + serde_json::from_value(res).map_err(|e| EvalError::Deserialization(e)) } } impl IntoFuture for Eval { - type Output = Result; + type Output = Result; type IntoFuture = Pin>>; fn into_future(self) -> Self::IntoFuture { diff --git a/packages/liveview/src/document.rs b/packages/liveview/src/document.rs index b198456b35..e13234735b 100644 --- a/packages/liveview/src/document.rs +++ b/packages/liveview/src/document.rs @@ -17,10 +17,6 @@ pub struct LiveviewDocument { } impl Document for LiveviewDocument { - fn as_any(&self) -> &dyn std::any::Any { - self - } - fn eval(&self, js: String) -> dioxus_document::Eval { todo!() } diff --git a/packages/web/src/document.rs b/packages/web/src/document.rs index 9226d020a5..b30150c691 100644 --- a/packages/web/src/document.rs +++ b/packages/web/src/document.rs @@ -46,10 +46,6 @@ impl Document for WebDocument { eval } - fn as_any(&self) -> &dyn std::any::Any { - self - } - fn set_title(&self, title: String) { self.document.set_title(&title); } From 8320a3ed98c68c8227a583e96e64aab6899c11dc Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 6 Sep 2024 12:58:21 -0700 Subject: [PATCH 091/139] rename pwa --- Cargo.toml | 2 +- .../{PWA-example => pwa}/Cargo.toml | 0 .../{PWA-example => pwa}/Dioxus.toml | 0 example-projects/{PWA-example => pwa}/LICENSE | 0 .../{PWA-example => pwa}/README.md | 0 .../{PWA-example => pwa}/index.html | 0 .../{PWA-example => pwa}/public/favicon.ico | Bin .../{PWA-example => pwa}/public/logo_192.png | Bin .../{PWA-example => pwa}/public/logo_512.png | Bin .../{PWA-example => pwa}/public/manifest.json | 0 .../{PWA-example => pwa}/public/sw.js | 0 .../{PWA-example => pwa}/src/main.rs | 0 examples/all_events.rs | 4 +- examples/assets/purecss.css | 12 ++++++ examples/control_focus.rs | 4 +- examples/counters.rs | 4 +- examples/crm.rs | 7 +-- examples/custom_assets.rs | 4 +- examples/dynamic_asset.rs | 4 +- examples/errors.rs | 40 ++++++------------ packages/desktop/examples/stress.rs | 23 ---------- 21 files changed, 32 insertions(+), 72 deletions(-) rename example-projects/{PWA-example => pwa}/Cargo.toml (100%) rename example-projects/{PWA-example => pwa}/Dioxus.toml (100%) rename example-projects/{PWA-example => pwa}/LICENSE (100%) rename example-projects/{PWA-example => pwa}/README.md (100%) rename example-projects/{PWA-example => pwa}/index.html (100%) rename example-projects/{PWA-example => pwa}/public/favicon.ico (100%) rename example-projects/{PWA-example => pwa}/public/logo_192.png (100%) rename example-projects/{PWA-example => pwa}/public/logo_512.png (100%) rename example-projects/{PWA-example => pwa}/public/manifest.json (100%) rename example-projects/{PWA-example => pwa}/public/sw.js (100%) rename example-projects/{PWA-example => pwa}/src/main.rs (100%) create mode 100644 examples/assets/purecss.css delete mode 100644 packages/desktop/examples/stress.rs diff --git a/Cargo.toml b/Cargo.toml index 339d2a3030..53eeb921d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -69,7 +69,7 @@ members = [ "example-projects/hackernews", "example-projects/fullstack-mobile", "example-projects/tailwind", - "example-projects/PWA-example", + "example-projects/pwa", "example-projects/basic-liveview", ] diff --git a/example-projects/PWA-example/Cargo.toml b/example-projects/pwa/Cargo.toml similarity index 100% rename from example-projects/PWA-example/Cargo.toml rename to example-projects/pwa/Cargo.toml diff --git a/example-projects/PWA-example/Dioxus.toml b/example-projects/pwa/Dioxus.toml similarity index 100% rename from example-projects/PWA-example/Dioxus.toml rename to example-projects/pwa/Dioxus.toml diff --git a/example-projects/PWA-example/LICENSE b/example-projects/pwa/LICENSE similarity index 100% rename from example-projects/PWA-example/LICENSE rename to example-projects/pwa/LICENSE diff --git a/example-projects/PWA-example/README.md b/example-projects/pwa/README.md similarity index 100% rename from example-projects/PWA-example/README.md rename to example-projects/pwa/README.md diff --git a/example-projects/PWA-example/index.html b/example-projects/pwa/index.html similarity index 100% rename from example-projects/PWA-example/index.html rename to example-projects/pwa/index.html diff --git a/example-projects/PWA-example/public/favicon.ico b/example-projects/pwa/public/favicon.ico similarity index 100% rename from example-projects/PWA-example/public/favicon.ico rename to example-projects/pwa/public/favicon.ico diff --git a/example-projects/PWA-example/public/logo_192.png b/example-projects/pwa/public/logo_192.png similarity index 100% rename from example-projects/PWA-example/public/logo_192.png rename to example-projects/pwa/public/logo_192.png diff --git a/example-projects/PWA-example/public/logo_512.png b/example-projects/pwa/public/logo_512.png similarity index 100% rename from example-projects/PWA-example/public/logo_512.png rename to example-projects/pwa/public/logo_512.png diff --git a/example-projects/PWA-example/public/manifest.json b/example-projects/pwa/public/manifest.json similarity index 100% rename from example-projects/PWA-example/public/manifest.json rename to example-projects/pwa/public/manifest.json diff --git a/example-projects/PWA-example/public/sw.js b/example-projects/pwa/public/sw.js similarity index 100% rename from example-projects/PWA-example/public/sw.js rename to example-projects/pwa/public/sw.js diff --git a/example-projects/PWA-example/src/main.rs b/example-projects/pwa/src/main.rs similarity index 100% rename from example-projects/PWA-example/src/main.rs rename to example-projects/pwa/src/main.rs diff --git a/examples/all_events.rs b/examples/all_events.rs index feb2b3ba02..4e4ca3d6d5 100644 --- a/examples/all_events.rs +++ b/examples/all_events.rs @@ -6,8 +6,6 @@ use dioxus::prelude::*; use std::{collections::VecDeque, fmt::Debug, rc::Rc}; -const STYLE: Asset = asset!("/examples/assets/events.css"); - fn main() { dioxus::launch(app); } @@ -26,7 +24,7 @@ fn app() -> Element { }; rsx! { - document::Stylesheet { href: STYLE } + document::Stylesheet { href: asset!("/examples/assets/events.css") } div { id: "container", // focusing is necessary to catch keyboard events div { id: "receiver", tabindex: 0, diff --git a/examples/assets/purecss.css b/examples/assets/purecss.css new file mode 100644 index 0000000000..dd301cc364 --- /dev/null +++ b/examples/assets/purecss.css @@ -0,0 +1,12 @@ +/*! +Pure v2.0.6 +Copyright 2013 Yahoo! +Licensed under the BSD License. +https://github.com/pure-css/pure/blob/master/LICENSE +*/ +/*! +normalize.css v | MIT License | git.io/normalize +Copyright (c) Nicolas Gallagher and Jonathan Neal +*/ +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ +html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{-webkit-box-sizing:content-box;box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{-webkit-box-sizing:border-box;box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{-webkit-box-sizing:border-box;box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}html{font-family:sans-serif}.hidden,[hidden]{display:none!important}.pure-img{max-width:100%;height:auto;display:block}.pure-g{letter-spacing:-.31em;text-rendering:optimizespeed;font-family:FreeSans,Arimo,"Droid Sans",Helvetica,Arial,sans-serif;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-line-pack:start;align-content:flex-start}@media all and (-ms-high-contrast:none),(-ms-high-contrast:active){table .pure-g{display:block}}.opera-only :-o-prefocus,.pure-g{word-spacing:-0.43em}.pure-u{display:inline-block;letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-g [class*=pure-u]{font-family:sans-serif}.pure-u-1,.pure-u-1-1,.pure-u-1-12,.pure-u-1-2,.pure-u-1-24,.pure-u-1-3,.pure-u-1-4,.pure-u-1-5,.pure-u-1-6,.pure-u-1-8,.pure-u-10-24,.pure-u-11-12,.pure-u-11-24,.pure-u-12-24,.pure-u-13-24,.pure-u-14-24,.pure-u-15-24,.pure-u-16-24,.pure-u-17-24,.pure-u-18-24,.pure-u-19-24,.pure-u-2-24,.pure-u-2-3,.pure-u-2-5,.pure-u-20-24,.pure-u-21-24,.pure-u-22-24,.pure-u-23-24,.pure-u-24-24,.pure-u-3-24,.pure-u-3-4,.pure-u-3-5,.pure-u-3-8,.pure-u-4-24,.pure-u-4-5,.pure-u-5-12,.pure-u-5-24,.pure-u-5-5,.pure-u-5-6,.pure-u-5-8,.pure-u-6-24,.pure-u-7-12,.pure-u-7-24,.pure-u-7-8,.pure-u-8-24,.pure-u-9-24{display:inline-block;letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-u-1-24{width:4.1667%}.pure-u-1-12,.pure-u-2-24{width:8.3333%}.pure-u-1-8,.pure-u-3-24{width:12.5%}.pure-u-1-6,.pure-u-4-24{width:16.6667%}.pure-u-1-5{width:20%}.pure-u-5-24{width:20.8333%}.pure-u-1-4,.pure-u-6-24{width:25%}.pure-u-7-24{width:29.1667%}.pure-u-1-3,.pure-u-8-24{width:33.3333%}.pure-u-3-8,.pure-u-9-24{width:37.5%}.pure-u-2-5{width:40%}.pure-u-10-24,.pure-u-5-12{width:41.6667%}.pure-u-11-24{width:45.8333%}.pure-u-1-2,.pure-u-12-24{width:50%}.pure-u-13-24{width:54.1667%}.pure-u-14-24,.pure-u-7-12{width:58.3333%}.pure-u-3-5{width:60%}.pure-u-15-24,.pure-u-5-8{width:62.5%}.pure-u-16-24,.pure-u-2-3{width:66.6667%}.pure-u-17-24{width:70.8333%}.pure-u-18-24,.pure-u-3-4{width:75%}.pure-u-19-24{width:79.1667%}.pure-u-4-5{width:80%}.pure-u-20-24,.pure-u-5-6{width:83.3333%}.pure-u-21-24,.pure-u-7-8{width:87.5%}.pure-u-11-12,.pure-u-22-24{width:91.6667%}.pure-u-23-24{width:95.8333%}.pure-u-1,.pure-u-1-1,.pure-u-24-24,.pure-u-5-5{width:100%}.pure-button{display:inline-block;line-height:normal;white-space:nowrap;vertical-align:middle;text-align:center;cursor:pointer;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-box-sizing:border-box;box-sizing:border-box}.pure-button::-moz-focus-inner{padding:0;border:0}.pure-button-group{letter-spacing:-.31em;text-rendering:optimizespeed}.opera-only :-o-prefocus,.pure-button-group{word-spacing:-0.43em}.pure-button-group .pure-button{letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-button{font-family:inherit;font-size:100%;padding:.5em 1em;color:rgba(0,0,0,.8);border:none transparent;background-color:#e6e6e6;text-decoration:none;border-radius:2px}.pure-button-hover,.pure-button:focus,.pure-button:hover{background-image:-webkit-gradient(linear,left top,left bottom,from(transparent),color-stop(40%,rgba(0,0,0,.05)),to(rgba(0,0,0,.1)));background-image:linear-gradient(transparent,rgba(0,0,0,.05) 40%,rgba(0,0,0,.1))}.pure-button:focus{outline:0}.pure-button-active,.pure-button:active{-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.15) inset,0 0 6px rgba(0,0,0,.2) inset;box-shadow:0 0 0 1px rgba(0,0,0,.15) inset,0 0 6px rgba(0,0,0,.2) inset;border-color:#000}.pure-button-disabled,.pure-button-disabled:active,.pure-button-disabled:focus,.pure-button-disabled:hover,.pure-button[disabled]{border:none;background-image:none;opacity:.4;cursor:not-allowed;-webkit-box-shadow:none;box-shadow:none;pointer-events:none}.pure-button-hidden{display:none}.pure-button-primary,.pure-button-selected,a.pure-button-primary,a.pure-button-selected{background-color:#0078e7;color:#fff}.pure-button-group .pure-button{margin:0;border-radius:0;border-right:1px solid rgba(0,0,0,.2)}.pure-button-group .pure-button:first-child{border-top-left-radius:2px;border-bottom-left-radius:2px}.pure-button-group .pure-button:last-child{border-top-right-radius:2px;border-bottom-right-radius:2px;border-right:none}.pure-form input[type=color],.pure-form input[type=date],.pure-form input[type=datetime-local],.pure-form input[type=datetime],.pure-form input[type=email],.pure-form input[type=month],.pure-form input[type=number],.pure-form input[type=password],.pure-form input[type=search],.pure-form input[type=tel],.pure-form input[type=text],.pure-form input[type=time],.pure-form input[type=url],.pure-form input[type=week],.pure-form select,.pure-form textarea{padding:.5em .6em;display:inline-block;border:1px solid #ccc;-webkit-box-shadow:inset 0 1px 3px #ddd;box-shadow:inset 0 1px 3px #ddd;border-radius:4px;vertical-align:middle;-webkit-box-sizing:border-box;box-sizing:border-box}.pure-form input:not([type]){padding:.5em .6em;display:inline-block;border:1px solid #ccc;-webkit-box-shadow:inset 0 1px 3px #ddd;box-shadow:inset 0 1px 3px #ddd;border-radius:4px;-webkit-box-sizing:border-box;box-sizing:border-box}.pure-form input[type=color]{padding:.2em .5em}.pure-form input[type=color]:focus,.pure-form input[type=date]:focus,.pure-form input[type=datetime-local]:focus,.pure-form input[type=datetime]:focus,.pure-form input[type=email]:focus,.pure-form input[type=month]:focus,.pure-form input[type=number]:focus,.pure-form input[type=password]:focus,.pure-form input[type=search]:focus,.pure-form input[type=tel]:focus,.pure-form input[type=text]:focus,.pure-form input[type=time]:focus,.pure-form input[type=url]:focus,.pure-form input[type=week]:focus,.pure-form select:focus,.pure-form textarea:focus{outline:0;border-color:#129fea}.pure-form input:not([type]):focus{outline:0;border-color:#129fea}.pure-form input[type=checkbox]:focus,.pure-form input[type=file]:focus,.pure-form input[type=radio]:focus{outline:thin solid #129FEA;outline:1px auto #129FEA}.pure-form .pure-checkbox,.pure-form .pure-radio{margin:.5em 0;display:block}.pure-form input[type=color][disabled],.pure-form input[type=date][disabled],.pure-form input[type=datetime-local][disabled],.pure-form input[type=datetime][disabled],.pure-form input[type=email][disabled],.pure-form input[type=month][disabled],.pure-form input[type=number][disabled],.pure-form input[type=password][disabled],.pure-form input[type=search][disabled],.pure-form input[type=tel][disabled],.pure-form input[type=text][disabled],.pure-form input[type=time][disabled],.pure-form input[type=url][disabled],.pure-form input[type=week][disabled],.pure-form select[disabled],.pure-form textarea[disabled]{cursor:not-allowed;background-color:#eaeded;color:#cad2d3}.pure-form input:not([type])[disabled]{cursor:not-allowed;background-color:#eaeded;color:#cad2d3}.pure-form input[readonly],.pure-form select[readonly],.pure-form textarea[readonly]{background-color:#eee;color:#777;border-color:#ccc}.pure-form input:focus:invalid,.pure-form select:focus:invalid,.pure-form textarea:focus:invalid{color:#b94a48;border-color:#e9322d}.pure-form input[type=checkbox]:focus:invalid:focus,.pure-form input[type=file]:focus:invalid:focus,.pure-form input[type=radio]:focus:invalid:focus{outline-color:#e9322d}.pure-form select{height:2.25em;border:1px solid #ccc;background-color:#fff}.pure-form select[multiple]{height:auto}.pure-form label{margin:.5em 0 .2em}.pure-form fieldset{margin:0;padding:.35em 0 .75em;border:0}.pure-form legend{display:block;width:100%;padding:.3em 0;margin-bottom:.3em;color:#333;border-bottom:1px solid #e5e5e5}.pure-form-stacked input[type=color],.pure-form-stacked input[type=date],.pure-form-stacked input[type=datetime-local],.pure-form-stacked input[type=datetime],.pure-form-stacked input[type=email],.pure-form-stacked input[type=file],.pure-form-stacked input[type=month],.pure-form-stacked input[type=number],.pure-form-stacked input[type=password],.pure-form-stacked input[type=search],.pure-form-stacked input[type=tel],.pure-form-stacked input[type=text],.pure-form-stacked input[type=time],.pure-form-stacked input[type=url],.pure-form-stacked input[type=week],.pure-form-stacked label,.pure-form-stacked select,.pure-form-stacked textarea{display:block;margin:.25em 0}.pure-form-stacked input:not([type]){display:block;margin:.25em 0}.pure-form-aligned input,.pure-form-aligned select,.pure-form-aligned textarea,.pure-form-message-inline{display:inline-block;vertical-align:middle}.pure-form-aligned textarea{vertical-align:top}.pure-form-aligned .pure-control-group{margin-bottom:.5em}.pure-form-aligned .pure-control-group label{text-align:right;display:inline-block;vertical-align:middle;width:10em;margin:0 1em 0 0}.pure-form-aligned .pure-controls{margin:1.5em 0 0 11em}.pure-form .pure-input-rounded,.pure-form input.pure-input-rounded{border-radius:2em;padding:.5em 1em}.pure-form .pure-group fieldset{margin-bottom:10px}.pure-form .pure-group input,.pure-form .pure-group textarea{display:block;padding:10px;margin:0 0 -1px;border-radius:0;position:relative;top:-1px}.pure-form .pure-group input:focus,.pure-form .pure-group textarea:focus{z-index:3}.pure-form .pure-group input:first-child,.pure-form .pure-group textarea:first-child{top:1px;border-radius:4px 4px 0 0;margin:0}.pure-form .pure-group input:first-child:last-child,.pure-form .pure-group textarea:first-child:last-child{top:1px;border-radius:4px;margin:0}.pure-form .pure-group input:last-child,.pure-form .pure-group textarea:last-child{top:-2px;border-radius:0 0 4px 4px;margin:0}.pure-form .pure-group button{margin:.35em 0}.pure-form .pure-input-1{width:100%}.pure-form .pure-input-3-4{width:75%}.pure-form .pure-input-2-3{width:66%}.pure-form .pure-input-1-2{width:50%}.pure-form .pure-input-1-3{width:33%}.pure-form .pure-input-1-4{width:25%}.pure-form-message-inline{display:inline-block;padding-left:.3em;color:#666;vertical-align:middle;font-size:.875em}.pure-form-message{display:block;color:#666;font-size:.875em}@media only screen and (max-width :480px){.pure-form button[type=submit]{margin:.7em 0 0}.pure-form input:not([type]),.pure-form input[type=color],.pure-form input[type=date],.pure-form input[type=datetime-local],.pure-form input[type=datetime],.pure-form input[type=email],.pure-form input[type=month],.pure-form input[type=number],.pure-form input[type=password],.pure-form input[type=search],.pure-form input[type=tel],.pure-form input[type=text],.pure-form input[type=time],.pure-form input[type=url],.pure-form input[type=week],.pure-form label{margin-bottom:.3em;display:block}.pure-group input:not([type]),.pure-group input[type=color],.pure-group input[type=date],.pure-group input[type=datetime-local],.pure-group input[type=datetime],.pure-group input[type=email],.pure-group input[type=month],.pure-group input[type=number],.pure-group input[type=password],.pure-group input[type=search],.pure-group input[type=tel],.pure-group input[type=text],.pure-group input[type=time],.pure-group input[type=url],.pure-group input[type=week]{margin-bottom:0}.pure-form-aligned .pure-control-group label{margin-bottom:.3em;text-align:left;display:block;width:100%}.pure-form-aligned .pure-controls{margin:1.5em 0 0 0}.pure-form-message,.pure-form-message-inline{display:block;font-size:.75em;padding:.2em 0 .8em}}.pure-menu{-webkit-box-sizing:border-box;box-sizing:border-box}.pure-menu-fixed{position:fixed;left:0;top:0;z-index:3}.pure-menu-item,.pure-menu-list{position:relative}.pure-menu-list{list-style:none;margin:0;padding:0}.pure-menu-item{padding:0;margin:0;height:100%}.pure-menu-heading,.pure-menu-link{display:block;text-decoration:none;white-space:nowrap}.pure-menu-horizontal{width:100%;white-space:nowrap}.pure-menu-horizontal .pure-menu-list{display:inline-block}.pure-menu-horizontal .pure-menu-heading,.pure-menu-horizontal .pure-menu-item,.pure-menu-horizontal .pure-menu-separator{display:inline-block;vertical-align:middle}.pure-menu-item .pure-menu-item{display:block}.pure-menu-children{display:none;position:absolute;left:100%;top:0;margin:0;padding:0;z-index:3}.pure-menu-horizontal .pure-menu-children{left:0;top:auto;width:inherit}.pure-menu-active>.pure-menu-children,.pure-menu-allow-hover:hover>.pure-menu-children{display:block;position:absolute}.pure-menu-has-children>.pure-menu-link:after{padding-left:.5em;content:"\25B8";font-size:small}.pure-menu-horizontal .pure-menu-has-children>.pure-menu-link:after{content:"\25BE"}.pure-menu-scrollable{overflow-y:scroll;overflow-x:hidden}.pure-menu-scrollable .pure-menu-list{display:block}.pure-menu-horizontal.pure-menu-scrollable .pure-menu-list{display:inline-block}.pure-menu-horizontal.pure-menu-scrollable{white-space:nowrap;overflow-y:hidden;overflow-x:auto;padding:.5em 0}.pure-menu-horizontal .pure-menu-children .pure-menu-separator,.pure-menu-separator{background-color:#ccc;height:1px;margin:.3em 0}.pure-menu-horizontal .pure-menu-separator{width:1px;height:1.3em;margin:0 .3em}.pure-menu-horizontal .pure-menu-children .pure-menu-separator{display:block;width:auto}.pure-menu-heading{text-transform:uppercase;color:#565d64}.pure-menu-link{color:#777}.pure-menu-children{background-color:#fff}.pure-menu-heading,.pure-menu-link{padding:.5em 1em}.pure-menu-disabled{opacity:.5}.pure-menu-disabled .pure-menu-link:hover{background-color:transparent;cursor:default}.pure-menu-active>.pure-menu-link,.pure-menu-link:focus,.pure-menu-link:hover{background-color:#eee}.pure-menu-selected>.pure-menu-link,.pure-menu-selected>.pure-menu-link:visited{color:#000}.pure-table{border-collapse:collapse;border-spacing:0;empty-cells:show;border:1px solid #cbcbcb}.pure-table caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.pure-table td,.pure-table th{border-left:1px solid #cbcbcb;border-width:0 0 0 1px;font-size:inherit;margin:0;overflow:visible;padding:.5em 1em}.pure-table thead{background-color:#e0e0e0;color:#000;text-align:left;vertical-align:bottom}.pure-table td{background-color:transparent}.pure-table-odd td{background-color:#f2f2f2}.pure-table-striped tr:nth-child(2n-1) td{background-color:#f2f2f2}.pure-table-bordered td{border-bottom:1px solid #cbcbcb}.pure-table-bordered tbody>tr:last-child>td{border-bottom-width:0}.pure-table-horizontal td,.pure-table-horizontal th{border-width:0 0 1px 0;border-bottom:1px solid #cbcbcb}.pure-table-horizontal tbody>tr:last-child>td{border-bottom-width:0} diff --git a/examples/control_focus.rs b/examples/control_focus.rs index 538bfc3f1b..64b840a1e5 100644 --- a/examples/control_focus.rs +++ b/examples/control_focus.rs @@ -6,8 +6,6 @@ use dioxus::prelude::*; use std::rc::Rc; -const STYLE: Asset = asset!("/examples/assets/roulette.css"); - fn main() { dioxus::launch(app); } @@ -38,7 +36,7 @@ fn app() -> Element { }); rsx! { - document::Stylesheet { href: STYLE } + document::Stylesheet { href: asset!("/examples/assets/roulette.css") } h1 { "Input Roulette" } button { onclick: move |_| running.toggle(), "Toggle roulette" } div { id: "roulette-grid", diff --git a/examples/counters.rs b/examples/counters.rs index 739778e72a..63013a1cc5 100644 --- a/examples/counters.rs +++ b/examples/counters.rs @@ -2,8 +2,6 @@ use dioxus::prelude::*; -const STYLE: Asset = asset!("/examples/assets/counter.css"); - fn main() { dioxus::launch(app); } @@ -16,7 +14,7 @@ fn app() -> Element { let sum = use_memo(move || counters.read().iter().copied().sum::()); rsx! { - document::Stylesheet { href: STYLE } + document::Stylesheet { href: asset!("/examples/assets/counter.css") } div { id: "controls", button { onclick: move |_| counters.write().push(0), "Add counter" } diff --git a/examples/crm.rs b/examples/crm.rs index 96f265587d..ca83f58d40 100644 --- a/examples/crm.rs +++ b/examples/crm.rs @@ -20,13 +20,8 @@ fn main() { })) .launch(|| { rsx! { - document::Link { - rel: "stylesheet", - href: "https://unpkg.com/purecss@2.0.6/build/pure-min.css", - integrity: "sha384-Uu6IeWbM+gzNVXJcM9XV3SohHtmWE+3VGi496jvgX1jyvDTXfdK+rfZc8C1Aehk5", - crossorigin: "anonymous" - } document::Stylesheet { href: asset!("/examples/assets/crm.css") } + document::Link { href: asset!("/examples/assets/purecss.css") } h1 { "Dioxus CRM Example" } Router:: {} } diff --git a/examples/custom_assets.rs b/examples/custom_assets.rs index c9cfe7bad7..8a56530110 100644 --- a/examples/custom_assets.rs +++ b/examples/custom_assets.rs @@ -16,7 +16,7 @@ use dioxus::prelude::*; /// /// When used with web apps, manganis will detect the import of the image, optimize it, and put it /// in the output dist folder in the right location, ensuring no two images have the same name. -static ASSET_PATH: ImageAsset = asset!("/examples/assets/logo.png".image().format(ImageType::Avif)); +static IMAGE: ImageAsset = asset!("/examples/assets/logo.png".image().format(ImageType::Avif)); fn main() { dioxus::launch(app); @@ -26,7 +26,7 @@ fn app() -> Element { rsx! { div { h1 { "This should show an image:" } - img { src: ASSET_PATH } + img { src: IMAGE } // temporarily keep support for these too img { src: "/Users/jonkelley/Development/dioxus/examples/assets/logo.png" } diff --git a/examples/dynamic_asset.rs b/examples/dynamic_asset.rs index cca5efb76c..b741ba57af 100644 --- a/examples/dynamic_asset.rs +++ b/examples/dynamic_asset.rs @@ -7,8 +7,6 @@ use dioxus::desktop::{use_asset_handler, wry::http::Response}; use dioxus::prelude::*; -const STYLE: Asset = asset!("/examples/assets/custom_assets.css"); - fn main() { dioxus::launch(app); } @@ -24,7 +22,7 @@ fn app() -> Element { }); rsx! { - document::Stylesheet { href: STYLE } + document::Stylesheet { href: asset!("/examples/assets/custom_assets.css") } h1 { "Dynamic Assets" } img { src: "/logos/logo.png" } } diff --git a/examples/errors.rs b/examples/errors.rs index a7319e0170..d4002c8100 100644 --- a/examples/errors.rs +++ b/examples/errors.rs @@ -11,7 +11,11 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(|| rsx! { Router:: {} }); + dioxus::launch(|| { + rsx! { + Router:: {} + } + }); } /// You can use an ErrorBoundary to catch errors in children and display a warning @@ -34,11 +38,8 @@ fn ParseNumber() -> Element { h1 { "Error handler demo" } button { onclick: move |_| { - // You can return a result from an event handler which lets you easily quit rendering early if something fails let data: i32 = "0.5".parse()?; - println!("parsed {data}"); - Ok(()) }, "Click to throw an error" @@ -58,10 +59,7 @@ fn Show() -> Element { if let Some(error) = error.show() { {error} } else { - pre { - color: "red", - "{error}" - } + pre { color: "red", "{error}" } } } } @@ -79,8 +77,8 @@ fn ParseNumberWithShow() -> Element { button { onclick: move |_| { let request_data = "0.5"; - let data: i32 = request_data.parse() - // You can attach rsx to results that can be displayed in the Error Boundary + let data: i32 = request_data + .parse() .show(|_| rsx! { div { background_color: "red", @@ -88,15 +86,10 @@ fn ParseNumberWithShow() -> Element { border_width: "2px", border_radius: "5px", p { "Failed to parse data" } - Link { - to: Route::Home {}, - "Go back to the homepage" - } + Link { to: Route::Home {}, "Go back to the homepage" } } })?; - println!("parsed {data}"); - Ok(()) }, "Click to throw an error" @@ -139,22 +132,13 @@ fn Home() -> Element { rsx! { ul { li { - Link { - to: Route::Simple {}, - "Simple errors" - } + Link { to: Route::Simple {}, "Simple errors" } } li { - Link { - to: Route::Panic {}, - "Capture panics" - } + Link { to: Route::Panic {}, "Capture panics" } } li { - Link { - to: Route::Show {}, - "Show errors" - } + Link { to: Route::Show {}, "Show errors" } } } } diff --git a/packages/desktop/examples/stress.rs b/packages/desktop/examples/stress.rs deleted file mode 100644 index 4ad317617c..0000000000 --- a/packages/desktop/examples/stress.rs +++ /dev/null @@ -1,23 +0,0 @@ -use dioxus::prelude::*; - -fn main() { - dioxus::launch(app); -} - -fn app() -> Element { - let mut state = use_signal(|| 0); - - use_future(move || async move { - loop { - state += 1; - tokio::time::sleep(std::time::Duration::from_millis(1)).await; - } - }); - - rsx! { - button { onclick: move |_| state.set(0), "reset" } - for _ in 0..10000 { - div { "hello desktop! {state}" } - } - } -} From 34dd5af120b11a14ea7ba303ca3329bd4b1bd0e1 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 6 Sep 2024 13:04:36 -0700 Subject: [PATCH 092/139] pull out router --- Cargo.lock | 9 ++++++ Cargo.toml | 1 + example-projects/liveview-router/.gitignore | 4 +++ example-projects/liveview-router/Cargo.toml | 15 +++++++++ .../liveview-router/src/main.rs | 12 ++----- packages/dioxus/src/lib.rs | 4 +++ packages/document/src/document.rs | 8 +++++ packages/router/src/components/link.rs | 2 +- packages/router/src/contexts/router.rs | 32 +++++++------------ 9 files changed, 57 insertions(+), 30 deletions(-) create mode 100644 example-projects/liveview-router/.gitignore create mode 100644 example-projects/liveview-router/Cargo.toml rename packages/router/examples/simple_routes.rs => example-projects/liveview-router/src/main.rs (93%) diff --git a/Cargo.lock b/Cargo.lock index c4c0268a45..f4af7347e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5881,6 +5881,15 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "liveview-router" +version = "0.1.0" +dependencies = [ + "axum 0.7.5", + "dioxus", + "tokio", +] + [[package]] name = "lock_api" version = "0.4.12" diff --git a/Cargo.toml b/Cargo.toml index 53eeb921d3..54b1e07f2e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,6 +71,7 @@ members = [ "example-projects/tailwind", "example-projects/pwa", "example-projects/basic-liveview", + "example-projects/liveview-router", ] [workspace.package] diff --git a/example-projects/liveview-router/.gitignore b/example-projects/liveview-router/.gitignore new file mode 100644 index 0000000000..21fff11dd3 --- /dev/null +++ b/example-projects/liveview-router/.gitignore @@ -0,0 +1,4 @@ +dist +target +static +.dioxus \ No newline at end of file diff --git a/example-projects/liveview-router/Cargo.toml b/example-projects/liveview-router/Cargo.toml new file mode 100644 index 0000000000..e6d20a9135 --- /dev/null +++ b/example-projects/liveview-router/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "liveview-router" +version = "0.1.0" +edition = "2021" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +dioxus = { workspace = true, features = ["liveview", "router"] } +axum = { workspace = true } +tokio = { workspace = true, features = ["full"] } + +[features] +default = [] diff --git a/packages/router/examples/simple_routes.rs b/example-projects/liveview-router/src/main.rs similarity index 93% rename from packages/router/examples/simple_routes.rs rename to example-projects/liveview-router/src/main.rs index fc91cd00b5..dff351a75d 100644 --- a/packages/router/examples/simple_routes.rs +++ b/example-projects/liveview-router/src/main.rs @@ -1,13 +1,12 @@ use dioxus::prelude::*; use std::str::FromStr; -#[cfg(feature = "liveview")] #[tokio::main] async fn main() { use axum::{extract::ws::WebSocketUpgrade, response::Html, routing::get, Router}; let listen_address: std::net::SocketAddr = ([127, 0, 0, 1], 3030).into(); - let view = dioxus_liveview::LiveViewPool::new(); + let view = liveview::LiveViewPool::new(); let app = Router::new() .fallback(get(move || async move { Html(format!( @@ -19,14 +18,14 @@ async fn main() { {glue} "#, - glue = dioxus_liveview::interpreter_glue(&format!("ws://{listen_address}/ws")) + glue = liveview::interpreter_glue(&format!("ws://{listen_address}/ws")) )) })) .route( "/ws", get(move |ws: WebSocketUpgrade| async move { ws.on_upgrade(move |socket| async move { - _ = view.launch(dioxus_liveview::axum_socket(socket), app).await; + _ = view.launch(liveview::axum_socket(socket), app).await; }) }), ); @@ -42,11 +41,6 @@ async fn main() { .unwrap(); } -#[cfg(not(feature = "liveview"))] -fn main() { - dioxus::launch(app) -} - fn app() -> Element { rsx! { Router:: {} diff --git a/packages/dioxus/src/lib.rs b/packages/dioxus/src/lib.rs index 3aefcf8ea0..733862a33b 100644 --- a/packages/dioxus/src/lib.rs +++ b/packages/dioxus/src/lib.rs @@ -163,4 +163,8 @@ pub mod prelude { #[cfg(feature = "document")] #[cfg_attr(docsrs, doc(cfg(feature = "document")))] pub use dioxus_document as document; + + #[cfg(feature = "liveview")] + #[cfg_attr(docsrs, doc(cfg(feature = "liveview")))] + pub use dioxus_liveview as liveview; } diff --git a/packages/document/src/document.rs b/packages/document/src/document.rs index 1284172982..f19b89c324 100644 --- a/packages/document/src/document.rs +++ b/packages/document/src/document.rs @@ -291,4 +291,12 @@ pub trait Document: 'static { /// updates are received, they should call `callback`, which will cause the router to update. #[allow(unused_variables)] fn updater(&self, callback: Arc) {} + + /// Whether the history provider is synchronous or asynchronous. + /// + /// Generally we only support synchronous history providers, but some platforms like liveview + /// may need to be asynchronous. + fn is_synchronous(&self) -> bool { + true + } } diff --git a/packages/router/src/components/link.rs b/packages/router/src/components/link.rs index 5b0ae4524c..6fa428a4bd 100644 --- a/packages/router/src/components/link.rs +++ b/packages/router/src/components/link.rs @@ -229,7 +229,7 @@ pub fn Link(props: LinkProps) -> Element { let liveview_prevent_default = { // If the event is a click with the left mouse button and no modifiers, prevent the default action // and navigate to the href with client side routing - router.is_liveview().then_some( + router.is_synchronous().then_some( "if (event.button === 0 && !event.ctrlKey && !event.metaKey && !event.shiftKey && !event.altKey) { event.preventDefault() }" ) }; diff --git a/packages/router/src/contexts/router.rs b/packages/router/src/contexts/router.rs index 89a9dd8b3d..b836c2557c 100644 --- a/packages/router/src/contexts/router.rs +++ b/packages/router/src/contexts/router.rs @@ -1,4 +1,3 @@ -use core::panic; use std::{ any::Any, collections::HashSet, @@ -29,7 +28,7 @@ struct RouterContextInner { runtime: Rc, - history: DocumentContext, + document: DocumentContext, unresolved_error: Option, @@ -51,7 +50,7 @@ impl RouterContext { basepath: Default::default(), unresolved_error: None, subscribers: subscribers.clone(), - history: todo!(), + document: todo!(), // history: cfg.take_history(), // subscriber_update, @@ -97,7 +96,7 @@ impl RouterContext { // set the updater { let rt = myself.runtime.clone(); - myself.history.updater(Arc::new(move || { + myself.document.updater(Arc::new(move || { for &id in subscribers.read().unwrap().iter() { rt.mark_dirty(id); } @@ -111,15 +110,8 @@ impl RouterContext { /// Check if the router is running in a liveview context /// We do some slightly weird things for liveview because of the network boundary - pub fn is_liveview(&self) -> bool { - #[cfg(feature = "liveview")] - { - self.inner.read().history.is_liveview() - } - #[cfg(not(feature = "liveview"))] - { - false - } + pub fn is_synchronous(&self) -> bool { + self.inner.read().document.is_synchronous() } pub(crate) fn route_from_str(&self, route: &str) -> Result { @@ -130,13 +122,13 @@ impl RouterContext { /// Check whether there is a previous page to navigate back to. #[must_use] pub fn can_go_back(&self) -> bool { - self.inner.read().history.can_go_back() + self.inner.read().document.can_go_back() } /// Check whether there is a future page to navigate forward to. #[must_use] pub fn can_go_forward(&self) -> bool { - self.inner.read().history.can_go_forward() + self.inner.read().document.can_go_forward() } /// Go back to the previous location. @@ -144,7 +136,7 @@ impl RouterContext { /// Will fail silently if there is no previous location to go to. pub fn go_back(&self) { { - self.inner.write_unchecked().history.go_back(); + self.inner.write_unchecked().document.go_back(); } self.change_route(); @@ -155,7 +147,7 @@ impl RouterContext { /// Will fail silently if there is no next location to go to. pub fn go_forward(&self) { { - self.inner.write_unchecked().history.go_forward(); + self.inner.write_unchecked().document.go_forward(); } self.change_route(); @@ -169,7 +161,7 @@ impl RouterContext { NavigationTarget::Internal(p) => self .inner .write_unchecked() - .history + .document .push_route(p.serialize()), NavigationTarget::External(e) => return self.navigate_external(e), } @@ -225,7 +217,7 @@ impl RouterContext { /// The route that is currently active. pub fn current_route_string(&self) -> String { - self.inner.read_unchecked().history.current_route() + self.inner.read_unchecked().document.current_route() } pub(crate) fn any_route_to_string(&self, route: &dyn Any) -> String { @@ -317,7 +309,7 @@ impl RouterContext { fn navigate_external(&self, external: String) -> Option { let failure = { let mut myself = self.inner.write_unchecked(); - match myself.history.navigate_external(external.clone()) { + match myself.document.navigate_external(external.clone()) { true => None, false => { let failure = ExternalNavigationFailure(external); From 528a94c3e985f9ed553615c906df563efdece78e Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 6 Sep 2024 13:36:48 -0700 Subject: [PATCH 093/139] cut out hotreload from rsx --- Cargo.lock | 18 ++- Cargo.toml | 2 + packages/cli/Cargo.toml | 3 +- .../cli/src/serve/hot_reloading_file_map.rs | 8 +- packages/html/Cargo.toml | 3 +- packages/liveview/src/document.rs | 82 +++++++++++-- packages/liveview/src/history.rs | 7 +- packages/liveview/src/lib.rs | 6 +- packages/router/src/contexts/router.rs | 46 ++++---- packages/rsx-hotreload/Cargo.toml | 19 +++ .../src}/collect.rs | 0 .../src}/context.rs | 0 .../hot_reload => rsx-hotreload/src}/diff.rs | 8 +- packages/rsx-hotreload/src/extensions.rs | 111 ++++++++++++++++++ .../src}/last_build_state.rs | 2 +- packages/rsx-hotreload/src/lib.rs | 9 ++ packages/rsx/Cargo.toml | 5 - packages/rsx/src/attribute.rs | 46 -------- packages/rsx/src/hot_reload/mod.rs | 12 -- packages/rsx/src/lib.rs | 2 - packages/rsx/src/node.rs | 48 -------- packages/rsx/src/template_body.rs | 4 +- packages/rsx/src/text_node.rs | 13 -- packages/rsx/src/util.rs | 11 -- 24 files changed, 268 insertions(+), 197 deletions(-) create mode 100644 packages/rsx-hotreload/Cargo.toml rename packages/{rsx/src/hot_reload => rsx-hotreload/src}/collect.rs (100%) rename packages/{rsx/src/hot_reload => rsx-hotreload/src}/context.rs (100%) rename packages/{rsx/src/hot_reload => rsx-hotreload/src}/diff.rs (99%) create mode 100644 packages/rsx-hotreload/src/extensions.rs rename packages/{rsx/src/hot_reload => rsx-hotreload/src}/last_build_state.rs (99%) create mode 100644 packages/rsx-hotreload/src/lib.rs delete mode 100644 packages/rsx/src/hot_reload/mod.rs diff --git a/Cargo.lock b/Cargo.lock index f4af7347e7..461eaf3562 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2412,6 +2412,7 @@ dependencies = [ "dioxus-fullstack", "dioxus-html", "dioxus-rsx", + "dioxus-rsx-hotreload", "dioxus-runtime-config", "dirs", "env_logger", @@ -2710,6 +2711,7 @@ dependencies = [ "dioxus-core-types", "dioxus-html-internal-macro", "dioxus-rsx", + "dioxus-rsx-hotreload", "dioxus-web", "enumset", "euclid", @@ -2910,12 +2912,24 @@ dependencies = [ [[package]] name = "dioxus-rsx" version = "0.6.0-alpha.2" +dependencies = [ + "prettier-please", + "prettyplease", + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn 2.0.75", + "tracing", +] + +[[package]] +name = "dioxus-rsx-hotreload" +version = "0.6.0-alpha.2" dependencies = [ "dioxus-core", "dioxus-core-types", + "dioxus-rsx", "internment", - "prettier-please", - "prettyplease", "proc-macro2", "proc-macro2-diagnostics", "quote", diff --git a/Cargo.toml b/Cargo.toml index 54b1e07f2e..5fac4971df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,7 @@ members = [ "packages/isrg", "packages/manganis-core", "packages/server", + "packages/rsx-hotreload", # Static generation examples "packages/static-generation/examples/simple", @@ -100,6 +101,7 @@ dioxus-liveview = { path = "packages/liveview", version = "0.6.0-alpha.2" } dioxus-autofmt = { path = "packages/autofmt", version = "0.6.0-alpha.2" } dioxus-check = { path = "packages/check", version = "0.6.0-alpha.2" } dioxus-rsx = { path = "packages/rsx", version = "0.6.0-alpha.2" } +dioxus-rsx-hotreload = { path = "packages/rsx-hotreload", version = "0.6.0-alpha.2" } rsx-rosetta = { path = "packages/rsx-rosetta", version = "0.6.0-alpha.2" } dioxus-signals = { path = "packages/signals", version = "0.6.0-alpha.2" } dioxus-devtools = { path = "packages/devtools", version = "0.6.0-alpha.2" } diff --git a/packages/cli/Cargo.toml b/packages/cli/Cargo.toml index 3bcf1dc5c6..15654549d6 100644 --- a/packages/cli/Cargo.toml +++ b/packages/cli/Cargo.toml @@ -86,7 +86,8 @@ brotli = "6.0.0" dioxus-autofmt = { workspace = true } dioxus-check = { workspace = true } rsx-rosetta = { workspace = true } -dioxus-rsx = { workspace = true, features = ["hot_reload"] } +dioxus-rsx = { workspace = true } +dioxus-rsx-hotreload = { workspace = true } dioxus-html = { workspace = true, features = ["hot-reload-context"] } dioxus-core = { workspace = true, features = ["serialize"] } dioxus-core-types = { workspace = true } diff --git a/packages/cli/src/serve/hot_reloading_file_map.rs b/packages/cli/src/serve/hot_reloading_file_map.rs index 0659047427..8d1129c017 100644 --- a/packages/cli/src/serve/hot_reloading_file_map.rs +++ b/packages/cli/src/serve/hot_reloading_file_map.rs @@ -1,9 +1,7 @@ use dioxus_core::internal::{HotReloadTemplateWithLocation, HotReloadedTemplate}; use dioxus_core_types::HotReloadingContext; -use dioxus_rsx::{ - hot_reload::{diff_rsx, ChangedRsx}, - CallBody, -}; +use dioxus_rsx::CallBody; +use dioxus_rsx_hotreload::{diff_rsx, ChangedRsx, HotReloadResult}; use krates::cm::MetadataCommand; use krates::Cmd; pub(crate) use std::collections::HashMap; @@ -156,7 +154,7 @@ impl FileMap { let template_location = template_location(old_start, file); // Returns a list of templates that are hotreloadable - let hotreload_result = dioxus_rsx::hot_reload::HotReloadResult::new::( + let hotreload_result = HotReloadResult::new::( &old_call_body.body, &new_call_body.body, template_location.clone(), diff --git a/packages/html/Cargo.toml b/packages/html/Cargo.toml index 9ac8b98dd2..2aa6204c97 100644 --- a/packages/html/Cargo.toml +++ b/packages/html/Cargo.toml @@ -13,6 +13,7 @@ keywords = ["dom", "ui", "gui", "react"] dioxus-core = { workspace = true } dioxus-core-types = { workspace = true } dioxus-rsx = { workspace = true, optional = true } +dioxus-rsx-hotreload = { workspace = true, optional = true } dioxus-html-internal-macro = { workspace = true } serde = { version = "1", features = ["derive"], optional = true } @@ -69,7 +70,7 @@ file-engine = [ # "web-sys?/FileList", # "web-sys?/FileReader" ] -hot-reload-context = ["dep:dioxus-rsx", "dioxus-rsx/hot_reload_traits"] +hot-reload-context = ["dep:dioxus-rsx", "dep:dioxus-rsx-hotreload"] html-to-rsx = [] [package.metadata.docs.rs] diff --git a/packages/liveview/src/document.rs b/packages/liveview/src/document.rs index e13234735b..6a0d66f167 100644 --- a/packages/liveview/src/document.rs +++ b/packages/liveview/src/document.rs @@ -1,19 +1,32 @@ +use crate::query::{Query, QueryEngine}; use dioxus_core::ScopeId; use dioxus_document::{Document, EvalError}; -use std::rc::Rc; - -use crate::query::{Query, QueryEngine}; +use std::{ + collections::BTreeMap, + rc::Rc, + sync::{Arc, RwLock}, +}; /// Provides the LiveviewDocument through [`ScopeId::provide_context`]. pub fn init_eval() { let query = ScopeId::ROOT.consume_context::().unwrap(); - let provider: Rc = Rc::new(LiveviewDocument { query }); + let provider: Rc = Rc::new(LiveviewDocument { + query, + action_tx: todo!(), + updater_callback: todo!(), + current_index: todo!(), + routes: todo!(), + }); ScopeId::ROOT.provide_context(provider); } -/// Reprints the liveview-target's provider of evaluators. +// The document impl for LiveView pub struct LiveviewDocument { query: QueryEngine, + action_tx: tokio::sync::mpsc::UnboundedSender, + updater_callback: Arc>>, + current_index: usize, + routes: BTreeMap, } impl Document for LiveviewDocument { @@ -34,23 +47,66 @@ impl Document for LiveviewDocument { todo!() } - fn current_route(&self) -> String { - todo!() - } - fn go_back(&self) { - todo!() + let _ = self.action_tx.send(Action::GoBack); } fn go_forward(&self) { - todo!() + let _ = self.action_tx.send(Action::GoForward); } fn push_route(&self, route: String) { - todo!() + let _ = self.action_tx.send(Action::Push(route)); + } + + fn replace_route(&self, route: String) { + let _ = self.action_tx.send(Action::Replace(route)); } - fn replace_route(&self, path: String) { + fn navigate_external(&self, url: String) -> bool { + let _ = self.action_tx.send(Action::External(url)); + true + } + + fn current_route(&self) -> String { + self.routes[&self.current_index].clone() + } + + fn can_go_back(&self) -> bool { + // Check if the one before is contiguous (i.e., not an external page) + let visited_indices: Vec = self.routes.keys().cloned().collect(); + visited_indices + .iter() + .position(|&rhs| self.current_index == rhs) + .map_or(false, |index| { + index > 0 && visited_indices[index - 1] == self.current_index - 1 + }) + } + + fn can_go_forward(&self) -> bool { + // let timeline = self.timeline.lock().expect("unpoisoned mutex"); + // Check if the one after is contiguous (i.e., not an external page) + let visited_indices: Vec = self.routes.keys().cloned().collect(); + visited_indices + .iter() + .rposition(|&rhs| self.current_index == rhs) + .map_or(false, |index| { + index < visited_indices.len() - 1 + && visited_indices[index + 1] == self.current_index + 1 + }) + } + + fn updater(&self, callback: Arc) { todo!() + // let mut updater_callback = self.updater_callback.write().unwrap(); + // *updater_callback = callback; } } + +enum Action { + GoBack, + GoForward, + Push(String), + Replace(String), + External(String), +} diff --git a/packages/liveview/src/history.rs b/packages/liveview/src/history.rs index 790b9d98e9..fe200beed7 100644 --- a/packages/liveview/src/history.rs +++ b/packages/liveview/src/history.rs @@ -1,7 +1,6 @@ -use super::HistoryProvider; -use crate::routable::Routable; -use dioxus_lib::prelude::*; -use document::UseEval; +// use crate::routable::Routable; +// use dioxus_lib::prelude::*; +// use document::UseEval; use serde::{Deserialize, Serialize}; use std::sync::{Mutex, RwLock}; use std::{collections::BTreeMap, rc::Rc, str::FromStr, sync::Arc}; diff --git a/packages/liveview/src/lib.rs b/packages/liveview/src/lib.rs index 84efd00a43..1f95324c82 100644 --- a/packages/liveview/src/lib.rs +++ b/packages/liveview/src/lib.rs @@ -7,15 +7,15 @@ mod config; mod document; mod element; mod events; -pub mod pool; +mod pool; mod query; -#[allow(unused_imports)] pub use adapters::*; pub use config::*; +pub use pool::*; + use dioxus_interpreter_js::NATIVE_JS; use futures_util::{SinkExt, StreamExt}; -pub use pool::*; #[cfg(feature = "axum")] pub mod launch; diff --git a/packages/router/src/contexts/router.rs b/packages/router/src/contexts/router.rs index b836c2557c..f404d6362f 100644 --- a/packages/router/src/contexts/router.rs +++ b/packages/router/src/contexts/router.rs @@ -1,5 +1,4 @@ use std::{ - any::Any, collections::HashSet, rc::Rc, sync::{Arc, RwLock}, @@ -36,9 +35,8 @@ struct RouterContextInner { failure_external_navigation: fn() -> Element, - any_route_to_string: fn(&dyn Any) -> String, - site_map: &'static [SiteMapSegment], + // any_route_to_string: fn(&dyn Any) -> String, // routing_callback: Option, } @@ -51,6 +49,9 @@ impl RouterContext { unresolved_error: None, subscribers: subscribers.clone(), document: todo!(), + failure_external_navigation: cfg.failure_external_navigation, + site_map: todo!(), + runtime: todo!(), // history: cfg.take_history(), // subscriber_update, @@ -70,26 +71,21 @@ impl RouterContext { // }) // as Arc Option>>> // }), - failure_external_navigation: cfg.failure_external_navigation, - - any_route_to_string: |route| { - todo!() - // route - // .downcast_ref::() - // .unwrap_or_else(|| { - // panic!( - // "Route is not of the expected type: {}\n found typeid: {:?}\n expected typeid: {:?}", - // std::any::type_name::(), - // route.type_id(), - // std::any::TypeId::of::() - // ) - // }) - // .to_string() - }, - - site_map: todo!(), + // any_route_to_string: |route| { + // todo!() + // route + // .downcast_ref::() + // .unwrap_or_else(|| { + // panic!( + // "Route is not of the expected type: {}\n found typeid: {:?}\n expected typeid: {:?}", + // std::any::type_name::(), + // route.type_id(), + // std::any::TypeId::of::() + // ) + // }) + // .to_string() + // }, // site_map: R::SITE_MAP, - runtime: todo!(), // routing_callback: todo!(), }; @@ -220,9 +216,9 @@ impl RouterContext { self.inner.read_unchecked().document.current_route() } - pub(crate) fn any_route_to_string(&self, route: &dyn Any) -> String { - (self.inner.read().any_route_to_string)(route) - } + // pub(crate) fn any_route_to_string(&self, route: &dyn Any) -> String { + // (self.inner.read().any_route_to_string)(route) + // } pub(crate) fn resolve_into_routable( &self, diff --git a/packages/rsx-hotreload/Cargo.toml b/packages/rsx-hotreload/Cargo.toml new file mode 100644 index 0000000000..6bead51b32 --- /dev/null +++ b/packages/rsx-hotreload/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "dioxus-rsx-hotreload" +edition = "2021" +version.workspace = true + +[dependencies] +dioxus-rsx = { workspace = true } +internment = { workspace = true } +proc-macro2 = { workspace = true, features = ["span-locations"] } +proc-macro2-diagnostics = { workspace = true } +quote = { workspace = true } +syn = { workspace = true, features = ["full", "extra-traits", "visit", "visit-mut"] } +tracing = { workspace = true } +dioxus-core = { workspace = true } +dioxus-core-types = { workspace = true } + +# internment = { workspace = true, optional = true } +# dioxus-core = { workspace = true, optional = true } # todo: this is being compiled in a proc macro +# dioxus-core-types = { workspace = true, optional = true } diff --git a/packages/rsx/src/hot_reload/collect.rs b/packages/rsx-hotreload/src/collect.rs similarity index 100% rename from packages/rsx/src/hot_reload/collect.rs rename to packages/rsx-hotreload/src/collect.rs diff --git a/packages/rsx/src/hot_reload/context.rs b/packages/rsx-hotreload/src/context.rs similarity index 100% rename from packages/rsx/src/hot_reload/context.rs rename to packages/rsx-hotreload/src/context.rs diff --git a/packages/rsx/src/hot_reload/diff.rs b/packages/rsx-hotreload/src/diff.rs similarity index 99% rename from packages/rsx/src/hot_reload/diff.rs rename to packages/rsx-hotreload/src/diff.rs index 79af95c65e..ca49521613 100644 --- a/packages/rsx/src/hot_reload/diff.rs +++ b/packages/rsx-hotreload/src/diff.rs @@ -63,14 +63,16 @@ //! The subproblem is optimal because the alternative is leaving less dynamic items for the remaining templates to hot reload which just makes it //! more difficult to match future templates. -use crate::innerlude::*; use dioxus_core::internal::{ FmtedSegments, HotReloadAttributeValue, HotReloadDynamicAttribute, HotReloadDynamicNode, HotReloadLiteral, HotReloadedTemplate, NamedAttribute, }; use dioxus_core_types::HotReloadingContext; +use dioxus_rsx::*; use std::collections::HashMap; +use crate::extensions::{html_tag_and_namespace, intern, to_template_node}; + use super::last_build_state::LastBuildState; /// A result of hot reloading @@ -193,7 +195,7 @@ impl HotReloadResult { let roots: Vec<_> = new .roots .iter() - .map(|node| node.to_template_node::()) + .map(|node| to_template_node::(node)) .collect(); let roots: &[dioxus_core::TemplateNode] = intern(&*roots); @@ -587,7 +589,7 @@ impl HotReloadResult { &mut self, attribute: &Attribute, ) -> Option<()> { - let (tag, namespace) = attribute.html_tag_and_namespace::(); + let (tag, namespace) = html_tag_and_namespace::(attribute); // If the attribute is a spread, try to grab it from the last build // If it wasn't in the last build with the same name, we can't hot reload it diff --git a/packages/rsx-hotreload/src/extensions.rs b/packages/rsx-hotreload/src/extensions.rs new file mode 100644 index 0000000000..88ab4d2b2d --- /dev/null +++ b/packages/rsx-hotreload/src/extensions.rs @@ -0,0 +1,111 @@ +use dioxus_core::TemplateNode; +use dioxus_core_types::HotReloadingContext; +use dioxus_rsx::*; +use internment::Intern; +use std::hash::Hash; + +// interns a object into a static object, reusing the value if it already exists +pub(crate) fn intern( + s: impl Into>, +) -> &'static T { + s.into().as_ref() +} + +pub(crate) fn html_tag_and_namespace( + attr: &Attribute, +) -> (&'static str, Option<&'static str>) { + let attribute_name_rust = attr.name.to_string(); + let element_name = attr.el_name.as_ref().unwrap(); + let rust_name = match element_name { + ElementName::Ident(i) => i.to_string(), + // If this is a web component, just use the name of the elements instead of mapping the attribute + // through the hot reloading context + ElementName::Custom(_) => return (intern(attribute_name_rust.as_str()), None), + }; + + Ctx::map_attribute(&rust_name, &attribute_name_rust) + .unwrap_or((intern(attribute_name_rust.as_str()), None)) +} + +pub fn to_template_attribute( + attr: &Attribute, +) -> dioxus_core::TemplateAttribute { + use dioxus_core::TemplateAttribute; + + // If it's a dynamic node, just return it + // For dynamic attributes, we need to check the mapping to see if that mapping exists + // todo: one day we could generate new dynamic attributes on the fly if they're a literal, + // or something sufficiently serializable + // (ie `checked`` being a bool and bools being interpretable) + // + // For now, just give up if that attribute doesn't exist in the mapping + if !attr.is_static_str_literal() { + let id = attr.dyn_idx.get(); + return TemplateAttribute::Dynamic { id }; + } + + // Otherwise it's a static node and we can build it + let (_, value) = attr.as_static_str_literal().unwrap(); + let (name, namespace) = html_tag_and_namespace::(attr); + + TemplateAttribute::Static { + name, + namespace, + value: intern(value.to_static().unwrap().as_str()), + } +} + +/// Convert this BodyNode into a TemplateNode. +/// +/// dioxus-core uses this to understand templates at compiletime +pub fn to_template_node(node: &BodyNode) -> dioxus_core::TemplateNode { + use dioxus_core::TemplateNode; + match node { + BodyNode::Element(el) => { + let rust_name = el.name.to_string(); + + let (tag, namespace) = + Ctx::map_element(&rust_name).unwrap_or((intern(rust_name.as_str()), None)); + + TemplateNode::Element { + tag, + namespace, + children: intern( + el.children + .iter() + .map(|c| to_template_node::(c)) + .collect::>(), + ), + attrs: intern( + el.merged_attributes + .iter() + .map(|attr| to_template_attribute::(attr)) + .collect::>(), + ), + } + } + BodyNode::Text(text) => text_to_template_node(text), + BodyNode::RawExpr(exp) => TemplateNode::Dynamic { + id: exp.dyn_idx.get(), + }, + BodyNode::Component(comp) => TemplateNode::Dynamic { + id: comp.dyn_idx.get(), + }, + BodyNode::ForLoop(floop) => TemplateNode::Dynamic { + id: floop.dyn_idx.get(), + }, + BodyNode::IfChain(chain) => TemplateNode::Dynamic { + id: chain.dyn_idx.get(), + }, + } +} +pub fn text_to_template_node(node: &TextNode) -> TemplateNode { + match node.input.to_static() { + Some(text) => TemplateNode::Text { + text: intern(text.as_str()), + }, + None => TemplateNode::Dynamic { + id: node.dyn_idx.get(), + }, + } +} diff --git a/packages/rsx/src/hot_reload/last_build_state.rs b/packages/rsx-hotreload/src/last_build_state.rs similarity index 99% rename from packages/rsx/src/hot_reload/last_build_state.rs rename to packages/rsx-hotreload/src/last_build_state.rs index a8d8a5e0d7..49c2e46695 100644 --- a/packages/rsx/src/hot_reload/last_build_state.rs +++ b/packages/rsx-hotreload/src/last_build_state.rs @@ -1,5 +1,5 @@ -use crate::innerlude::*; use dioxus_core::internal::{FmtSegment, FmtedSegments, HotReloadLiteral}; +use dioxus_rsx::*; use std::cell::Cell; /// A pool of items we can grab from during hot reloading. diff --git a/packages/rsx-hotreload/src/lib.rs b/packages/rsx-hotreload/src/lib.rs new file mode 100644 index 0000000000..080c6b0275 --- /dev/null +++ b/packages/rsx-hotreload/src/lib.rs @@ -0,0 +1,9 @@ +mod collect; +pub use collect::*; + +mod diff; +pub use diff::*; + +mod last_build_state; + +mod extensions; diff --git a/packages/rsx/Cargo.toml b/packages/rsx/Cargo.toml index f87ad8d60b..857ce27ac7 100644 --- a/packages/rsx/Cargo.toml +++ b/packages/rsx/Cargo.toml @@ -13,19 +13,14 @@ keywords = ["dom", "ui", "gui", "react"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -internment = { workspace = true, optional = true } proc-macro2 = { workspace = true, features = ["span-locations"] } proc-macro2-diagnostics = { workspace = true } quote = { workspace = true } syn = { workspace = true, features = ["full", "extra-traits", "visit", "visit-mut"] } tracing = { workspace = true } -dioxus-core = { workspace = true, optional = true } # todo: this is being compiled in a proc macro -dioxus-core-types = { workspace = true, optional = true } [features] default = [] -hot_reload_traits = [] -hot_reload = ["dep:internment", "dep:dioxus-core", "dep:dioxus-core-types", "hot_reload_traits"] [dev-dependencies] prettyplease = { workspace = true } diff --git a/packages/rsx/src/attribute.rs b/packages/rsx/src/attribute.rs index 882eeb07c6..95a2825875 100644 --- a/packages/rsx/src/attribute.rs +++ b/packages/rsx/src/attribute.rs @@ -166,52 +166,6 @@ impl Attribute { self.as_static_str_literal().is_some() } - #[cfg(feature = "hot_reload")] - pub(crate) fn html_tag_and_namespace( - &self, - ) -> (&'static str, Option<&'static str>) { - let attribute_name_rust = self.name.to_string(); - let element_name = self.el_name.as_ref().unwrap(); - let rust_name = match element_name { - ElementName::Ident(i) => i.to_string(), - // If this is a web component, just use the name of the elements instead of mapping the attribute - // through the hot reloading context - ElementName::Custom(_) => return (intern(attribute_name_rust.as_str()), None), - }; - - Ctx::map_attribute(&rust_name, &attribute_name_rust) - .unwrap_or((intern(attribute_name_rust.as_str()), None)) - } - - #[cfg(feature = "hot_reload")] - pub fn to_template_attribute( - &self, - ) -> dioxus_core::TemplateAttribute { - use dioxus_core::TemplateAttribute; - - // If it's a dynamic node, just return it - // For dynamic attributes, we need to check the mapping to see if that mapping exists - // todo: one day we could generate new dynamic attributes on the fly if they're a literal, - // or something sufficiently serializable - // (ie `checked`` being a bool and bools being interpretable) - // - // For now, just give up if that attribute doesn't exist in the mapping - if !self.is_static_str_literal() { - let id = self.dyn_idx.get(); - return TemplateAttribute::Dynamic { id }; - } - - // Otherwise it's a static node and we can build it - let (_, value) = self.as_static_str_literal().unwrap(); - let (name, namespace) = self.html_tag_and_namespace::(); - - TemplateAttribute::Static { - name, - namespace, - value: intern(value.to_static().unwrap().as_str()), - } - } - pub fn rendered_as_dynamic_attr(&self) -> TokenStream2 { // Shortcut out with spreads if let AttributeName::Spread(_) = self.name { diff --git a/packages/rsx/src/hot_reload/mod.rs b/packages/rsx/src/hot_reload/mod.rs deleted file mode 100644 index e07a8a204d..0000000000 --- a/packages/rsx/src/hot_reload/mod.rs +++ /dev/null @@ -1,12 +0,0 @@ -#[cfg(feature = "hot_reload")] -mod collect; -#[cfg(feature = "hot_reload")] -pub use collect::*; - -#[cfg(feature = "hot_reload")] -mod diff; -#[cfg(feature = "hot_reload")] -pub use diff::*; - -#[cfg(feature = "hot_reload")] -mod last_build_state; diff --git a/packages/rsx/src/lib.rs b/packages/rsx/src/lib.rs index 11cc0dc2b3..fa77661867 100644 --- a/packages/rsx/src/lib.rs +++ b/packages/rsx/src/lib.rs @@ -78,8 +78,6 @@ pub use partial_closure::PartialClosure; pub use rsx_call::*; pub use template_body::TemplateBody; -pub mod hot_reload; - use quote::{quote, ToTokens, TokenStreamExt}; use syn::{ parse::{Parse, ParseStream}, diff --git a/packages/rsx/src/node.rs b/packages/rsx/src/node.rs index dd68a7c8d1..ad5e67d7b2 100644 --- a/packages/rsx/src/node.rs +++ b/packages/rsx/src/node.rs @@ -126,54 +126,6 @@ impl ToTokens for BodyNode { } impl BodyNode { - /// Convert this BodyNode into a TemplateNode. - /// - /// dioxus-core uses this to understand templates at compiletime - #[cfg(feature = "hot_reload")] - pub fn to_template_node( - &self, - ) -> dioxus_core::TemplateNode { - use dioxus_core::TemplateNode; - match self { - BodyNode::Element(el) => { - let rust_name = el.name.to_string(); - - let (tag, namespace) = - Ctx::map_element(&rust_name).unwrap_or((intern(rust_name.as_str()), None)); - - TemplateNode::Element { - tag, - namespace, - children: intern( - el.children - .iter() - .map(|c| c.to_template_node::()) - .collect::>(), - ), - attrs: intern( - el.merged_attributes - .iter() - .map(|attr| attr.to_template_attribute::()) - .collect::>(), - ), - } - } - BodyNode::Text(text) => text.to_template_node(), - BodyNode::RawExpr(exp) => TemplateNode::Dynamic { - id: exp.dyn_idx.get(), - }, - BodyNode::Component(comp) => TemplateNode::Dynamic { - id: comp.dyn_idx.get(), - }, - BodyNode::ForLoop(floop) => TemplateNode::Dynamic { - id: floop.dyn_idx.get(), - }, - BodyNode::IfChain(chain) => TemplateNode::Dynamic { - id: chain.dyn_idx.get(), - }, - } - } - pub fn get_dyn_idx(&self) -> usize { match self { BodyNode::Text(text) => text.dyn_idx.get(), diff --git a/packages/rsx/src/template_body.rs b/packages/rsx/src/template_body.rs index baa72b3d15..5b9c057773 100644 --- a/packages/rsx/src/template_body.rs +++ b/packages/rsx/src/template_body.rs @@ -238,7 +238,7 @@ impl TemplateBody { self.roots.is_empty() } - pub(crate) fn implicit_key(&self) -> Option<&AttributeValue> { + pub fn implicit_key(&self) -> Option<&AttributeValue> { match self.roots.first() { Some(BodyNode::Element(el)) => el.key(), Some(BodyNode::Component(comp)) => comp.get_key(), @@ -310,7 +310,7 @@ impl TemplateBody { } /// Iterate through the literal component properties of this rsx call in depth-first order - pub(crate) fn literal_component_properties(&self) -> impl Iterator + '_ { + pub fn literal_component_properties(&self) -> impl Iterator + '_ { self.dynamic_nodes() .filter_map(|node| { if let BodyNode::Component(component) = node { diff --git a/packages/rsx/src/text_node.rs b/packages/rsx/src/text_node.rs index de82f3394c..d5714ee31f 100644 --- a/packages/rsx/src/text_node.rs +++ b/packages/rsx/src/text_node.rs @@ -62,19 +62,6 @@ impl TextNode { pub fn is_static(&self) -> bool { self.input.is_static() } - - #[cfg(feature = "hot_reload")] - pub fn to_template_node(&self) -> TemplateNode { - use crate::intern; - match self.input.to_static() { - Some(text) => TemplateNode::Text { - text: intern(text.as_str()), - }, - None => TemplateNode::Dynamic { - id: self.dyn_idx.get(), - }, - } - } } #[cfg(test)] diff --git a/packages/rsx/src/util.rs b/packages/rsx/src/util.rs index 828c376c3d..273962414e 100644 --- a/packages/rsx/src/util.rs +++ b/packages/rsx/src/util.rs @@ -1,8 +1,5 @@ #![allow(unused)] -#[cfg(feature = "hot_reload")] -use internment::Intern; - use proc_macro2::TokenStream as TokenStream2; use std::{fmt::Debug, hash::Hash}; use syn::{ @@ -11,14 +8,6 @@ use syn::{ Ident, }; -/// interns a object into a static object, reusing the value if it already exists -#[cfg(feature = "hot_reload")] -pub(crate) fn intern( - s: impl Into>, -) -> &'static T { - s.into().as_ref() -} - /// Parse a raw ident and return a new ident with the r# prefix added pub fn parse_raw_ident(parse_buffer: &ParseBuffer) -> syn::Result { // First try to parse as a normal ident From fdad091f10de977e03c65ca8d520c17200cb2431 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 6 Sep 2024 13:40:14 -0700 Subject: [PATCH 094/139] kill out commented code --- packages/router/src/router_cfg.rs | 45 ------------------------------- packages/rsx/src/text_node.rs | 3 --- 2 files changed, 48 deletions(-) diff --git a/packages/router/src/router_cfg.rs b/packages/router/src/router_cfg.rs index 3647db7a50..b8bc3e1dc6 100644 --- a/packages/router/src/router_cfg.rs +++ b/packages/router/src/router_cfg.rs @@ -118,48 +118,3 @@ where } } } - -// /// Get the default history provider for the current platform. -// #[allow(unreachable_code, unused)] -// fn default_history(initial_route: R) -> Box -// where -// ::Err: std::fmt::Display, -// { -// todo!() -// // If we're on the web and have wasm, use the web history provider - -// #[cfg(all(target_arch = "wasm32", feature = "web"))] -// return Box::new(AnyHistoryProviderImplWrapper::new( -// WebHistory::::default(), -// )); - -// // If we're using fullstack and server side rendering, use the memory history provider -// #[cfg(all(feature = "fullstack", feature = "ssr"))] -// return Box::new(AnyHistoryProviderImplWrapper::new( -// MemoryHistory::::with_initial_path( -// dioxus_fullstack::prelude::server_context() -// .request_parts() -// .uri -// .to_string() -// .parse() -// .unwrap_or_else(|err| { -// tracing::error!("Failed to parse uri: {}", err); -// "/".parse().unwrap_or_else(|err| { -// panic!("Failed to parse uri: {}", err); -// }) -// }), -// ), -// )); - -// // If liveview is enabled, use the liveview history provider -// #[cfg(feature = "liveview")] -// return Box::new(AnyHistoryProviderImplWrapper::new( -// LiveviewHistory::new_with_initial_path(initial_route), -// )); - -// // If none of the above, use the memory history provider, which is a decent enough fallback -// // Eventually we want to integrate with the mobile history provider, and other platform providers -// Box::new(AnyHistoryProviderImplWrapper::new( -// MemoryHistory::with_initial_path(initial_route), -// )) -// } diff --git a/packages/rsx/src/text_node.rs b/packages/rsx/src/text_node.rs index d5714ee31f..190a7b4e68 100644 --- a/packages/rsx/src/text_node.rs +++ b/packages/rsx/src/text_node.rs @@ -1,6 +1,3 @@ -#[cfg(feature = "hot_reload")] -use dioxus_core::TemplateNode; - use crate::{literal::HotLiteral, location::DynIdx, HotReloadFormattedSegment, IfmtInput}; use proc_macro2::{Span, TokenStream as TokenStream2}; use quote::ToTokens; From e2342be3d6e8b9a184a152075b8d40a904f57f43 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 6 Sep 2024 13:57:48 -0700 Subject: [PATCH 095/139] rename rsx, cut out hotreload tests --- Cargo.lock | 34 +++++----- Cargo.toml | 5 +- example-projects/router/src/main.rs | 11 +-- packages/cli/Cargo.toml | 2 +- packages/cli/src/cli/translate.rs | 4 +- packages/core-macro/Cargo.toml | 7 +- packages/core/Cargo.toml | 35 ++++------ packages/desktop/src/document.rs | 5 +- packages/extension/Cargo.toml | 4 +- packages/extension/src/lib.rs | 2 +- packages/generational-box/Cargo.toml | 2 +- packages/html-internal-macro/Cargo.toml | 6 +- .../tests/hotreload_pattern.rs | 6 +- .../tests/hotreloads.rs | 2 +- .../rsx-hotreload/tests/valid/combo.new.rsx | 68 +++++++++++++++++++ .../rsx-hotreload/tests/valid/combo.old.rsx | 57 ++++++++++++++++ .../rsx-hotreload/tests/valid/expr.new.rsx | 17 +++++ .../rsx-hotreload/tests/valid/expr.old.rsx | 17 +++++ .../rsx-hotreload/tests/valid/for_.new.rsx | 9 +++ .../rsx-hotreload/tests/valid/for_.old.rsx | 10 +++ .../rsx-hotreload/tests/valid/if_.new.rsx | 10 +++ .../rsx-hotreload/tests/valid/if_.old.rsx | 9 +++ .../rsx-hotreload/tests/valid/let_.new.rsx | 12 ++++ .../rsx-hotreload/tests/valid/let_.old.rsx | 12 ++++ .../rsx-hotreload/tests/valid/nested.new.rsx | 10 +++ .../rsx-hotreload/tests/valid/nested.old.rsx | 9 +++ packages/rsx-rosetta/Cargo.toml | 2 +- packages/rsx-rosetta/examples/html.rs | 2 +- packages/rsx-rosetta/tests/h-tags.rs | 2 +- packages/rsx-rosetta/tests/raw.rs | 2 +- packages/rsx-rosetta/tests/simple.rs | 4 +- packages/rsx-rosetta/tests/web-component.rs | 2 +- packages/signals/Cargo.toml | 2 +- 33 files changed, 301 insertions(+), 80 deletions(-) rename packages/{rsx => rsx-hotreload}/tests/hotreload_pattern.rs (99%) rename packages/{rsx => rsx-hotreload}/tests/hotreloads.rs (95%) create mode 100644 packages/rsx-hotreload/tests/valid/combo.new.rsx create mode 100644 packages/rsx-hotreload/tests/valid/combo.old.rsx create mode 100644 packages/rsx-hotreload/tests/valid/expr.new.rsx create mode 100644 packages/rsx-hotreload/tests/valid/expr.old.rsx create mode 100644 packages/rsx-hotreload/tests/valid/for_.new.rsx create mode 100644 packages/rsx-hotreload/tests/valid/for_.old.rsx create mode 100644 packages/rsx-hotreload/tests/valid/if_.new.rsx create mode 100644 packages/rsx-hotreload/tests/valid/if_.old.rsx create mode 100644 packages/rsx-hotreload/tests/valid/let_.new.rsx create mode 100644 packages/rsx-hotreload/tests/valid/let_.old.rsx create mode 100644 packages/rsx-hotreload/tests/valid/nested.new.rsx create mode 100644 packages/rsx-hotreload/tests/valid/nested.old.rsx diff --git a/Cargo.lock b/Cargo.lock index 461eaf3562..de8403b3e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2413,6 +2413,7 @@ dependencies = [ "dioxus-html", "dioxus-rsx", "dioxus-rsx-hotreload", + "dioxus-rsx-rosetta", "dioxus-runtime-config", "dirs", "env_logger", @@ -2437,7 +2438,6 @@ dependencies = [ "ratatui", "rayon", "reqwest", - "rsx-rosetta", "rustls 0.23.12", "serde", "serde_json", @@ -2629,8 +2629,8 @@ name = "dioxus-ext" version = "0.6.0-alpha.2" dependencies = [ "dioxus-autofmt", + "dioxus-rsx-rosetta", "html_parser", - "rsx-rosetta", "syn 2.0.75", "wasm-bindgen", ] @@ -2937,6 +2937,21 @@ dependencies = [ "tracing", ] +[[package]] +name = "dioxus-rsx-rosetta" +version = "0.6.0-alpha.2" +dependencies = [ + "convert_case 0.6.0", + "dioxus-autofmt", + "dioxus-html", + "dioxus-rsx", + "html_parser", + "pretty_assertions", + "proc-macro2", + "quote", + "syn 2.0.75", +] + [[package]] name = "dioxus-runtime-config" version = "0.6.0-alpha.2" @@ -8048,21 +8063,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "rsx-rosetta" -version = "0.6.0-alpha.2" -dependencies = [ - "convert_case 0.6.0", - "dioxus-autofmt", - "dioxus-html", - "dioxus-rsx", - "html_parser", - "pretty_assertions", - "proc-macro2", - "quote", - "syn 2.0.75", -] - [[package]] name = "rust_decimal" version = "1.36.0" diff --git a/Cargo.toml b/Cargo.toml index 5fac4971df..349ab96c92 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -102,7 +102,7 @@ dioxus-autofmt = { path = "packages/autofmt", version = "0.6.0-alpha.2" } dioxus-check = { path = "packages/check", version = "0.6.0-alpha.2" } dioxus-rsx = { path = "packages/rsx", version = "0.6.0-alpha.2" } dioxus-rsx-hotreload = { path = "packages/rsx-hotreload", version = "0.6.0-alpha.2" } -rsx-rosetta = { path = "packages/rsx-rosetta", version = "0.6.0-alpha.2" } +dioxus-rsx-rosetta = { path = "packages/rsx-rosetta", version = "0.6.0-alpha.2" } dioxus-signals = { path = "packages/signals", version = "0.6.0-alpha.2" } dioxus-devtools = { path = "packages/devtools", version = "0.6.0-alpha.2" } dioxus-devtools-types = { path = "packages/devtools-types", version = "0.6.0-alpha.2" } @@ -182,6 +182,9 @@ chrono = { version = "0.4.34" } gloo = { version = "0.8.0" } gloo-utils = { version = "0.1.6" } rustversion = "1.0.17" +rand = "0.8.5" +longest-increasing-subsequence = "0.1.0" +trybuild = "1.0" # desktop wry = { version = "0.42.0", default-features = false } diff --git a/example-projects/router/src/main.rs b/example-projects/router/src/main.rs index 77933a70cb..e2803a469b 100644 --- a/example-projects/router/src/main.rs +++ b/example-projects/router/src/main.rs @@ -7,16 +7,7 @@ use dioxus::prelude::*; fn main() { - todo!("isrg"); - dioxus::builder() - // .with_cfg(server_only!( - // // - // ServeConfig::builder().incremental( - // IncrementalRendererConfig::default() - // .invalidate_after(std::time::Duration::from_secs(120)), - // ) - // )) - .launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/packages/cli/Cargo.toml b/packages/cli/Cargo.toml index 15654549d6..759e4a758e 100644 --- a/packages/cli/Cargo.toml +++ b/packages/cli/Cargo.toml @@ -85,7 +85,7 @@ brotli = "6.0.0" dioxus-autofmt = { workspace = true } dioxus-check = { workspace = true } -rsx-rosetta = { workspace = true } +dioxus-rsx-rosetta = { workspace = true } dioxus-rsx = { workspace = true } dioxus-rsx-hotreload = { workspace = true } dioxus-html = { workspace = true, features = ["hot-reload-context"] } diff --git a/packages/cli/src/cli/translate.rs b/packages/cli/src/cli/translate.rs index e822cd5eb0..5759ddc505 100644 --- a/packages/cli/src/cli/translate.rs +++ b/packages/cli/src/cli/translate.rs @@ -48,7 +48,7 @@ impl Translate { } pub(crate) fn convert_html_to_formatted_rsx(dom: &Dom, component: bool) -> String { - let callbody = rsx_rosetta::rsx_from_html(dom); + let callbody = dioxus_rsx_rosetta::rsx_from_html(dom); match component { true => write_callbody_with_icon_section(callbody), @@ -59,7 +59,7 @@ pub(crate) fn convert_html_to_formatted_rsx(dom: &Dom, component: bool) -> Strin fn write_callbody_with_icon_section(mut callbody: CallBody) -> String { let mut svgs = vec![]; - rsx_rosetta::collect_svgs(&mut callbody.body.roots, &mut svgs); + dioxus_rsx_rosetta::collect_svgs(&mut callbody.body.roots, &mut svgs); let mut out = write_component_body(dioxus_autofmt::write_block_out(&callbody).unwrap()); diff --git a/packages/core-macro/Cargo.toml b/packages/core-macro/Cargo.toml index 9d35ac3ab4..21cee47d3d 100644 --- a/packages/core-macro/Cargo.toml +++ b/packages/core-macro/Cargo.toml @@ -13,19 +13,18 @@ keywords = ["dom", "ui", "gui", "react"] proc-macro = true [dependencies] -proc-macro2 = { version = "1.0" } +proc-macro2 = { workspace = true } quote = { workspace = true } syn = { workspace = true, features = ["full", "extra-traits", "visit"] } dioxus-rsx = { workspace = true } convert_case = { workspace = true } -# testing [dev-dependencies] dioxus = { workspace = true } dioxus-html = { workspace = true, features = ["serialize"]} -rustversion = "1.0" +rustversion = { workspace = true } tokio = { workspace = true, features = ["full", "time"] } -trybuild = "1.0" +trybuild = { workspace = true } [features] default = [] diff --git a/packages/core/Cargo.toml b/packages/core/Cargo.toml index 04d1648e41..9ba11ad466 100644 --- a/packages/core/Cargo.toml +++ b/packages/core/Cargo.toml @@ -11,40 +11,33 @@ keywords = ["dom", "ui", "gui", "react"] [dependencies] dioxus-core-types = { workspace = true } +const_format = { workspace = true } +futures-channel = { workspace = true } +generational-box = { workspace = true } +longest-increasing-subsequence = { workspace = true } rustc-hash = { workspace = true } -longest-increasing-subsequence = "0.1.0" -futures-util = { workspace = true, default-features = false, features = [ - "alloc", - "std", -] } +rustversion = { workspace = true } slab = { workspace = true } slotmap = { workspace = true } -futures-channel = { workspace = true } tracing = { workspace = true } -serde = { version = "1", features = ["derive"], optional = true } -generational-box = { workspace = true } -rustversion = "1.0.17" -const_format = { workspace = true } warnings = { workspace = true } +futures-util = { workspace = true, default-features = false, features = ["alloc", "std"] } +serde = { workspace = true, optional = true, features = ["derive"] } [dev-dependencies] -tokio = { workspace = true, features = ["full"] } -tracing-fluent-assertions = "0.3.0" dioxus = { workspace = true } +dioxus-ssr = { workspace = true } dioxus-html = { workspace = true, features = ["serialize"] } +tokio = { workspace = true, features = ["full"] } +rand = { workspace = true } +reqwest = { workspace = true } +tracing-subscriber = { workspace = true } +tracing-fluent-assertions = "0.3.0" pretty_assertions = "1.3.0" -rand = "0.8.5" -dioxus-ssr = { workspace = true } -reqwest = { workspace = true} -tracing-subscriber = "0.3.18" [dev-dependencies.web-sys] version = "0.3.56" -features = [ - "Document", - "HtmlElement", - "Window" -] +features = ["Document", "HtmlElement", "Window"] [features] serialize = ["dep:serde"] diff --git a/packages/desktop/src/document.rs b/packages/desktop/src/document.rs index 6d41c17fe9..f4862f2625 100644 --- a/packages/desktop/src/document.rs +++ b/packages/desktop/src/document.rs @@ -1,9 +1,6 @@ use crate::DesktopService; use dioxus_document::{Document, Eval, EvalError}; -use std::{ - str::FromStr, - sync::{Arc, Mutex}, -}; +use std::sync::{Arc, Mutex}; impl Document for DesktopService { fn set_title(&self, title: String) { diff --git a/packages/extension/Cargo.toml b/packages/extension/Cargo.toml index 081f1405df..2ab7b947f4 100644 --- a/packages/extension/Cargo.toml +++ b/packages/extension/Cargo.toml @@ -9,9 +9,9 @@ publish = false [dependencies] wasm-bindgen = { workspace = true } dioxus-autofmt = { workspace = true } -rsx-rosetta = { workspace = true } +dioxus-rsx-rosetta = { workspace = true } html_parser = { workspace = true } -syn ={ workspace = true } +syn = { workspace = true } [lib] crate-type = ["cdylib", "rlib"] diff --git a/packages/extension/src/lib.rs b/packages/extension/src/lib.rs index 616bded736..1084472ede 100644 --- a/packages/extension/src/lib.rs +++ b/packages/extension/src/lib.rs @@ -94,7 +94,7 @@ pub fn translate_rsx(contents: String, _component: bool) -> String { // Ensure we're loading valid HTML let dom = html_parser::Dom::parse(&contents).unwrap(); - let callbody = rsx_rosetta::rsx_from_html(&dom); + let callbody = dioxus_rsx_rosetta::rsx_from_html(&dom); // Convert the HTML to RSX dioxus_autofmt::write_block_out(&callbody).unwrap() diff --git a/packages/generational-box/Cargo.toml b/packages/generational-box/Cargo.toml index 2824e3eb3a..419eb17047 100644 --- a/packages/generational-box/Cargo.toml +++ b/packages/generational-box/Cargo.toml @@ -13,7 +13,7 @@ keywords = ["generational", "box", "memory", "allocator"] parking_lot = "0.12.1" [dev-dependencies] -rand = "0.8.5" +rand = { workspace = true } criterion = { workspace = true } [features] diff --git a/packages/html-internal-macro/Cargo.toml b/packages/html-internal-macro/Cargo.toml index 9e4b39d896..2ca84157fb 100644 --- a/packages/html-internal-macro/Cargo.toml +++ b/packages/html-internal-macro/Cargo.toml @@ -12,9 +12,9 @@ description = "HTML function macros for Dioxus" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -proc-macro2 = "1.0.66" +proc-macro2 = { workspace = true } syn = { workspace = true, features = ["full"] } -quote = "^1.0.26" +quote = { workspace = true } convert_case = { workspace = true } [lib] @@ -25,4 +25,4 @@ name = "tests" path = "tests/progress.rs" [dev-dependencies] -trybuild = { version = "1.0.82", features = ["diff"] } +trybuild = { workspace = true, features = ["diff"] } diff --git a/packages/rsx/tests/hotreload_pattern.rs b/packages/rsx-hotreload/tests/hotreload_pattern.rs similarity index 99% rename from packages/rsx/tests/hotreload_pattern.rs rename to packages/rsx-hotreload/tests/hotreload_pattern.rs index 61cddc81fb..79d888cccc 100644 --- a/packages/rsx/tests/hotreload_pattern.rs +++ b/packages/rsx-hotreload/tests/hotreload_pattern.rs @@ -11,10 +11,8 @@ use dioxus_core::{ TemplateAttribute, VNode, }; use dioxus_core_types::HotReloadingContext; -use dioxus_rsx::{ - hot_reload::{self, diff_rsx, ChangedRsx, HotReloadResult}, - CallBody, -}; +use dioxus_rsx::CallBody; +use dioxus_rsx_hotreload::{self, diff_rsx, ChangedRsx, HotReloadResult}; use proc_macro2::TokenStream; use quote::{quote, ToTokens}; use syn::{parse::Parse, spanned::Spanned, token::Token, File}; diff --git a/packages/rsx/tests/hotreloads.rs b/packages/rsx-hotreload/tests/hotreloads.rs similarity index 95% rename from packages/rsx/tests/hotreloads.rs rename to packages/rsx-hotreload/tests/hotreloads.rs index c094414125..f7312b831a 100644 --- a/packages/rsx/tests/hotreloads.rs +++ b/packages/rsx-hotreload/tests/hotreloads.rs @@ -1,4 +1,4 @@ -use dioxus_rsx::hot_reload::diff_rsx; +use dioxus_rsx_hotreload::diff_rsx; use syn::File; macro_rules! assert_rsx_changed { diff --git a/packages/rsx-hotreload/tests/valid/combo.new.rsx b/packages/rsx-hotreload/tests/valid/combo.new.rsx new file mode 100644 index 0000000000..08a0c49b15 --- /dev/null +++ b/packages/rsx-hotreload/tests/valid/combo.new.rsx @@ -0,0 +1,68 @@ +// This test is used by playwright configured in the root of the repo + +use dioxus::prelude::*; + +fn app() -> Element { + let mut num = use_signal(|| 0); + let mut eval_result = use_signal(String::new); + let a = 123; + + rsx! { + div { + "hello axum! {num}" + button { class: "increment-button", onclick: move |_| num += 1, "Increment" } + } + svg { + circle { + cx: 50, + cy: 50, + r: 40, + stroke: "green", + fill: "yellow" + } + } + div { + class: "raw-attribute-div", + "raw-attribute": "raw-attribute-value" + } + div { class: "hidden-attribute-div", hidden: true } + div { + class: "dangerous-inner-html-div", + dangerous_inner_html: "

    hello dangerous inner html

    " + } + input { value: "hello input" } + div { class: "style-div", color: "red", "colored text" } + div { class: "style-div", color: "red", "colored text" } + button { + class: "eval-button", + onclick: move |_| async move { + let mut eval = eval( + r#" + window.document.title = 'Hello from Dioxus Eval!'; + dioxus.send("returned eval value"); + "#, + ); + let result = eval.recv().await; + if let Ok(serde_json::Value::String(string)) = result { + eval_result.set(string); + } + }, + "Eval!!!!" + "Eval!!!!" + "Eval!!!!!" + "Eval!!!!" + "Eval!!!!" + "Eval!!!!" + } + div { class: "eval-result", "{eval_result}" } + } +} + +fn main() { + // tracing_wasm::set_as_global_default_with_config( + // tracing_wasm::WASMLayerConfigBuilder::default() + // .set_max_level(tracing::Level::TRACE) + // .build(), + // ); + launch(app); +} diff --git a/packages/rsx-hotreload/tests/valid/combo.old.rsx b/packages/rsx-hotreload/tests/valid/combo.old.rsx new file mode 100644 index 0000000000..d08ca4e87b --- /dev/null +++ b/packages/rsx-hotreload/tests/valid/combo.old.rsx @@ -0,0 +1,57 @@ +// This test is used by playwright configured in the root of the repo + +use dioxus::prelude::*; + +fn app() -> Element { + let mut num = use_signal(|| 0); + let mut eval_result = use_signal(String::new); + let a = 123; + + rsx! { + div { + "hello axum! {num}" + button { class: "increment-button", onclick: move |_| num += 1, "Increment" } + } + svg { circle { cx: 50, cy: 50, r: 40, stroke: "green", fill: "yellow" } } + div { class: "raw-attribute-div", "raw-attribute": "raw-attribute-value" } + div { class: "hidden-attribute-div", hidden: true } + div { + class: "dangerous-inner-html-div", + dangerous_inner_html: "

    hello dangerous inner html

    " + } + input { value: "hello input" } + div { class: "style-div", color: "red", "colored text" } + div { class: "style-div", color: "red", "colored text" } + button { + class: "eval-button", + onclick: move |_| async move { + let mut eval = eval( + r#" + window.document.title = 'Hello from Dioxus Eval!'; + dioxus.send("returned eval value"); + "#, + ); + + let result = eval.recv().await; + if let Ok(serde_json::Value::String(string)) = result { + eval_result.set(string); + } + }, + "Eval!!!!" + "Eval!!!!" + "Eval!!!!!" + "Eval!!!!" + "Eval!!!!" + } + div { class: "eval-result", "{eval_result}" } + } +} + +fn main() { + // tracing_wasm::set_as_global_default_with_config( + // tracing_wasm::WASMLayerConfigBuilder::default() + // .set_max_level(tracing::Level::TRACE) + // .build(), + // ); + launch(app); +} diff --git a/packages/rsx-hotreload/tests/valid/expr.new.rsx b/packages/rsx-hotreload/tests/valid/expr.new.rsx new file mode 100644 index 0000000000..eb0d04b59c --- /dev/null +++ b/packages/rsx-hotreload/tests/valid/expr.new.rsx @@ -0,0 +1,17 @@ +use dioxus::prelude::*; + +pub fn CoolChild() -> Element { + let head_ = rsx! { + div { + div { "asasddasdasd" } + div { "asasdd1asaassdd23asasddasd" } + div { "aasdsdsaasdsddasd" } + } + }; + + rsx! { + div { + {head_} + } + } +} diff --git a/packages/rsx-hotreload/tests/valid/expr.old.rsx b/packages/rsx-hotreload/tests/valid/expr.old.rsx new file mode 100644 index 0000000000..c356ac6383 --- /dev/null +++ b/packages/rsx-hotreload/tests/valid/expr.old.rsx @@ -0,0 +1,17 @@ +use dioxus::prelude::*; + +pub fn CoolChild() -> Element { + let head_ = rsx! { + div { + div { "asasddasdasd" } + div { "asasdd1asaassdd23asasddasd" } + // div { "aasdsdsaasdsddasd" } + } + }; + + rsx! { + div { + {head_} + } + } +} diff --git a/packages/rsx-hotreload/tests/valid/for_.new.rsx b/packages/rsx-hotreload/tests/valid/for_.new.rsx new file mode 100644 index 0000000000..5a1e0cd6d8 --- /dev/null +++ b/packages/rsx-hotreload/tests/valid/for_.new.rsx @@ -0,0 +1,9 @@ +use dioxus::prelude::*; + +pub fn CoolChild() -> Element { + rsx! { + for items in vec![1, 2, 3] { + div { "asasddasdasd" } + } + } +} diff --git a/packages/rsx-hotreload/tests/valid/for_.old.rsx b/packages/rsx-hotreload/tests/valid/for_.old.rsx new file mode 100644 index 0000000000..f02504618b --- /dev/null +++ b/packages/rsx-hotreload/tests/valid/for_.old.rsx @@ -0,0 +1,10 @@ +use dioxus::prelude::*; + +pub fn CoolChild() -> Element { + rsx! { + for items in vec![1, 2, 3] { + div { "123" } + div { "asasddasdasd" } + } + } +} diff --git a/packages/rsx-hotreload/tests/valid/if_.new.rsx b/packages/rsx-hotreload/tests/valid/if_.new.rsx new file mode 100644 index 0000000000..9de9c9688b --- /dev/null +++ b/packages/rsx-hotreload/tests/valid/if_.new.rsx @@ -0,0 +1,10 @@ +use dioxus::prelude::*; + +pub fn CoolChild() -> Element { + rsx! { + if cond() { + div { "123" } + div { "asasddasdasd" } + } + } +} diff --git a/packages/rsx-hotreload/tests/valid/if_.old.rsx b/packages/rsx-hotreload/tests/valid/if_.old.rsx new file mode 100644 index 0000000000..97183ef438 --- /dev/null +++ b/packages/rsx-hotreload/tests/valid/if_.old.rsx @@ -0,0 +1,9 @@ +use dioxus::prelude::*; + +pub fn CoolChild() -> Element { + rsx! { + if cond() { + div { "asasddasdasd" } + } + } +} diff --git a/packages/rsx-hotreload/tests/valid/let_.new.rsx b/packages/rsx-hotreload/tests/valid/let_.new.rsx new file mode 100644 index 0000000000..cfd1477059 --- /dev/null +++ b/packages/rsx-hotreload/tests/valid/let_.new.rsx @@ -0,0 +1,12 @@ +use dioxus::prelude::*; + +pub fn CoolChild() -> Element { + let head_ = rsx! { + div { + div { "asasddasdasd" } + div { "asasdd1asaassdd23asasddasd" } + } + }; + + head_ +} diff --git a/packages/rsx-hotreload/tests/valid/let_.old.rsx b/packages/rsx-hotreload/tests/valid/let_.old.rsx new file mode 100644 index 0000000000..5755fe1076 --- /dev/null +++ b/packages/rsx-hotreload/tests/valid/let_.old.rsx @@ -0,0 +1,12 @@ +use dioxus::prelude::*; + +pub fn CoolChild() -> Element { + let head_ = rsx! { + div { + div { "asasddasdasd" } + div { "asasdd1asaassdd23asasddasdasd" } + } + }; + + head_ +} diff --git a/packages/rsx-hotreload/tests/valid/nested.new.rsx b/packages/rsx-hotreload/tests/valid/nested.new.rsx new file mode 100644 index 0000000000..52a19a2e52 --- /dev/null +++ b/packages/rsx-hotreload/tests/valid/nested.new.rsx @@ -0,0 +1,10 @@ +use dioxus::prelude::*; + +pub fn CoolChild() -> Element { + rsx! { + ForLoop { + div { "123" } + div { "asasddasdasd" } + } + } +} diff --git a/packages/rsx-hotreload/tests/valid/nested.old.rsx b/packages/rsx-hotreload/tests/valid/nested.old.rsx new file mode 100644 index 0000000000..1f80215051 --- /dev/null +++ b/packages/rsx-hotreload/tests/valid/nested.old.rsx @@ -0,0 +1,9 @@ +use dioxus::prelude::*; + +pub fn CoolChild() -> Element { + rsx! { + ForLoop { + div { "asasddasdasd" } + } + } +} diff --git a/packages/rsx-rosetta/Cargo.toml b/packages/rsx-rosetta/Cargo.toml index db318bb0d8..8a632d3d4f 100644 --- a/packages/rsx-rosetta/Cargo.toml +++ b/packages/rsx-rosetta/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "rsx-rosetta" +name = "dioxus-rsx-rosetta" version = { workspace = true } edition = "2021" authors = ["Jonathan Kelley"] diff --git a/packages/rsx-rosetta/examples/html.rs b/packages/rsx-rosetta/examples/html.rs index 013dcd9265..3e627553a0 100644 --- a/packages/rsx-rosetta/examples/html.rs +++ b/packages/rsx-rosetta/examples/html.rs @@ -16,7 +16,7 @@ fn main() { let dom = Dom::parse(html).unwrap(); - let body = rsx_rosetta::rsx_from_html(&dom); + let body = dioxus_rsx_rosetta::rsx_from_html(&dom); let out = dioxus_autofmt::write_block_out(&body).unwrap(); diff --git a/packages/rsx-rosetta/tests/h-tags.rs b/packages/rsx-rosetta/tests/h-tags.rs index ce9db65528..dfc2bbb2bb 100644 --- a/packages/rsx-rosetta/tests/h-tags.rs +++ b/packages/rsx-rosetta/tests/h-tags.rs @@ -16,7 +16,7 @@ fn h_tags_translate() { let dom = Dom::parse(html).unwrap(); - let body = rsx_rosetta::rsx_from_html(&dom); + let body = dioxus_rsx_rosetta::rsx_from_html(&dom); let out = dioxus_autofmt::write_block_out(&body).unwrap(); diff --git a/packages/rsx-rosetta/tests/raw.rs b/packages/rsx-rosetta/tests/raw.rs index 5a5c3d1b31..42fc831e64 100644 --- a/packages/rsx-rosetta/tests/raw.rs +++ b/packages/rsx-rosetta/tests/raw.rs @@ -11,7 +11,7 @@ fn raw_attribute() { let dom = Dom::parse(html).unwrap(); - let body = rsx_rosetta::rsx_from_html(&dom); + let body = dioxus_rsx_rosetta::rsx_from_html(&dom); let out = dioxus_autofmt::write_block_out(&body).unwrap(); diff --git a/packages/rsx-rosetta/tests/simple.rs b/packages/rsx-rosetta/tests/simple.rs index 21ecfc7101..cf90f46bfe 100644 --- a/packages/rsx-rosetta/tests/simple.rs +++ b/packages/rsx-rosetta/tests/simple.rs @@ -15,7 +15,7 @@ fn simple_elements() { let dom = Dom::parse(html).unwrap(); - let body = rsx_rosetta::rsx_from_html(&dom); + let body = dioxus_rsx_rosetta::rsx_from_html(&dom); let out = dioxus_autofmt::write_block_out(&body).unwrap(); @@ -48,7 +48,7 @@ fn deeply_nested() { let dom = Dom::parse(html).unwrap(); - let body = rsx_rosetta::rsx_from_html(&dom); + let body = dioxus_rsx_rosetta::rsx_from_html(&dom); let out = dioxus_autofmt::write_block_out(&body).unwrap(); diff --git a/packages/rsx-rosetta/tests/web-component.rs b/packages/rsx-rosetta/tests/web-component.rs index 2b9e4ca10d..2d855e8eef 100644 --- a/packages/rsx-rosetta/tests/web-component.rs +++ b/packages/rsx-rosetta/tests/web-component.rs @@ -11,7 +11,7 @@ fn web_components_translate() { let dom = Dom::parse(html).unwrap(); - let body = rsx_rosetta::rsx_from_html(&dom); + let body = dioxus_rsx_rosetta::rsx_from_html(&dom); let out = dioxus_autofmt::write_block_out(&body).unwrap(); diff --git a/packages/signals/Cargo.toml b/packages/signals/Cargo.toml index c3258b2ee4..19cf70b465 100644 --- a/packages/signals/Cargo.toml +++ b/packages/signals/Cargo.toml @@ -30,7 +30,7 @@ tokio = { version = "1", features = ["full"] } tracing-subscriber = "0.3.17" simple_logger = "4.2.0" reqwest = { workspace = true } -rand = "0.8" +rand = { workspace = true } [features] default = [] From 9a356ac9ff4b48b4629b5e186a524ef796d79f99 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 12 Sep 2024 14:44:52 -0700 Subject: [PATCH 096/139] Hoist more dependencies --- Cargo.toml | 2 ++ examples/custom_html.rs | 2 +- packages/core/src/hotreload_utils.rs | 34 ++++++++++++++-------------- packages/core/src/nodes.rs | 7 ------ packages/html/Cargo.toml | 8 ------- packages/interpreter/Cargo.toml | 13 ++++------- packages/server/src/document.rs | 1 + packages/server/src/lib.rs | 15 +----------- packages/web/Cargo.toml | 10 +++----- 9 files changed, 30 insertions(+), 62 deletions(-) create mode 100644 packages/server/src/document.rs diff --git a/Cargo.toml b/Cargo.toml index 349ab96c92..b196d793b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -185,6 +185,8 @@ rustversion = "1.0.17" rand = "0.8.5" longest-increasing-subsequence = "0.1.0" trybuild = "1.0" +js-sys = "0.3.56" +web-sys = { version = "0.3.56", default-features = false } # desktop wry = { version = "0.42.0", default-features = false } diff --git a/examples/custom_html.rs b/examples/custom_html.rs index a77468039d..f1ea4b91ad 100644 --- a/examples/custom_html.rs +++ b/examples/custom_html.rs @@ -4,7 +4,7 @@ use dioxus::prelude::*; fn main() { - dioxus::builder() + dioxus::launch::builder() .with_cfg( dioxus::desktop::Config::new().with_custom_index( r#" diff --git a/packages/core/src/hotreload_utils.rs b/packages/core/src/hotreload_utils.rs index e94ce0e505..8527c170e6 100644 --- a/packages/core/src/hotreload_utils.rs +++ b/packages/core/src/hotreload_utils.rs @@ -113,23 +113,23 @@ pub enum FmtSegment { }, } -// let __pool = DynamicValuePool::new( -// vec![...], -// vec![...], -// vec![...], -// ); -// VNode::new( -// None, -// Template { -// name: "...", -// roots: &[...], -// node_paths: &[..], -// attr_paths: &[...], -// }, -// Box::new([...]), -// Box::new([...]), -// ) - +/// let __pool = DynamicValuePool::new( +/// vec![...], +/// vec![...], +/// vec![...], +/// ); +/// VNode::new( +/// None, +/// Template { +/// name: "...", +/// roots: &[...], +/// node_paths: &[..], +/// attr_paths: &[...], +/// }, +/// Box::new([...]), +/// Box::new([...]), +/// ) +/// // Open questions: // - How do we handle type coercion for different sized component property integers? // - Should non-string hot literals go through the centralized pool? diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index bfafec4773..9e7f3e7639 100644 --- a/packages/core/src/nodes.rs +++ b/packages/core/src/nodes.rs @@ -520,13 +520,6 @@ pub enum DynamicNode { Fragment(Vec), } -impl DynamicNode { - /// Convert any item that implements [`IntoDynNode`] into a [`DynamicNode`] - pub fn make_node<'c, I>(into: impl IntoDynNode + 'c) -> DynamicNode { - into.into_dyn_node() - } -} - impl Default for DynamicNode { fn default() -> Self { Self::Placeholder(Default::default()) diff --git a/packages/html/Cargo.toml b/packages/html/Cargo.toml index 2aa6204c97..457a5c720a 100644 --- a/packages/html/Cargo.toml +++ b/packages/html/Cargo.toml @@ -27,14 +27,6 @@ tracing.workspace = true futures-channel = { workspace = true } -# dioxus-core = { workspace = true } -# generational-box = { workspace = true } -# todo: we shouldn't have any wasm-bindgen dependencies in the html crate -# [target.'cfg(target_arch = "wasm32")'.dependencies] -# wasm-bindgen = { workspace = true, optional = true } -# js-sys = { version = "0.3.56", optional = true } -# wasm-bindgen-futures = { workspace = true, optional = true } - [build-dependencies] lazy-js-bundle = { workspace = true } diff --git a/packages/interpreter/Cargo.toml b/packages/interpreter/Cargo.toml index 8c48fbda38..d0c8086879 100644 --- a/packages/interpreter/Cargo.toml +++ b/packages/interpreter/Cargo.toml @@ -13,18 +13,15 @@ keywords = ["dom", "ui", "gui", "react", "wasm"] [dependencies] wasm-bindgen = { workspace = true, optional = true } wasm-bindgen-futures = { workspace = true, optional = true } -js-sys = { version = "0.3.56", optional = true } -web-sys = { version = "0.3.56", optional = true, features = [ - "Element", - "Node", -] } -sledgehammer_bindgen = { version = "0.6.0", default-features = false, optional = true } -sledgehammer_utils = { version = "0.3.1", optional = true } -serde = { version = "1.0", features = ["derive"], optional = true } +js-sys = { workspace = true, optional = true } +web-sys = { workspace = true, optional = true, features = ["Element", "Node"] } +serde = { workspace = true, optional = true, features = ["derive"] } rustc-hash = { workspace = true, optional = true } dioxus-core = { workspace = true, optional = true } dioxus-core-types = { workspace = true, optional = true } +sledgehammer_bindgen = { version = "0.6.0", default-features = false, optional = true } +sledgehammer_utils = { version = "0.3.1", optional = true } [build-dependencies] lazy-js-bundle = { workspace = true } diff --git a/packages/server/src/document.rs b/packages/server/src/document.rs new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/packages/server/src/document.rs @@ -0,0 +1 @@ + diff --git a/packages/server/src/lib.rs b/packages/server/src/lib.rs index 7d12d9af81..103318948c 100644 --- a/packages/server/src/lib.rs +++ b/packages/server/src/lib.rs @@ -1,14 +1 @@ -pub fn add(left: usize, right: usize) -> usize { - left + right -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } -} +mod document; diff --git a/packages/web/Cargo.toml b/packages/web/Cargo.toml index 5ca763a00c..e641cbd329 100644 --- a/packages/web/Cargo.toml +++ b/packages/web/Cargo.toml @@ -17,25 +17,21 @@ dioxus-html = { workspace = true } dioxus-document = { workspace = true } dioxus-devtools = { workspace = true } dioxus-signals = { workspace = true } -dioxus-interpreter-js = { workspace = true, features = [ - "minimal_bindings", - "webonly", -] } +dioxus-interpreter-js = { workspace = true, features = ["minimal_bindings", "webonly"] } generational-box = { workspace = true } - async-trait = { workspace = true } ciborium = { workspace = true, optional = true } console_error_panic_hook = { version = "0.1.7", optional = true } futures-channel = { workspace = true } futures-util = { workspace = true, features = [ "async-await", "async-await-macro" ] } -js-sys = "0.3.56" +js-sys = { workspace = true } rustc-hash = { workspace = true } serde = { workspace = true, optional = true } serde_json = { workspace = true, optional = true } serde-wasm-bindgen = { version = "0.6.5", optional = true } tracing = { workspace = true } wasm-bindgen = { workspace = true } -wasm-bindgen-futures = "0.4.29" +wasm-bindgen-futures = { workspace = true } [dependencies.web-sys] version = "0.3.56" From 00c461ae681c65c77a00933d68888f76b22c2540 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 12 Sep 2024 14:56:02 -0700 Subject: [PATCH 097/139] rename method --- packages/desktop/src/element.rs | 4 ++-- packages/dioxus/src/launch.rs | 5 +++++ packages/html/src/events/mounted.rs | 10 +++++----- packages/liveview/src/element.rs | 4 ++-- packages/web/src/event/synthetic.rs | 2 +- 5 files changed, 15 insertions(+), 10 deletions(-) diff --git a/packages/desktop/src/element.rs b/packages/desktop/src/element.rs index 4e2f8d89ac..89d84b219c 100644 --- a/packages/desktop/src/element.rs +++ b/packages/desktop/src/element.rs @@ -1,7 +1,7 @@ use dioxus_core::ElementId; use dioxus_html::{ geometry::{PixelsRect, PixelsSize, PixelsVector2D}, - MountedResult, RenderedElementBacking, ScrollBehavior, + MountedElement, MountedResult, ScrollBehavior, }; use crate::desktop_context::DesktopContext; @@ -20,7 +20,7 @@ impl DesktopElement { } #[async_trait::async_trait(?Send)] -impl RenderedElementBacking for DesktopElement { +impl MountedElement for DesktopElement { fn as_any(&self) -> &dyn std::any::Any { self } diff --git a/packages/dioxus/src/launch.rs b/packages/dioxus/src/launch.rs index 67ac1aaea2..ca60d360b6 100644 --- a/packages/dioxus/src/launch.rs +++ b/packages/dioxus/src/launch.rs @@ -197,6 +197,11 @@ impl LaunchBuilder { dioxus_fullstack::launch::launch(app, Default::default(), Default::default()); } + #[cfg(feature = "desktop")] + { + dioxus_desktop::launch::launch(app, Default::default(), Default::default()); + } + panic!("No platform feature enabled. Please enable one of the following features: liveview, desktop, mobile, web, fullstack to use the launch API.") } } diff --git a/packages/html/src/events/mounted.rs b/packages/html/src/events/mounted.rs index 23db1b12e6..8e8da9674c 100644 --- a/packages/html/src/events/mounted.rs +++ b/packages/html/src/events/mounted.rs @@ -6,7 +6,7 @@ use std::fmt::{Display, Formatter}; /// Different platforms will have different implementations and different levels of support for this trait. Renderers that do not support specific features will return `None` for those queries. // we can not use async_trait here because it does not create a trait that is object safe // #[async_trait::async_trait(?Send)] -pub trait RenderedElementBacking: std::any::Any { +pub trait MountedElement: std::any::Any { /// return self as Any fn as_any(&self) -> &dyn std::any::Any; @@ -43,7 +43,7 @@ pub trait RenderedElementBacking: std::any::Any { } } -impl RenderedElementBacking for () { +impl MountedElement for () { fn as_any(&self) -> &dyn std::any::Any { self } @@ -65,10 +65,10 @@ pub enum ScrollBehavior { /// /// Different platforms will have different implementations and different levels of support for this trait. Renderers that do not support specific features will return `None` for those queries. pub struct MountedData { - inner: Box, + inner: Box, } -impl From for MountedData { +impl From for MountedData { fn from(e: E) -> Self { Self { inner: Box::new(e) } } @@ -76,7 +76,7 @@ impl From for MountedData { impl MountedData { /// Create a new MountedData - pub fn new(registry: impl RenderedElementBacking + 'static) -> Self { + pub fn new(registry: impl MountedElement + 'static) -> Self { Self { inner: Box::new(registry), } diff --git a/packages/liveview/src/element.rs b/packages/liveview/src/element.rs index 7e4593d307..3befe6dc25 100644 --- a/packages/liveview/src/element.rs +++ b/packages/liveview/src/element.rs @@ -1,7 +1,7 @@ use dioxus_core::ElementId; use dioxus_html::{ geometry::{PixelsRect, PixelsSize, PixelsVector2D}, - MountedResult, RenderedElementBacking, + MountedResult, MountedElement, }; use crate::query::QueryEngine; @@ -47,7 +47,7 @@ macro_rules! scripted_getter { }; } -impl RenderedElementBacking for LiveviewElement { +impl MountedElement for LiveviewElement { fn as_any(&self) -> &dyn std::any::Any { self } diff --git a/packages/web/src/event/synthetic.rs b/packages/web/src/event/synthetic.rs index 98f3214a32..a3d7924f03 100644 --- a/packages/web/src/event/synthetic.rs +++ b/packages/web/src/event/synthetic.rs @@ -446,7 +446,7 @@ impl HasTransitionData for Synthetic { } #[cfg(feature = "mounted")] -impl RenderedElementBacking for Synthetic { +impl MountedElement for Synthetic { fn as_any(&self) -> &dyn std::any::Any { self } From ff32f462a2214373ce4b3bb4f4b3b0c2ad9a9153 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 13 Sep 2024 02:02:20 -0700 Subject: [PATCH 098/139] add log --- packages/cli/src/serve/handle.rs | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/serve/handle.rs b/packages/cli/src/serve/handle.rs index f3d4767cb5..f5d809368f 100644 --- a/packages/cli/src/serve/handle.rs +++ b/packages/cli/src/serve/handle.rs @@ -1,5 +1,5 @@ -use crate::Result; use crate::{builder::Platform, bundler::AppBundle}; +use crate::{Result, TraceSrc}; use std::{net::SocketAddr, path::PathBuf, process::Stdio}; use tokio::{ io::AsyncBufReadExt, @@ -51,6 +51,33 @@ impl AppHandle { stdout: None, }; + match platform { + TargetPlatform::Web => { + tracing::info!(dx_src = ?TraceSrc::Dev, "Serving web app on http://{} 🎉", serve.address.address()); + } + TargetPlatform::Desktop => { + tracing::info!(dx_src = ?TraceSrc::Dev, "Launching desktop app at {} 🎉", self.executable.display()); + } + TargetPlatform::Server => { + if let Some(fullstack_address) = fullstack_address { + tracing::info!( + dx_src = ?TraceSrc::Dev, + "Launching fullstack server on http://{:?} 🎉", + fullstack_address + ); + } + } + TargetPlatform::Liveview => { + if let Some(fullstack_address) = fullstack_address { + tracing::info!( + dx_src = ?TraceSrc::Dev, + "Launching liveview server on http://{:?} 🎉", + fullstack_address + ); + } + } + } + // open the exe with some arguments/envvars/etc // we're going to try and configure this binary from the environment, if we can // From 8284ee8159ed7127c31317562e129bfafb8c6ada Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 13 Sep 2024 02:18:36 -0700 Subject: [PATCH 099/139] comment out, fix merge conflict --- packages/cli/src/builder/progress.rs | 28 +- packages/cli/src/builder/web.rs | 18 +- packages/cli/src/main.rs | 1 + packages/cli/src/serve/handle.rs | 14 +- packages/cli/src/serve/output.rs | 635 ++++++++++++------------ packages/cli/src/serve/output/render.rs | 2 +- packages/cli/src/serve/watcher.rs | 2 +- packages/cli/src/tracer.rs | 4 +- packages/rsx/src/component.rs | 2 +- 9 files changed, 354 insertions(+), 352 deletions(-) diff --git a/packages/cli/src/builder/progress.rs b/packages/cli/src/builder/progress.rs index cb9465518f..c096d6e8ca 100644 --- a/packages/cli/src/builder/progress.rs +++ b/packages/cli/src/builder/progress.rs @@ -13,23 +13,23 @@ use tracing::Level; impl BuildRequest { pub(crate) fn status_build_diagnostic(&self, message: &Diagnostic) { - _ = self.progress.unbounded_send(BuildUpdateProgress { - stage: Stage::Compiling, - update: UpdateStage::AddMessage(message.clone().into()), - platform: self.platform(), - }); + // _ = self.progress.unbounded_send(BuildUpdateProgress { + // stage: Stage::Compiling, + // update: UpdateStage::AddMessage(message.clone().into()), + // platform: self.platform(), + // }); } pub(crate) fn status_build_message(&self, line: String) { - _ = self.progress.unbounded_send(BuildUpdateProgress { - platform: self.platform(), - stage: Stage::Compiling, - update: UpdateStage::AddMessage(BuildMessage { - level: Level::DEBUG, - message: MessageType::Text(line), - source: MessageSource::Build, - }), - }); + // _ = self.progress.unbounded_send(BuildUpdateProgress { + // platform: self.platform(), + // stage: Stage::Compiling, + // update: UpdateStage::AddMessage(BuildMessage { + // level: Level::DEBUG, + // message: MessageType::Text(line), + // source: MessageSource::Build, + // }), + // }); } pub(crate) fn status_build_progress(&self, build_progress: f64) { diff --git a/packages/cli/src/builder/web.rs b/packages/cli/src/builder/web.rs index 4f3326cdcb..270b61f079 100644 --- a/packages/cli/src/builder/web.rs +++ b/packages/cli/src/builder/web.rs @@ -268,15 +268,15 @@ impl BuildRequest { "{RESOURCE_DEPRECATION_MESSAGE}\nTo migrate to head components, remove `{section_name}` and include the following rsx in your root component:\n```rust\n{replacement_components}\n```" ); - _ = self.progress.unbounded_send(BuildUpdateProgress { - platform: self.platform(), - stage: Stage::OptimizingWasm, - update: UpdateStage::AddMessage(BuildMessage { - level: Level::WARN, - message: MessageType::Text(message), - source: MessageSource::Build, - }), - }); + // _ = self.progress.unbounded_send(BuildUpdateProgress { + // platform: self.platform(), + // stage: Stage::OptimizingWasm, + // update: UpdateStage::AddMessage(BuildMessage { + // level: Level::WARN, + // message: MessageType::Text(message), + // source: MessageSource::Build, + // }), + // }); } /// Check if the build is targeting the web platform diff --git a/packages/cli/src/main.rs b/packages/cli/src/main.rs index 1c11fd01f4..976e605e66 100644 --- a/packages/cli/src/main.rs +++ b/packages/cli/src/main.rs @@ -18,6 +18,7 @@ pub(crate) mod settings; pub(crate) mod tooling; pub(crate) mod tracer; +pub(crate) use builder::Platform; pub(crate) use cli::*; pub(crate) use dioxus_crate::*; pub(crate) use error::*; diff --git a/packages/cli/src/serve/handle.rs b/packages/cli/src/serve/handle.rs index f5d809368f..1703c127b5 100644 --- a/packages/cli/src/serve/handle.rs +++ b/packages/cli/src/serve/handle.rs @@ -52,13 +52,13 @@ impl AppHandle { }; match platform { - TargetPlatform::Web => { - tracing::info!(dx_src = ?TraceSrc::Dev, "Serving web app on http://{} 🎉", serve.address.address()); + Platform::Web => { + tracing::info!(dx_src = ?TraceSrc::Dev, "Serving web app on http://{} 🎉", ip); } - TargetPlatform::Desktop => { - tracing::info!(dx_src = ?TraceSrc::Dev, "Launching desktop app at {} 🎉", self.executable.display()); + Platform::Desktop => { + tracing::info!(dx_src = ?TraceSrc::Dev, "Launching desktop app at {} 🎉", handle.executable.display()); } - TargetPlatform::Server => { + Platform::Server => { if let Some(fullstack_address) = fullstack_address { tracing::info!( dx_src = ?TraceSrc::Dev, @@ -67,7 +67,9 @@ impl AppHandle { ); } } - TargetPlatform::Liveview => { + Platform::Ios => {} + Platform::Android => {} + Platform::Liveview => { if let Some(fullstack_address) = fullstack_address { tracing::info!( dx_src = ?TraceSrc::Dev, diff --git a/packages/cli/src/serve/output.rs b/packages/cli/src/serve/output.rs index 214392ef63..a5fcff9fdf 100644 --- a/packages/cli/src/serve/output.rs +++ b/packages/cli/src/serve/output.rs @@ -1,10 +1,8 @@ use crate::{ - builder::{BuildResult, UpdateStage}, - builder::{Stage, TargetPlatform, UpdateBuildProgress}, + builder::{BuildMessage, Platform, Stage, UpdateStage}, + cli::serve::ServeArgs, dioxus_crate::DioxusCrate, - serve::next_or_pending, - serve::Serve, - serve::{Builder, Server, Watcher}, + serve::{Builder, Watcher}, tracer::CLILogControl, TraceMsg, TraceSrc, }; @@ -18,20 +16,31 @@ use crossterm::{ tty::IsTty, ExecutableCommand, }; -use dioxus_cli_config::{AddressArguments, Platform}; -use dioxus_hot_reload::ClientMsg; -use futures_util::{future::select_all, Future, FutureExt, StreamExt}; -use ratatui::{prelude::*, TerminalOptions, Viewport}; +use dioxus_devtools_types::ClientMsg; +// use dioxus_cli_config::{AddressArguments, Platform}; +// use dioxus_hot_reload::ClientMsg; +use futures_util::{ + future::{select_all, OptionFuture}, + Future, FutureExt, StreamExt, +}; +use ratatui::{ + prelude::*, + widgets::{Block, Borders, Paragraph}, + TerminalOptions, Viewport, +}; use std::{ cell::RefCell, collections::{HashMap, HashSet}, + fmt::Display, io::{self, stdout}, rc::Rc, - time::Instant, + time::{Duration, Instant}, }; use tracing::Level; +use super::{AppHandle, DevServer, ServeUpdate}; + mod render; // How many lines should be scroll on each mouse scroll or arrow key input. @@ -41,26 +50,6 @@ const SCROLL_MODIFIER: u16 = 4; // Scroll modifier key. const SCROLL_MODIFIER_KEY: KeyModifiers = KeyModifiers::SHIFT; -#[derive(Default)] -pub struct BuildProgress { - current_builds: HashMap, -} - -impl BuildProgress { - pub fn progress(&self) -> f64 { - self.current_builds - .values() - .min_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)) - .map(|build| match build.stage { - Stage::Initializing => 0.0, - Stage::InstallingWasmTooling => 0.0, - Stage::Compiling => build.progress, - Stage::OptimizingWasm | Stage::OptimizingAssets | Stage::Finished => 1.0, - }) - .unwrap_or_default() - } -} - pub struct Output { term: Rc>>, @@ -68,7 +57,7 @@ pub struct Output { events: Option, pub(crate) build_progress: BuildProgress, - running_apps: HashMap, + // running_apps: HashMap, // A list of all messages from build, dev, app, and more. messages: Vec, @@ -84,7 +73,7 @@ pub struct Output { interactive: bool, _is_cli_release: bool, platform: Platform, - addr: AddressArguments, + // addr: AddressArguments, // Filters show_filter_menu: bool, @@ -164,13 +153,13 @@ impl Output { messages: Vec::new(), more_modal_open: false, build_progress: Default::default(), - running_apps: HashMap::new(), + // running_apps: HashMap::new(), scroll_position: 0, num_lines_wrapping: 0, console_width: 0, console_height: 0, anim_start: Instant::now(), - addr: cfg.server_arguments.address.clone(), + // addr: cfg.address.clone(), // Filter show_filter_menu: false, @@ -182,15 +171,15 @@ impl Output { } /// Add a message from stderr to the logs - fn push_stderr(&mut self, platform: TargetPlatform, stderr: String) { - self.running_apps - .get_mut(&platform) - .unwrap() - .output - .as_mut() - .unwrap() - .stderr_line - .push_str(&stderr); + pub fn push_stderr(&mut self, platform: Platform, stderr: String) { + // self.running_apps + // .get_mut(&platform) + // .unwrap() + // .output + // .as_mut() + // .unwrap() + // .stderr_line + // .push_str(&stderr); self.messages.push(TraceMsg { source: TraceSrc::App(platform), @@ -204,15 +193,15 @@ impl Output { } /// Add a message from stdout to the logs - fn push_stdout(&mut self, platform: TargetPlatform, stdout: String) { - self.running_apps - .get_mut(&platform) - .unwrap() - .output - .as_mut() - .unwrap() - .stdout_line - .push_str(&stdout); + pub fn push_stdout(&mut self, platform: Platform, stdout: String) { + // self.running_apps + // .get_mut(&platform) + // .unwrap() + // .output + // .as_mut() + // .unwrap() + // .stdout_line + // .push_str(&stdout); self.messages.push(TraceMsg { source: TraceSrc::App(platform), @@ -386,7 +375,7 @@ impl Output { if key.code == KeyCode::Char('o') && key.kind == KeyEventKind::Press => { // Open the running app. - open::that(format!("http://{}:{}", self.addr.addr, self.addr.port))?; + // open::that(format!("http://{}:{}", self.addr, self.port))?; } Event::Key(key) @@ -450,262 +439,250 @@ impl Output { } } - pub(crate) fn scroll_to_bottom(&mut self) { - self.scroll = (self.num_lines_with_wrapping).saturating_sub(self.term_height); - } + // pub(crate) fn scroll_to_bottom(&mut self) { + // self.scroll = (self.num_lines_with_wrapping).saturating_sub(self.term_height); + // } pub(crate) fn push_inner_log(&mut self, msg: String) { - self.push_log( - LogSource::Internal, - crate::builder::BuildMessage { - level: tracing::Level::INFO, - message: crate::builder::MessageType::Text(msg), - source: crate::builder::MessageSource::Dev, - }, - ); + // self.push_log( + // LogSource::Internal, + // crate::builder::BuildMessage { + // level: tracing::Level::INFO, + // message: crate::builder::MessageType::Text(msg), + // source: crate::builder::MessageSource::Dev, + // }, + // ); } - - pub(crate) fn new_build_logs(&mut self, platform: Platform, update: BuildUpdateProgress) { - let snapped = self.is_snapped(LogSource::Target(platform)); - - // when the build is finished, switch to the console - if update.stage == Stage::Finished { - self.tab = Tab::Console; - } - - fn is_snapped(&self) -> bool { - true - } - - pub fn scroll_to_bottom(&mut self) { - self.scroll_position = self.num_lines_wrapping.saturating_sub(self.console_height); - } - - pub fn push_log(&mut self, message: TraceMsg) { - self.messages.push(message); - - if self.is_snapped() { - self.scroll_to_bottom(); - } - } - - pub fn new_build_progress( - &mut self, - platform: TargetPlatform, - update: UpdateBuildProgress, - ) { - self.build_progress - .current_builds - .entry(platform) - .or_default() - .update(update); - - if self.is_snapped() { - self.scroll_to_bottom(); - } - } - - pub(crate) fn new_ready_app(&mut self, handle: &AppHandle) { - // Finish the build progress for the platform that just finished building - if let Some(build) = self - .build_progress - .build_logs - .get_mut(&handle.app.build.platform()) - { - build.stage = Stage::Finished; - } - } - - pub(crate) fn render( - &mut self, - _args: &ServeArgs, - _krate: &DioxusCrate, - _builder: &Builder, - server: &DevServer, - _watcher: &Watcher, - ) { - // just drain the build logs - if !self.interactive { - self.drain_print_logs(); - return; - } - - // Keep the animation track in terms of 100ms frames - the frame should be a number between 0 and 10 - // todo: we want to use this somehow to animate things... - let elapsed = self.anim_start.elapsed().as_millis() as f32; - let num_frames = elapsed / 100.0; - let _frame_step = (num_frames % 10.0) as usize; - - _ = self - .term - .clone() - .borrow_mut() - .as_mut() - .unwrap() - .draw(|frame| { - let mut layout = render::TuiLayout::new(frame.size(), self.show_filter_menu); - let (console_width, console_height) = layout.get_console_size(); - self.console_width = console_width; - self.console_height = console_height; - - // Render the decor first as some of it (such as backgrounds) may be rendered on top of. - layout.render_decor(frame, self.show_filter_menu); - - // Get only the enabled filters. - let mut enabled_filters = self.filters.clone(); - enabled_filters.retain(|f| f.1); - let enabled_filters = enabled_filters - .iter() - .map(|f| f.0.clone()) - .collect::>(); - - // Render console, we need the number of wrapping lines for scroll. - self.num_lines_wrapping = layout.render_console( - frame, - self.scroll_position, - &self.messages, - &enabled_filters, - ); - - // // Render a border for the header - // frame.render_widget(Block::default().borders(Borders::BOTTOM), body[0]); - - // Render the metadata - let mut spans: Vec = vec![ - Span::from(if self.is_cli_release { "dx" } else { "dx-dev" }).green(), - Span::from(" ").green(), - Span::from("serve").green(), - Span::from(" | ").white(), - Span::from(self.platform.to_string()).green(), - Span::from(" | ").white(), - ]; - - // If there is build progress, display that next to the platform - if !self.build_progress.build_logs.is_empty() { - if self - .build_progress - .build_logs - .values() - .any(|b| b.failed.is_some()) - { - spans.push(Span::from("build failed ❌").red()); - } else { - spans.push(Span::from("status: ").green()); - let build = self - .build_progress - .build_logs - .values() - .min_by(|a, b| { - a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal) - }) - .unwrap(); - spans.extend_from_slice(&build.spans(Rect::new( - 0, - 0, - build.max_layout_size(), - 1, - ))); - } - } - - frame - .render_widget(Paragraph::new(Line::from(spans)).left_aligned(), header[0]); - - // Split apart the body into a center and a right side - // We only want to show the sidebar if there's enough space - if listening_len > 0 { - frame.render_widget( - Paragraph::new(Line::from(vec![ - Span::from("listening at ").dark_gray(), - Span::from(format!("http://{}", server.ip).as_str()).gray(), - ])), - header[1], - ) - } - - if self.show_filter_menu { - layout.render_filter_menu( - frame, - &self.filters, - self.selected_filter_index, - self.filter_search_mode, - self.filter_search_input.as_ref(), - ); - } - - layout.render_status_bar( - frame, - self.platform, - &self.build_progress, - self.more_modal_open, - self.show_filter_menu, - &self._dx_version, - ); - - if self.more_modal_open { - layout.render_more_modal(frame); - } - - layout.render_current_scroll( - self.scroll_position, - self.num_lines_wrapping, - self.console_height, - frame, - ); - }); - } - } - - fn render_fly_modal(&mut self, frame: &mut Frame, area: Rect) { - if !self.fly_modal_open { - return; - } - - // Create a frame slightly smaller than the area - let panel = Layout::default() - .direction(Direction::Vertical) - .constraints([Constraint::Fill(1)].as_ref()) - .split(area)[0]; - - // Wipe the panel - frame.render_widget(Clear, panel); - frame.render_widget(Block::default().borders(Borders::ALL), panel); - - let modal = Paragraph::new("Under construction, please check back at a later date!\n") - .alignment(Alignment::Center); - frame.render_widget(modal, panel); + // pub(crate) fn new_build_logs(&mut self, platform: Platform, update: BuildUpdateProgress) { + // let snapped = self.is_snapped(LogSource::Target(platform)); + + // // when the build is finished, switch to the console + // if update.stage == Stage::Finished { + // self.tab = Tab::Console; + // } + // } + fn is_snapped(&self) -> bool { + true } - fn set_tab(&mut self, tab: Tab) { - self.tab = tab; - self.scroll = 0; + pub fn scroll_to_bottom(&mut self) { + self.scroll_position = self.num_lines_wrapping.saturating_sub(self.console_height); } - fn push_log(&mut self, platform: impl Into, message: BuildMessage) { - let source = platform.into(); - let snapped = self.is_snapped(source); - - match source { - LogSource::Internal => self.build_progress.internal_logs.push(message), - LogSource::Target(platform) => self - .build_progress - .build_logs - .entry(platform) - .or_default() - .stdout_logs - .push(message), - } + pub fn push_log(&mut self, message: TraceMsg) { + self.messages.push(message); - if snapped { + if self.is_snapped() { self.scroll_to_bottom(); } } - // todo: re-enable - #[allow(unused)] - fn is_snapped(&self, _platform: LogSource) -> bool { - true + // pub fn new_build_progress(&mut self, platform: Platform, update: BuildProgress) { + // self.build_progress + // .current_builds + // .entry(platform) + // .or_default() + // .update(update); + + // if self.is_snapped() { + // self.scroll_to_bottom(); + // } + // } + + pub(crate) fn new_ready_app(&mut self, handle: &AppHandle) { + // Finish the build progress for the platform that just finished building + if let Some(build) = self + .build_progress + .current_builds + .get_mut(&handle.app.build.platform()) + { + build.stage = Stage::Finished; + } } + + // pub(crate) fn render( + // &mut self, + // _args: &ServeArgs, + // _krate: &DioxusCrate, + // _builder: &Builder, + // server: &DevServer, + // _watcher: &Watcher, + // ) { + // // just drain the build logs + // if !self.interactive { + // self.drain_print_logs(); + // return; + // } + + // // Keep the animation track in terms of 100ms frames - the frame should be a number between 0 and 10 + // // todo: we want to use this somehow to animate things... + // let elapsed = self.anim_start.elapsed().as_millis() as f32; + // let num_frames = elapsed / 100.0; + // let _frame_step = (num_frames % 10.0) as usize; + + // _ = self + // .term + // .clone() + // .borrow_mut() + // .as_mut() + // .unwrap() + // .draw(|frame| { + // let mut layout = render::TuiLayout::new(frame.size(), self.show_filter_menu); + // let (console_width, console_height) = layout.get_console_size(); + // self.console_width = console_width; + // self.console_height = console_height; + + // // Render the decor first as some of it (such as backgrounds) may be rendered on top of. + // layout.render_decor(frame, self.show_filter_menu); + + // // Get only the enabled filters. + // let mut enabled_filters = self.filters.clone(); + // enabled_filters.retain(|f| f.1); + // let enabled_filters = enabled_filters + // .iter() + // .map(|f| f.0.clone()) + // .collect::>(); + + // // Render console, we need the number of wrapping lines for scroll. + // self.num_lines_wrapping = layout.render_console( + // frame, + // self.scroll_position, + // &self.messages, + // &enabled_filters, + // ); + + // // // Render a border for the header + // // frame.render_widget(Block::default().borders(Borders::BOTTOM), body[0]); + + // // Render the metadata + // let mut spans: Vec = vec![ + // Span::from(if self.is_cli_release { "dx" } else { "dx-dev" }).green(), + // Span::from(" ").green(), + // Span::from("serve").green(), + // Span::from(" | ").white(), + // Span::from(self.platform.to_string()).green(), + // Span::from(" | ").white(), + // ]; + + // // If there is build progress, display that next to the platform + // if !self.build_progress.current_builds.is_empty() { + // if self + // .build_progress + // .current_builds + // .values() + // .any(|b| b.failed.is_some()) + // { + // spans.push(Span::from("build failed ❌").red()); + // } else { + // spans.push(Span::from("status: ").green()); + // let build = self + // .build_progress + // .current_builds + // .values() + // .min_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)) + // .unwrap(); + + // spans.extend_from_slice(&build.spans(Rect::new( + // 0, + // 0, + // build.max_layout_size(), + // 1, + // ))); + // } + // } + + // frame.render_widget(Paragraph::new(Line::from(spans)).left_aligned(), header[0]); + + // // Split apart the body into a center and a right side + // // We only want to show the sidebar if there's enough space + // if listening_len > 0 { + // frame.render_widget( + // Paragraph::new(Line::from(vec![ + // Span::from("listening at ").dark_gray(), + // Span::from(format!("http://{}", server.ip).as_str()).gray(), + // ])), + // header[1], + // ) + // } + + // if self.show_filter_menu { + // layout.render_filter_menu( + // frame, + // &self.filters, + // self.selected_filter_index, + // self.filter_search_mode, + // self.filter_search_input.as_ref(), + // ); + // } + + // layout.render_status_bar( + // frame, + // self.platform, + // &self.build_progress, + // self.more_modal_open, + // self.show_filter_menu, + // &self._dx_version, + // ); + + // if self.more_modal_open { + // layout.render_more_modal(frame); + // } + + // layout.render_current_scroll( + // self.scroll_position, + // self.num_lines_wrapping, + // self.console_height, + // frame, + // ); + // }); + // } + + // fn render_fly_modal(&mut self, frame: &mut Frame, area: Rect) { + // if !self.fly_modal_open { + // return; + // } + + // // Create a frame slightly smaller than the area + // let panel = Layout::default() + // .direction(Direction::Vertical) + // .constraints([Constraint::Fill(1)].as_ref()) + // .split(area)[0]; + + // // Wipe the panel + // frame.render_widget(Clear, panel); + // frame.render_widget(Block::default().borders(Borders::ALL), panel); + + // let modal = Paragraph::new("Under construction, please check back at a later date!\n") + // .alignment(Alignment::Center); + // frame.render_widget(modal, panel); + // } + + // fn push_log(&mut self, platform: impl Into, message: BuildMessage) { + // let source = platform.into(); + // let snapped = self.is_snapped(); + + // match source { + // LogSource::Internal => self.build_progress.internal_logs.push(message), + // LogSource::Target(platform) => self + // .build_progress + // .current_builds + // .entry(platform) + // .or_default() + // .stdout_logs + // .push(message), + // } + + // if snapped { + // self.scroll_to_bottom(); + // } + // } + + // // todo: re-enable + // #[allow(unused)] + // fn is_snapped(&self) -> bool { + // true + // } + async fn handle_events(&mut self, event: Event) -> io::Result { let mut events = vec![event]; @@ -747,26 +724,26 @@ pub(crate) struct ActiveBuild { } impl ActiveBuild { - fn update(&mut self, update: BuildUpdateProgress) { - match update.update { - UpdateStage::Start => { - // If we are already past the stage, don't roll back, but allow a fresh build to update. - if self.stage > update.stage && self.stage < Stage::Finished { - return; - } - self.stage = update.stage; - self.progress = 0.0; - self.failed = None; - } - UpdateStage::SetProgress(progress) => { - self.progress = progress; - } - UpdateStage::Failed(failed) => { - self.stage = Stage::Finished; - self.failed = Some(failed.clone()); - } - } - } + // fn update(&mut self, update: BuildUpdateProgress) { + // match update.update { + // UpdateStage::Start => { + // // If we are already past the stage, don't roll back, but allow a fresh build to update. + // if self.stage > update.stage && self.stage < Stage::Finished { + // return; + // } + // self.stage = update.stage; + // self.progress = 0.0; + // self.failed = None; + // } + // UpdateStage::SetProgress(progress) => { + // self.progress = progress; + // } + // UpdateStage::Failed(failed) => { + // self.stage = Stage::Finished; + // self.failed = Some(failed.clone()); + // } + // } + // } fn make_spans(&self, area: Rect) -> Vec { let mut spans = Vec::new(); @@ -874,12 +851,32 @@ impl From for LogSource { #[derive(Default)] pub(crate) struct BuildProgress { internal_logs: Vec, - build_logs: HashMap, + current_builds: HashMap, } +// impl BuildProgress { +// pub(crate) fn progress(&self) -> f64 { +// self.build_logs +// .values() +// .min_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)) +// .map(|build| match build.stage { +// Stage::Initializing => 0.0, +// Stage::InstallingWasmTooling => 0.0, +// Stage::Compiling => build.progress, +// Stage::OptimizingWasm | Stage::OptimizingAssets | Stage::Finished => 1.0, +// }) +// .unwrap_or_default() +// } +// } + +// #[derive(Default)] +// pub struct BuildProgress { +// current_builds: HashMap, +// } + impl BuildProgress { - pub(crate) fn progress(&self) -> f64 { - self.build_logs + pub fn progress(&self) -> f64 { + self.current_builds .values() .min_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)) .map(|build| match build.stage { diff --git a/packages/cli/src/serve/output/render.rs b/packages/cli/src/serve/output/render.rs index 02aa93fe21..fdaac0adc0 100644 --- a/packages/cli/src/serve/output/render.rs +++ b/packages/cli/src/serve/output/render.rs @@ -1,6 +1,6 @@ use super::{BuildProgress, TraceMsg, TraceSrc}; +use crate::Platform; use ansi_to_tui::IntoText as _; -use dioxus_cli_config::Platform; use ratatui::{ layout::{Alignment, Constraint, Direction, Layout, Rect}, style::{Color, Style, Stylize}, diff --git a/packages/cli/src/serve/watcher.rs b/packages/cli/src/serve/watcher.rs index 892073a959..0d7e01c99a 100644 --- a/packages/cli/src/serve/watcher.rs +++ b/packages/cli/src/serve/watcher.rs @@ -1,7 +1,7 @@ use super::{detect::is_wsl, AppRunner}; use super::{hot_reloading_file_map::HotreloadError, update::ServeUpdate}; -use crate::serve::hot_reloading_file_map::FileMap; use crate::{cli::serve::ServeArgs, dioxus_crate::DioxusCrate}; +use crate::{serve::hot_reloading_file_map::FileMap, TraceSrc}; use dioxus_devtools_types::HotReloadMsg; use dioxus_html::HtmlCtx; use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender}; diff --git a/packages/cli/src/tracer.rs b/packages/cli/src/tracer.rs index 5016d79d9e..a7765cde37 100644 --- a/packages/cli/src/tracer.rs +++ b/packages/cli/src/tracer.rs @@ -14,7 +14,7 @@ //! 3. Build CLI layer for routing tracing logs to the TUI. //! 4. Build fmt layer for non-interactive logging with a custom writer that prevents output during interactive mode. -use crate::builder::TargetPlatform; +use crate::Platform as TargetPlatform; use console::strip_ansi_codes; use futures_channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender}; use std::fmt::Display; @@ -404,6 +404,8 @@ impl Display for TraceSrc { TargetPlatform::Desktop => write!(f, "desktop"), TargetPlatform::Server => write!(f, "server"), TargetPlatform::Liveview => write!(f, "server"), + TargetPlatform::Ios => write!(f, "ios"), + TargetPlatform::Android => write!(f, "android"), }, Self::Dev => write!(f, "dev"), Self::Build => write!(f, "build"), diff --git a/packages/rsx/src/component.rs b/packages/rsx/src/component.rs index 821f95dfc6..af5a33baff 100644 --- a/packages/rsx/src/component.rs +++ b/packages/rsx/src/component.rs @@ -253,7 +253,7 @@ impl Component { } // Iterate over the props of the component (without spreads, key, and custom attributes) - pub(crate) fn component_props(&self) -> impl Iterator { + pub fn component_props(&self) -> impl Iterator { self.fields .iter() .filter(move |attr| !attr.name.is_likely_key()) From 15dc656fc3498d7e7ef00defa2faf582df28d1ec Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 13 Sep 2024 02:22:16 -0700 Subject: [PATCH 100/139] compiles again --- packages/cli/src/serve/mod.rs | 2 +- packages/cli/src/serve/output.rs | 83 ++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/serve/mod.rs b/packages/cli/src/serve/mod.rs index f490e5e6df..f6f4c01579 100644 --- a/packages/cli/src/serve/mod.rs +++ b/packages/cli/src/serve/mod.rs @@ -156,7 +156,7 @@ async fn handle_msg( // We also can check the status of the builds here in case we have multiple ongoing builds ServeUpdate::BuildUpdate(BuildUpdate::Progress(update)) => { let update_clone = update.clone(); - screen.new_build_logs(update.platform, update_clone); + // screen.new_build_logs(update.platform, update_clone); devserver .update_build_status(screen.build_progress.progress(), update.stage.to_string()) .await; diff --git a/packages/cli/src/serve/output.rs b/packages/cli/src/serve/output.rs index a5fcff9fdf..1b48c0d281 100644 --- a/packages/cli/src/serve/output.rs +++ b/packages/cli/src/serve/output.rs @@ -500,6 +500,89 @@ impl Output { } } + pub fn render( + &mut self, + _opts: &ServeArgs, + _config: &DioxusCrate, + _build_engine: &Builder, + _server: &DevServer, + _watcher: &Watcher, + ) { + // just drain the build logs + if !self.interactive { + self.drain_print_logs(); + return; + } + + // Keep the animation track in terms of 100ms frames - the frame should be a number between 0 and 10 + // todo: we want to use this somehow to animate things... + let elapsed = self.anim_start.elapsed().as_millis() as f32; + let num_frames = elapsed / 100.0; + let _frame_step = (num_frames % 10.0) as usize; + + _ = self + .term + .clone() + .borrow_mut() + .as_mut() + .unwrap() + .draw(|frame| { + let mut layout = render::TuiLayout::new(frame.size(), self.show_filter_menu); + let (console_width, console_height) = layout.get_console_size(); + self.console_width = console_width; + self.console_height = console_height; + + // Render the decor first as some of it (such as backgrounds) may be rendered on top of. + layout.render_decor(frame, self.show_filter_menu); + + // Get only the enabled filters. + let mut enabled_filters = self.filters.clone(); + enabled_filters.retain(|f| f.1); + let enabled_filters = enabled_filters + .iter() + .map(|f| f.0.clone()) + .collect::>(); + + // Render console, we need the number of wrapping lines for scroll. + self.num_lines_wrapping = layout.render_console( + frame, + self.scroll_position, + &self.messages, + &enabled_filters, + ); + + if self.show_filter_menu { + layout.render_filter_menu( + frame, + &self.filters, + self.selected_filter_index, + self.filter_search_mode, + self.filter_search_input.as_ref(), + ); + } + + layout.render_status_bar( + frame, + self.platform, + &self.build_progress, + self.more_modal_open, + self.show_filter_menu, + &self._dx_version, + ); + + if self.more_modal_open { + layout.render_more_modal(frame); + } + + layout.render_current_scroll( + self.scroll_position, + self.num_lines_wrapping, + self.console_height, + frame, + ); + }); + } + // pub(crate) fn render( // &mut self, // _args: &ServeArgs, From 224d30ed418046514dc9a43f8c42cf703f5e4bf5 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 13 Sep 2024 02:28:37 -0700 Subject: [PATCH 101/139] oh damn it launches --- packages/cli/src/serve/mod.rs | 27 +++++++++++---------------- packages/cli/src/serve/output.rs | 18 ++++++++++-------- 2 files changed, 21 insertions(+), 24 deletions(-) diff --git a/packages/cli/src/serve/mod.rs b/packages/cli/src/serve/mod.rs index f6f4c01579..2eca271655 100644 --- a/packages/cli/src/serve/mod.rs +++ b/packages/cli/src/serve/mod.rs @@ -56,9 +56,6 @@ pub(crate) async fn serve_all(args: ServeArgs, krate: DioxusCrate) -> Result<()> let mut runner = AppRunner::start(); loop { - // Make sure we don't hog the CPU: these loop { select! {} } blocks can starve the executor if we're not careful - tokio::task::yield_now().await; - // Draw the state of the server to the screen screen.render(&args, &krate, &builder, &devserver, &watcher); @@ -72,7 +69,7 @@ pub(crate) async fn serve_all(args: ServeArgs, krate: DioxusCrate) -> Result<()> msg = tracer.wait() => msg, }; - match handle_msg( + let res = handle_it( msg, &args, &mut devserver, @@ -80,19 +77,17 @@ pub(crate) async fn serve_all(args: ServeArgs, krate: DioxusCrate) -> Result<()> &mut builder, &mut runner, &mut watcher, - ) - .await - { - Ok(ControlFlow::Break(())) => break, - Ok(ControlFlow::Continue(())) => {} - Err(e) => { - tracing::error!("Error handling message: {}", e); - break; - } + ); + + match res.await { + Ok(ControlFlow::Continue(())) => continue, + Ok(ControlFlow::Break(())) => {} + Err(e) => tracing::error!("Error in TUI: {}", e), } + + break; } - // Kill the clients first _ = devserver.shutdown().await; _ = screen.shutdown(); _ = builder.abort_all(); @@ -101,7 +96,7 @@ pub(crate) async fn serve_all(args: ServeArgs, krate: DioxusCrate) -> Result<()> Ok(()) } -async fn handle_msg( +async fn handle_it( msg: ServeUpdate, args: &ServeArgs, devserver: &mut DevServer, @@ -156,7 +151,7 @@ async fn handle_msg( // We also can check the status of the builds here in case we have multiple ongoing builds ServeUpdate::BuildUpdate(BuildUpdate::Progress(update)) => { let update_clone = update.clone(); - // screen.new_build_logs(update.platform, update_clone); + screen.new_build_logs(update.platform, update_clone); devserver .update_build_status(screen.build_progress.progress(), update.stage.to_string()) .await; diff --git a/packages/cli/src/serve/output.rs b/packages/cli/src/serve/output.rs index 1b48c0d281..3443d552ae 100644 --- a/packages/cli/src/serve/output.rs +++ b/packages/cli/src/serve/output.rs @@ -1,5 +1,5 @@ use crate::{ - builder::{BuildMessage, Platform, Stage, UpdateStage}, + builder::{BuildMessage, BuildUpdateProgress, Platform, Stage, UpdateStage}, cli::serve::ServeArgs, dioxus_crate::DioxusCrate, serve::{Builder, Watcher}, @@ -453,14 +453,16 @@ impl Output { // }, // ); } - // pub(crate) fn new_build_logs(&mut self, platform: Platform, update: BuildUpdateProgress) { - // let snapped = self.is_snapped(LogSource::Target(platform)); - // // when the build is finished, switch to the console - // if update.stage == Stage::Finished { - // self.tab = Tab::Console; - // } - // } + pub(crate) fn new_build_logs(&mut self, platform: Platform, update: BuildUpdateProgress) { + // let snapped = self.is_snapped(LogSource::Target(platform)); + + // // when the build is finished, switch to the console + // if update.stage == Stage::Finished { + // self.tab = Tab::Console; + // } + } + fn is_snapped(&self) -> bool { true } From ba09427480a0e6185cf2e0c0e75b0db4f8cb850f Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 13 Sep 2024 02:29:33 -0700 Subject: [PATCH 102/139] fix dioxus compile --- packages/dioxus/src/launch.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/dioxus/src/launch.rs b/packages/dioxus/src/launch.rs index dcf05180ed..534d2ac777 100644 --- a/packages/dioxus/src/launch.rs +++ b/packages/dioxus/src/launch.rs @@ -151,9 +151,7 @@ impl LaunchBuilder { /// /// LaunchBuilder::custom(my_custom_launcher).launch(app); /// ``` - pub fn custom( - launch_fn: LaunchFn, - ) -> LaunchBuilder { + pub fn custom(launch_fn: LaunchFn) -> LaunchBuilder { LaunchBuilder { // launch_fn, // contexts: vec![], From dbe663461e3f761b0adfd42bd4daacb188eb8607 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 13 Sep 2024 02:54:28 -0700 Subject: [PATCH 103/139] hmm --- packages/mobile/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mobile/Cargo.toml b/packages/mobile/Cargo.toml index bce9a513bd..1a058c7ce1 100644 --- a/packages/mobile/Cargo.toml +++ b/packages/mobile/Cargo.toml @@ -14,8 +14,8 @@ dioxus-desktop = { workspace = true } [lib] doctest = false -test = false # tests suspended until package ready +test = false [package.metadata.docs.rs] cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"] From afcb52c2df20234e5560032fce97209e328a9280 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 13 Sep 2024 13:01:34 -0700 Subject: [PATCH 104/139] remove liveview project --- Cargo.toml | 1 - example-projects/basic-liveview/.gitignore | 4 ---- example-projects/basic-liveview/Cargo.toml | 14 -------------- example-projects/basic-liveview/src/main.rs | 13 ------------- 4 files changed, 32 deletions(-) delete mode 100644 example-projects/basic-liveview/.gitignore delete mode 100644 example-projects/basic-liveview/Cargo.toml delete mode 100644 example-projects/basic-liveview/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index 320ba4f28c..144a203ea4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,7 +71,6 @@ members = [ "example-projects/fullstack-mobile", "example-projects/tailwind", "example-projects/pwa", - "example-projects/basic-liveview", "example-projects/liveview-router", ] diff --git a/example-projects/basic-liveview/.gitignore b/example-projects/basic-liveview/.gitignore deleted file mode 100644 index 21fff11dd3..0000000000 --- a/example-projects/basic-liveview/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -dist -target -static -.dioxus \ No newline at end of file diff --git a/example-projects/basic-liveview/Cargo.toml b/example-projects/basic-liveview/Cargo.toml deleted file mode 100644 index 8fde99c483..0000000000 --- a/example-projects/basic-liveview/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "basic-liveview" -version = "0.1.0" -edition = "2021" -publish = false - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -dioxus = { workspace = true, features = ["liveview"] } - -[features] -default = [] -liveview = ["dioxus/liveview"] diff --git a/example-projects/basic-liveview/src/main.rs b/example-projects/basic-liveview/src/main.rs deleted file mode 100644 index bd334a0189..0000000000 --- a/example-projects/basic-liveview/src/main.rs +++ /dev/null @@ -1,13 +0,0 @@ -use dioxus::prelude::*; - -fn main() { - println!("Launching app..."); - - dioxus::launch(app); -} - -fn app() -> Element { - rsx! { - div { "Hello, world!" } - } -} From a858c952edc332bb09afaf0e6ac0b88858d90239 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 13 Sep 2024 21:36:53 -0700 Subject: [PATCH 105/139] bump native --- Cargo.lock | 7 - packages/cli/src/cli/bundle.rs | 3 + packages/cli/src/cli/config.rs | 2 + packages/cli/src/cli/init.rs | 2 + packages/cli/src/cli/mod.rs | 16 +- packages/cli/src/config.rs | 7 +- packages/cli/src/main.rs | 3 +- packages/cli/src/serve/console_widget.rs | 2 + packages/cli/src/serve/mod.rs | 10 +- packages/cli/src/serve/output.rs | 346 +++-------------------- packages/cli/src/serve/output/render.rs | 125 ++++---- packages/desktop/Cargo.toml | 2 - packages/interpreter/src/js/common.js | 2 +- packages/interpreter/src/js/core.js | 2 +- packages/interpreter/src/js/hash.txt | 2 +- packages/interpreter/src/js/native.js | 1 + packages/interpreter/src/ts/native.ts | 2 - 17 files changed, 146 insertions(+), 388 deletions(-) create mode 100644 packages/cli/src/serve/console_widget.rs diff --git a/Cargo.lock b/Cargo.lock index bdc2f22f75..aeb4be98bd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -863,13 +863,6 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" -[[package]] -name = "basic-liveview" -version = "0.1.0" -dependencies = [ - "dioxus", -] - [[package]] name = "bcder" version = "0.7.4" diff --git a/packages/cli/src/cli/bundle.rs b/packages/cli/src/cli/bundle.rs index 4397a03f27..59f0fd10c2 100644 --- a/packages/cli/src/cli/bundle.rs +++ b/packages/cli/src/cli/bundle.rs @@ -15,6 +15,7 @@ pub struct Bundle { /// The package types to bundle #[clap(long)] pub packages: Option>, + /// The arguments for the dioxus build #[clap(flatten)] pub(crate) build_arguments: BuildArgs, @@ -44,6 +45,7 @@ impl FromStr for PackageType { "rpm" => Ok(PackageType::Rpm), "appimage" => Ok(PackageType::AppImage), "dmg" => Ok(PackageType::Dmg), + "updater" => Ok(PackageType::Updater), _ => Err(format!("{} is not a valid package type", s)), } } @@ -122,6 +124,7 @@ impl Bundle { // Copy the assets in the dist directory to the bundle let static_asset_output_dir = &dioxus_crate.dioxus_config.application.out_dir; + // Make sure the dist directory is relative to the crate directory let static_asset_output_dir = static_asset_output_dir .strip_prefix(dioxus_crate.workspace_dir()) diff --git a/packages/cli/src/cli/config.rs b/packages/cli/src/cli/config.rs index 974c1eb7f2..6292f16388 100644 --- a/packages/cli/src/cli/config.rs +++ b/packages/cli/src/cli/config.rs @@ -22,8 +22,10 @@ pub(crate) enum Config { #[clap(long, default_value = "web")] platform: String, }, + /// Format print Dioxus config. FormatPrint {}, + /// Create a custom html file. CustomHtml {}, diff --git a/packages/cli/src/cli/init.rs b/packages/cli/src/cli/init.rs index a9231cfa9b..f57d1eeaf5 100644 --- a/packages/cli/src/cli/init.rs +++ b/packages/cli/src/cli/init.rs @@ -31,12 +31,14 @@ impl Init { let name = std::env::current_dir()? .file_name() .map(|f| f.to_str().unwrap().to_string()); + // https://github.com/console-rs/dialoguer/issues/294 ctrlc::set_handler(move || { let _ = console::Term::stdout().show_cursor(); std::process::exit(0); }) .expect("ctrlc::set_handler"); + let args = GenerateArgs { define: self.option, init: true, diff --git a/packages/cli/src/cli/mod.rs b/packages/cli/src/cli/mod.rs index 8bd108cb06..6477528825 100644 --- a/packages/cli/src/cli/mod.rs +++ b/packages/cli/src/cli/mod.rs @@ -26,14 +26,6 @@ use std::{ process::{Command, Stdio}, }; -pub(crate) static VERSION: Lazy = Lazy::new(|| { - format!( - "{} ({})", - crate::build_info::PKG_VERSION, - crate::build_info::GIT_COMMIT_HASH_SHORT.unwrap_or("was built without git repository") - ) -}); - /// Build, Bundle & Ship Dioxus Apps. #[derive(Parser)] #[clap(name = "dioxus", version = VERSION.as_str())] @@ -118,3 +110,11 @@ impl Display for Commands { } } } + +pub(crate) static VERSION: Lazy = Lazy::new(|| { + format!( + "{} ({})", + crate::build_info::PKG_VERSION, + crate::build_info::GIT_COMMIT_HASH_SHORT.unwrap_or("was built without git repository") + ) +}); diff --git a/packages/cli/src/config.rs b/packages/cli/src/config.rs index 27b7a52403..3a80412965 100644 --- a/packages/cli/src/config.rs +++ b/packages/cli/src/config.rs @@ -5,9 +5,6 @@ mod dioxus_config; mod platform; mod serve; mod web; -// mod fullstack; -// mod liveview; -// mod static_generation; pub(crate) use app::*; pub(crate) use bundle::*; @@ -15,3 +12,7 @@ pub(crate) use desktop::*; pub(crate) use dioxus_config::*; pub(crate) use serve::*; pub(crate) use web::*; + +// mod fullstack; +// mod liveview; +// mod static_generation; diff --git a/packages/cli/src/main.rs b/packages/cli/src/main.rs index 976e605e66..b2602779f0 100644 --- a/packages/cli/src/main.rs +++ b/packages/cli/src/main.rs @@ -39,8 +39,7 @@ async fn main() -> anyhow::Result<()> { // Start the tracer so it captures logs from the build engine before we start the builder crate::serve::TraceController::initialize(); - let args = Cli::parse(); - match args.action { + match Cli::parse().action { Translate(opts) => opts .translate() .context("⛔️ Translation of HTML into RSX failed:"), diff --git a/packages/cli/src/serve/console_widget.rs b/packages/cli/src/serve/console_widget.rs new file mode 100644 index 0000000000..69766c4c87 --- /dev/null +++ b/packages/cli/src/serve/console_widget.rs @@ -0,0 +1,2 @@ +/// Scrollable console widget +pub struct ConsoleWidget {} diff --git a/packages/cli/src/serve/mod.rs b/packages/cli/src/serve/mod.rs index 2eca271655..f188d661da 100644 --- a/packages/cli/src/serve/mod.rs +++ b/packages/cli/src/serve/mod.rs @@ -2,8 +2,10 @@ use crate::builder::{BuildUpdate, BuildUpdateProgress, Builder, Platform, Stage, use crate::cli::serve::ServeArgs; use crate::DioxusCrate; use crate::Result; +use crate::TraceSrc; use std::ops::ControlFlow; +mod console_widget; mod detect; mod handle; mod hot_reloading_file_map; @@ -69,7 +71,7 @@ pub(crate) async fn serve_all(args: ServeArgs, krate: DioxusCrate) -> Result<()> msg = tracer.wait() => msg, }; - let res = handle_it( + let res = handle_update( msg, &args, &mut devserver, @@ -96,7 +98,7 @@ pub(crate) async fn serve_all(args: ServeArgs, krate: DioxusCrate) -> Result<()> Ok(()) } -async fn handle_it( +async fn handle_update( msg: ServeUpdate, args: &ServeArgs, devserver: &mut DevServer, @@ -180,7 +182,7 @@ async fn handle_it( } ServeUpdate::BuildUpdate(BuildUpdate::BuildReady { target, result }) => { - tracing::info!("Opening app for [{}]", target); + tracing::info!(dx_src = ?TraceSrc::Dev, "Opening app for [{}]", target); let handle = runner .open(result, devserver.ip, devserver.fullstack_address()) @@ -208,7 +210,7 @@ async fn handle_it( // If the process exited *cleanly*, we can exit ServeUpdate::ProcessExited { status, platform } => { if !status.success() { - tracing::error!("Application [{platform}] exited with status: {status}"); + tracing::error!(dx_src = ?TraceSrc::Dev, "Application [{platform}] exited with status: {status}"); return Ok(ControlFlow::Break(())); } diff --git a/packages/cli/src/serve/output.rs b/packages/cli/src/serve/output.rs index 3443d552ae..89feb9f468 100644 --- a/packages/cli/src/serve/output.rs +++ b/packages/cli/src/serve/output.rs @@ -17,8 +17,6 @@ use crossterm::{ ExecutableCommand, }; use dioxus_devtools_types::ClientMsg; -// use dioxus_cli_config::{AddressArguments, Platform}; -// use dioxus_hot_reload::ClientMsg; use futures_util::{ future::{select_all, OptionFuture}, Future, FutureExt, StreamExt, @@ -52,15 +50,12 @@ const SCROLL_MODIFIER_KEY: KeyModifiers = KeyModifiers::SHIFT; pub struct Output { term: Rc>>, - - // optional since when there's no tty there's no eventstream to read from - just stdin events: Option, pub(crate) build_progress: BuildProgress, - // running_apps: HashMap, // A list of all messages from build, dev, app, and more. - messages: Vec, + messages: Vec, num_lines_wrapping: u16, scroll_position: u16, @@ -71,9 +66,7 @@ pub struct Output { anim_start: Instant, interactive: bool, - _is_cli_release: bool, platform: Platform, - // addr: AddressArguments, // Filters show_filter_menu: bool, @@ -82,9 +75,7 @@ pub struct Output { filter_search_mode: bool, filter_search_input: Option, - _rustc_version: String, - _rustc_nightly: bool, - _dx_version: String, + dx_version: String, } type TerminalBackend = Terminal>; @@ -93,7 +84,6 @@ impl Output { pub(crate) fn start(cfg: &ServeArgs) -> io::Result { let interactive = cfg.interactive_tty(); let mut events = None; - if interactive { // log_control.output_enabled.store(true, Ordering::SeqCst); enable_raw_mode()?; @@ -119,19 +109,8 @@ impl Output { ) .ok(); - // todo: re-enable rustc version - // let rustc_version = rustc_version().await; - // let rustc_nightly = rustc_version.contains("nightly") || cfg.target_args.nightly; - let _rustc_version = String::from("1.0.0"); - let _rustc_nightly = false; - - let mut dx_version = String::new(); - - dx_version.push_str(env!("CARGO_PKG_VERSION")); - - let is_cli_release = crate::build_info::PROFILE == "release"; - - if !is_cli_release { + let mut dx_version = format!("{}", env!("CARGO_PKG_VERSION")); + if crate::build_info::PROFILE != "release" { if let Some(hash) = crate::build_info::GIT_COMMIT_HASH_SHORT { let hash = &hash.trim_start_matches('g')[..4]; dx_version.push('-'); @@ -144,22 +123,18 @@ impl Output { Ok(Self { term: Rc::new(RefCell::new(term)), events, - _rustc_version, - _rustc_nightly, - _dx_version: dx_version, + + dx_version, interactive, - _is_cli_release: is_cli_release, platform, messages: Vec::new(), more_modal_open: false, build_progress: Default::default(), - // running_apps: HashMap::new(), scroll_position: 0, num_lines_wrapping: 0, console_width: 0, console_height: 0, anim_start: Instant::now(), - // addr: cfg.address.clone(), // Filter show_filter_menu: false, @@ -172,20 +147,11 @@ impl Output { /// Add a message from stderr to the logs pub fn push_stderr(&mut self, platform: Platform, stderr: String) { - // self.running_apps - // .get_mut(&platform) - // .unwrap() - // .output - // .as_mut() - // .unwrap() - // .stderr_line - // .push_str(&stderr); - - self.messages.push(TraceMsg { + self.messages.push(ConsoleMessage::Log(TraceMsg { source: TraceSrc::App(platform), level: Level::ERROR, content: stderr, - }); + })); if self.is_snapped() { self.scroll_to_bottom(); @@ -194,20 +160,11 @@ impl Output { /// Add a message from stdout to the logs pub fn push_stdout(&mut self, platform: Platform, stdout: String) { - // self.running_apps - // .get_mut(&platform) - // .unwrap() - // .output - // .as_mut() - // .unwrap() - // .stdout_line - // .push_str(&stdout); - - self.messages.push(TraceMsg { + self.messages.push(ConsoleMessage::Log(TraceMsg { source: TraceSrc::App(platform), level: Level::INFO, content: stdout, - }); + })); if self.is_snapped() { self.scroll_to_bottom(); @@ -251,11 +208,19 @@ impl Output { let messages = self.messages.drain(..); for msg in messages { - // TODO: Better formatting for different content lengths. - if msg.source != TraceSrc::Cargo { - println!("[{}] {}: {}", msg.source, msg.level, msg.content); - } else { - println!("{}", msg.content); + match msg { + ConsoleMessage::Log(msg) => { + if msg.source != TraceSrc::Cargo { + println!("[{}] {}: {}", msg.source, msg.level, msg.content); + } else { + println!("{}", msg.content); + } + } + + ConsoleMessage::BuildReady => { + // TODO: Better formatting for different content lengths. + } + ConsoleMessage::OnngoingBuild { stage, progress } => todo!(), } } } @@ -444,23 +409,22 @@ impl Output { // } pub(crate) fn push_inner_log(&mut self, msg: String) { - // self.push_log( - // LogSource::Internal, - // crate::builder::BuildMessage { - // level: tracing::Level::INFO, - // message: crate::builder::MessageType::Text(msg), - // source: crate::builder::MessageSource::Dev, - // }, - // ); + self.push_log( + TraceMsg::new(TraceSrc::Build, Level::INFO, msg), + // crate::builder::BuildMessage { + // level: tracing::Level::INFO, + // message: crate::builder::MessageType::Text(msg), + // source: crate::builder::MessageSource::Dev, + // }, + ); } pub(crate) fn new_build_logs(&mut self, platform: Platform, update: BuildUpdateProgress) { - // let snapped = self.is_snapped(LogSource::Target(platform)); - - // // when the build is finished, switch to the console - // if update.stage == Stage::Finished { - // self.tab = Tab::Console; - // } + self.push_log(TraceMsg::new( + TraceSrc::Build, + Level::INFO, + format!("Build progress for {platform:?}: {update:?}"), + )) } fn is_snapped(&self) -> bool { @@ -472,25 +436,13 @@ impl Output { } pub fn push_log(&mut self, message: TraceMsg) { - self.messages.push(message); + self.messages.push(ConsoleMessage::Log(message)); if self.is_snapped() { self.scroll_to_bottom(); } } - // pub fn new_build_progress(&mut self, platform: Platform, update: BuildProgress) { - // self.build_progress - // .current_builds - // .entry(platform) - // .or_default() - // .update(update); - - // if self.is_snapped() { - // self.scroll_to_bottom(); - // } - // } - pub(crate) fn new_ready_app(&mut self, handle: &AppHandle) { // Finish the build progress for the platform that just finished building if let Some(build) = self @@ -569,7 +521,7 @@ impl Output { &self.build_progress, self.more_modal_open, self.show_filter_menu, - &self._dx_version, + &self.dx_version, ); if self.more_modal_open { @@ -584,221 +536,15 @@ impl Output { ); }); } +} - // pub(crate) fn render( - // &mut self, - // _args: &ServeArgs, - // _krate: &DioxusCrate, - // _builder: &Builder, - // server: &DevServer, - // _watcher: &Watcher, - // ) { - // // just drain the build logs - // if !self.interactive { - // self.drain_print_logs(); - // return; - // } - - // // Keep the animation track in terms of 100ms frames - the frame should be a number between 0 and 10 - // // todo: we want to use this somehow to animate things... - // let elapsed = self.anim_start.elapsed().as_millis() as f32; - // let num_frames = elapsed / 100.0; - // let _frame_step = (num_frames % 10.0) as usize; - - // _ = self - // .term - // .clone() - // .borrow_mut() - // .as_mut() - // .unwrap() - // .draw(|frame| { - // let mut layout = render::TuiLayout::new(frame.size(), self.show_filter_menu); - // let (console_width, console_height) = layout.get_console_size(); - // self.console_width = console_width; - // self.console_height = console_height; - - // // Render the decor first as some of it (such as backgrounds) may be rendered on top of. - // layout.render_decor(frame, self.show_filter_menu); - - // // Get only the enabled filters. - // let mut enabled_filters = self.filters.clone(); - // enabled_filters.retain(|f| f.1); - // let enabled_filters = enabled_filters - // .iter() - // .map(|f| f.0.clone()) - // .collect::>(); - - // // Render console, we need the number of wrapping lines for scroll. - // self.num_lines_wrapping = layout.render_console( - // frame, - // self.scroll_position, - // &self.messages, - // &enabled_filters, - // ); - - // // // Render a border for the header - // // frame.render_widget(Block::default().borders(Borders::BOTTOM), body[0]); - - // // Render the metadata - // let mut spans: Vec = vec![ - // Span::from(if self.is_cli_release { "dx" } else { "dx-dev" }).green(), - // Span::from(" ").green(), - // Span::from("serve").green(), - // Span::from(" | ").white(), - // Span::from(self.platform.to_string()).green(), - // Span::from(" | ").white(), - // ]; - - // // If there is build progress, display that next to the platform - // if !self.build_progress.current_builds.is_empty() { - // if self - // .build_progress - // .current_builds - // .values() - // .any(|b| b.failed.is_some()) - // { - // spans.push(Span::from("build failed ❌").red()); - // } else { - // spans.push(Span::from("status: ").green()); - // let build = self - // .build_progress - // .current_builds - // .values() - // .min_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)) - // .unwrap(); - - // spans.extend_from_slice(&build.spans(Rect::new( - // 0, - // 0, - // build.max_layout_size(), - // 1, - // ))); - // } - // } - - // frame.render_widget(Paragraph::new(Line::from(spans)).left_aligned(), header[0]); - - // // Split apart the body into a center and a right side - // // We only want to show the sidebar if there's enough space - // if listening_len > 0 { - // frame.render_widget( - // Paragraph::new(Line::from(vec![ - // Span::from("listening at ").dark_gray(), - // Span::from(format!("http://{}", server.ip).as_str()).gray(), - // ])), - // header[1], - // ) - // } - - // if self.show_filter_menu { - // layout.render_filter_menu( - // frame, - // &self.filters, - // self.selected_filter_index, - // self.filter_search_mode, - // self.filter_search_input.as_ref(), - // ); - // } - - // layout.render_status_bar( - // frame, - // self.platform, - // &self.build_progress, - // self.more_modal_open, - // self.show_filter_menu, - // &self._dx_version, - // ); - - // if self.more_modal_open { - // layout.render_more_modal(frame); - // } - - // layout.render_current_scroll( - // self.scroll_position, - // self.num_lines_wrapping, - // self.console_height, - // frame, - // ); - // }); - // } - - // fn render_fly_modal(&mut self, frame: &mut Frame, area: Rect) { - // if !self.fly_modal_open { - // return; - // } - - // // Create a frame slightly smaller than the area - // let panel = Layout::default() - // .direction(Direction::Vertical) - // .constraints([Constraint::Fill(1)].as_ref()) - // .split(area)[0]; - - // // Wipe the panel - // frame.render_widget(Clear, panel); - // frame.render_widget(Block::default().borders(Borders::ALL), panel); - - // let modal = Paragraph::new("Under construction, please check back at a later date!\n") - // .alignment(Alignment::Center); - // frame.render_widget(modal, panel); - // } - - // fn push_log(&mut self, platform: impl Into, message: BuildMessage) { - // let source = platform.into(); - // let snapped = self.is_snapped(); - - // match source { - // LogSource::Internal => self.build_progress.internal_logs.push(message), - // LogSource::Target(platform) => self - // .build_progress - // .current_builds - // .entry(platform) - // .or_default() - // .stdout_logs - // .push(message), - // } - - // if snapped { - // self.scroll_to_bottom(); - // } - // } - - // // todo: re-enable - // #[allow(unused)] - // fn is_snapped(&self) -> bool { - // true - // } - - async fn handle_events(&mut self, event: Event) -> io::Result { - let mut events = vec![event]; - - // Collect all the events within the next 10ms in one stream - let collect_events = async { - loop { - let Some(Ok(next)) = self.events.as_mut().unwrap().next().await else { - break; - }; - events.push(next); - } - }; - tokio::select! { - _ = collect_events => {}, - _ = tokio::time::sleep(Duration::from_millis(10)) => {} - } - - // Debounce events within the same frame - let mut handled = HashSet::new(); - for event in events { - if !handled.contains(&event) { - if self.handle_input(event.clone())? { - // Restart the running app. - return Ok(true); - } - handled.insert(event); - } - } - - Ok(false) - } +/// Our console has "special" messages that get better rendering. +/// +/// We want to display them differently since they have their own state and are rendered differently. +enum ConsoleMessage { + Log(TraceMsg), + OnngoingBuild { stage: Stage, progress: f64 }, + BuildReady, } #[derive(Default, Debug, PartialEq)] diff --git a/packages/cli/src/serve/output/render.rs b/packages/cli/src/serve/output/render.rs index fdaac0adc0..4158ee1a57 100644 --- a/packages/cli/src/serve/output/render.rs +++ b/packages/cli/src/serve/output/render.rs @@ -1,8 +1,9 @@ -use super::{BuildProgress, TraceMsg, TraceSrc}; +use super::{BuildProgress, ConsoleMessage, TraceMsg, TraceSrc}; use crate::Platform; use ansi_to_tui::IntoText as _; use ratatui::{ layout::{Alignment, Constraint, Direction, Layout, Rect}, + prelude::Buffer, style::{Color, Style, Stylize}, text::{Line, Span, Text}, widgets::{Block, Borders, Clear, List, ListState, Paragraph, Widget, Wrap}, @@ -117,7 +118,7 @@ impl TuiLayout { &self, frame: &mut Frame, scroll_position: u16, - messages: &[TraceMsg], + messages: &[ConsoleMessage], enabled_filters: &[String], ) -> u16 { const LEVEL_MAX: usize = "BUILD: ".len(); @@ -128,75 +129,82 @@ impl TuiLayout { for msg in messages.iter() { let mut sub_line_padding = 0; - let text = msg.content.trim_end().into_text().unwrap_or_default(); - - for (idx, line) in text.lines.into_iter().enumerate() { - // Don't add any formatting for cargo messages. - let out_line = if msg.source != TraceSrc::Cargo { - if idx == 0 { - match msg.source { - TraceSrc::Dev => { - let mut spans = vec![Span::from(" DEV: ").light_magenta()]; - - for span in line.spans { - spans.push(span); + match msg { + ConsoleMessage::BuildReady => {} + ConsoleMessage::OnngoingBuild { stage, progress } => {} + + ConsoleMessage::Log(msg) => { + let text = msg.content.trim_end().into_text().unwrap_or_default(); + + for (idx, line) in text.lines.into_iter().enumerate() { + // Don't add any formatting for cargo messages. + let out_line = if msg.source != TraceSrc::Cargo { + if idx == 0 { + match msg.source { + TraceSrc::Dev => { + let mut spans = vec![Span::from(" DEV: ").light_magenta()]; + + for span in line.spans { + spans.push(span); + } + spans + } + TraceSrc::Build => { + let mut spans = vec![Span::from("BUILD: ").light_blue()]; + + for span in line.spans { + spans.push(span); + } + spans + } + _ => { + // Build level tag: `INFO:`` + // We don't subtract 1 here for `:` because we still want at least 1 padding. + let padding = build_msg_padding( + LEVEL_MAX - msg.level.to_string().len() - 2, + ); + let level = format!("{padding}{}: ", msg.level); + sub_line_padding += level.len(); + + let level_span = Span::from(level); + let level_span = match msg.level { + Level::TRACE => level_span.black(), + Level::DEBUG => level_span.light_magenta(), + Level::INFO => level_span.light_green(), + Level::WARN => level_span.light_yellow(), + Level::ERROR => level_span.light_red(), + }; + + let mut out_line = vec![level_span]; + for span in line.spans { + out_line.push(span); + } + + out_line + } } - spans - } - TraceSrc::Build => { - let mut spans = vec![Span::from("BUILD: ").light_blue()]; + } else { + // Not the first line. Append the padding and merge into list. + let padding = build_msg_padding(sub_line_padding); - for span in line.spans { - spans.push(span); - } - spans - } - _ => { - // Build level tag: `INFO:`` - // We don't subtract 1 here for `:` because we still want at least 1 padding. - let padding = - build_msg_padding(LEVEL_MAX - msg.level.to_string().len() - 2); - let level = format!("{padding}{}: ", msg.level); - sub_line_padding += level.len(); - - let level_span = Span::from(level); - let level_span = match msg.level { - Level::TRACE => level_span.black(), - Level::DEBUG => level_span.light_magenta(), - Level::INFO => level_span.light_green(), - Level::WARN => level_span.light_yellow(), - Level::ERROR => level_span.light_red(), - }; - - let mut out_line = vec![level_span]; + let mut out_line = vec![Span::from(padding)]; for span in line.spans { out_line.push(span); } - out_line } - } - } else { - // Not the first line. Append the padding and merge into list. - let padding = build_msg_padding(sub_line_padding); + } else { + line.spans + }; - let mut out_line = vec![Span::from(padding)]; - for span in line.spans { - out_line.push(span); - } - out_line + out_text.push_line(Line::from(out_line)); } - } else { - line.spans - }; - - out_text.push_line(Line::from(out_line)); + } } } // Only show messages for filters that are enabled. let mut included_line_ids = Vec::new(); - for filter in enabled_filters { let re = Regex::new(filter); for (index, line) in out_text.lines.iter().enumerate() { @@ -236,6 +244,9 @@ impl TuiLayout { } } + // out_text.push_line(line); + // let lines: Buffer = Paragraph::new(out_text).try_into().unwrap(); + let (console_width, _console_height) = self.get_console_size(); let paragraph = Paragraph::new(out_text) diff --git a/packages/desktop/Cargo.toml b/packages/desktop/Cargo.toml index a977ffd65d..46e2de6545 100644 --- a/packages/desktop/Cargo.toml +++ b/packages/desktop/Cargo.toml @@ -69,8 +69,6 @@ global-hotkey = { workspace = true } rfd = { workspace = true, default-features = false, features = ["xdg-portal", "tokio"] } muda = { workspace = true } - - [target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies] cocoa = { workspace = true } core-foundation = { workspace = true } diff --git a/packages/interpreter/src/js/common.js b/packages/interpreter/src/js/common.js index f0d690b40c..9b3af6cc8c 100644 --- a/packages/interpreter/src/js/common.js +++ b/packages/interpreter/src/js/common.js @@ -1 +1 @@ -function setAttributeInner(node,field,value,ns){if(ns==="style"){node.style.setProperty(field,value);return}if(ns){node.setAttributeNS(ns,field,value);return}switch(field){case"value":if(node.value!==value)node.value=value;break;case"initial_value":node.defaultValue=value;break;case"checked":node.checked=truthy(value);break;case"initial_checked":node.defaultChecked=truthy(value);break;case"selected":node.selected=truthy(value);break;case"initial_selected":node.defaultSelected=truthy(value);break;case"dangerous_inner_html":node.innerHTML=value;break;default:if(!truthy(value)&&isBoolAttr(field))node.removeAttribute(field);else node.setAttribute(field,value)}}function truthy(val){return val==="true"||val===!0}function isBoolAttr(field){switch(field){case"allowfullscreen":case"allowpaymentrequest":case"async":case"autofocus":case"autoplay":case"checked":case"controls":case"default":case"defer":case"disabled":case"formnovalidate":case"hidden":case"ismap":case"itemscope":case"loop":case"multiple":case"muted":case"nomodule":case"novalidate":case"open":case"playsinline":case"readonly":case"required":case"reversed":case"selected":case"truespeed":case"webkitdirectory":return!0;default:return!1}}function retrieveFormValues(form){const formData=new FormData(form),contents={};return formData.forEach((value,key)=>{if(contents[key])contents[key].push(value);else contents[key]=[value]}),{valid:form.checkValidity(),values:contents}}export{setAttributeInner,retrieveFormValues}; +function setAttributeInner(node,field,value,ns){if(ns==="style"){node.style.setProperty(field,value);return}if(ns){node.setAttributeNS(ns,field,value);return}switch(field){case"value":if(node.value!==value)node.value=value;break;case"initial_value":node.defaultValue=value;break;case"checked":node.checked=truthy(value);break;case"initial_checked":node.defaultChecked=truthy(value);break;case"selected":node.selected=truthy(value);break;case"initial_selected":node.defaultSelected=truthy(value);break;case"dangerous_inner_html":node.innerHTML=value;break;default:if(!truthy(value)&&isBoolAttr(field))node.removeAttribute(field);else node.setAttribute(field,value)}}var truthy=function(val){return val==="true"||val===!0},isBoolAttr=function(field){switch(field){case"allowfullscreen":case"allowpaymentrequest":case"async":case"autofocus":case"autoplay":case"checked":case"controls":case"default":case"defer":case"disabled":case"formnovalidate":case"hidden":case"ismap":case"itemscope":case"loop":case"multiple":case"muted":case"nomodule":case"novalidate":case"open":case"playsinline":case"readonly":case"required":case"reversed":case"selected":case"truespeed":case"webkitdirectory":return!0;default:return!1}};function retrieveFormValues(form){const formData=new FormData(form),contents={};return formData.forEach((value,key)=>{if(contents[key])contents[key].push(value);else contents[key]=[value]}),{valid:form.checkValidity(),values:contents}}export{setAttributeInner,retrieveFormValues}; diff --git a/packages/interpreter/src/js/core.js b/packages/interpreter/src/js/core.js index 377e0ed3fc..01bc2f8948 100644 --- a/packages/interpreter/src/js/core.js +++ b/packages/interpreter/src/js/core.js @@ -1 +1 @@ -function setAttributeInner(node,field,value,ns){if(ns==="style"){node.style.setProperty(field,value);return}if(ns){node.setAttributeNS(ns,field,value);return}switch(field){case"value":if(node.value!==value)node.value=value;break;case"initial_value":node.defaultValue=value;break;case"checked":node.checked=truthy(value);break;case"initial_checked":node.defaultChecked=truthy(value);break;case"selected":node.selected=truthy(value);break;case"initial_selected":node.defaultSelected=truthy(value);break;case"dangerous_inner_html":node.innerHTML=value;break;default:if(!truthy(value)&&isBoolAttr(field))node.removeAttribute(field);else node.setAttribute(field,value)}}function truthy(val){return val==="true"||val===!0}function isBoolAttr(field){switch(field){case"allowfullscreen":case"allowpaymentrequest":case"async":case"autofocus":case"autoplay":case"checked":case"controls":case"default":case"defer":case"disabled":case"formnovalidate":case"hidden":case"ismap":case"itemscope":case"loop":case"multiple":case"muted":case"nomodule":case"novalidate":case"open":case"playsinline":case"readonly":case"required":case"reversed":case"selected":case"truespeed":case"webkitdirectory":return!0;default:return!1}}class BaseInterpreter{global;local;root;handler;resizeObserver;nodes;stack;templates;m;constructor(){}initialize(root,handler=null){this.global={},this.local={},this.root=root,this.nodes=[root],this.stack=[root],this.templates={},this.handler=handler}handleResizeEvent(entry){const target=entry.target;let event=new CustomEvent("resize",{bubbles:!1,detail:entry});target.dispatchEvent(event)}createObserver(element){if(!this.resizeObserver)this.resizeObserver=new ResizeObserver((entries)=>{for(let entry of entries)this.handleResizeEvent(entry)});this.resizeObserver.observe(element)}removeObserver(element){if(this.resizeObserver)this.resizeObserver.unobserve(element)}createListener(event_name,element,bubbles){if(bubbles)if(this.global[event_name]===void 0)this.global[event_name]={active:1,callback:this.handler},this.root.addEventListener(event_name,this.handler);else this.global[event_name].active++;else{const id=element.getAttribute("data-dioxus-id");if(!this.local[id])this.local[id]={};element.addEventListener(event_name,this.handler)}if(event_name=="resize")this.createObserver(element)}removeListener(element,event_name,bubbles){if(bubbles)this.removeBubblingListener(event_name);else this.removeNonBubblingListener(element,event_name)}removeBubblingListener(event_name){if(this.global[event_name].active--,this.global[event_name].active===0)this.root.removeEventListener(event_name,this.global[event_name].callback),delete this.global[event_name]}removeNonBubblingListener(element,event_name){const id=element.getAttribute("data-dioxus-id");if(delete this.local[id][event_name],Object.keys(this.local[id]).length===0)delete this.local[id];element.removeEventListener(event_name,this.handler)}removeAllNonBubblingListeners(element){const id=element.getAttribute("data-dioxus-id");delete this.local[id]}getNode(id){return this.nodes[id]}pushRoot(node){this.stack.push(node)}appendChildren(id,many){const root=this.nodes[id],els=this.stack.splice(this.stack.length-many);for(let k=0;k0;end--)node=node.nextSibling}return node}saveTemplate(nodes,tmpl_id){this.templates[tmpl_id]=nodes}hydrate_node(hydrateNode,ids){const split=hydrateNode.getAttribute("data-node-hydration").split(","),id=ids[parseInt(split[0])];if(this.nodes[id]=hydrateNode,split.length>1){hydrateNode.listening=split.length-1,hydrateNode.setAttribute("data-dioxus-id",id.toString());for(let j=1;j1){if(this.nodes[ids[parseInt(placeholderSplit[1])]]=currentNode,!treeWalker.nextNode())break;continue}const textNodeSplit=id.split("node-id");if(textNodeSplit.length>1){let next=currentNode.nextSibling;currentNode.remove();let commentAfterText,textNode;if(next.nodeType===Node.COMMENT_NODE){const newText=next.parentElement.insertBefore(document.createTextNode(""),next);commentAfterText=next,textNode=newText}else textNode=next,commentAfterText=textNode.nextSibling;treeWalker.currentNode=commentAfterText,this.nodes[ids[parseInt(textNodeSplit[1])]]=textNode;let exit=!treeWalker.nextNode();if(commentAfterText.remove(),exit)break;continue}}if(!treeWalker.nextNode())break}}}setAttributeInner(node,field,value,ns){setAttributeInner(node,field,value,ns)}}export{BaseInterpreter}; +function setAttributeInner(node,field,value,ns){if(ns==="style"){node.style.setProperty(field,value);return}if(ns){node.setAttributeNS(ns,field,value);return}switch(field){case"value":if(node.value!==value)node.value=value;break;case"initial_value":node.defaultValue=value;break;case"checked":node.checked=truthy(value);break;case"initial_checked":node.defaultChecked=truthy(value);break;case"selected":node.selected=truthy(value);break;case"initial_selected":node.defaultSelected=truthy(value);break;case"dangerous_inner_html":node.innerHTML=value;break;default:if(!truthy(value)&&isBoolAttr(field))node.removeAttribute(field);else node.setAttribute(field,value)}}var truthy=function(val){return val==="true"||val===!0},isBoolAttr=function(field){switch(field){case"allowfullscreen":case"allowpaymentrequest":case"async":case"autofocus":case"autoplay":case"checked":case"controls":case"default":case"defer":case"disabled":case"formnovalidate":case"hidden":case"ismap":case"itemscope":case"loop":case"multiple":case"muted":case"nomodule":case"novalidate":case"open":case"playsinline":case"readonly":case"required":case"reversed":case"selected":case"truespeed":case"webkitdirectory":return!0;default:return!1}};class BaseInterpreter{global;local;root;handler;resizeObserver;nodes;stack;templates;m;constructor(){}initialize(root,handler=null){this.global={},this.local={},this.root=root,this.nodes=[root],this.stack=[root],this.templates={},this.handler=handler}handleResizeEvent(entry){const target=entry.target;let event=new CustomEvent("resize",{bubbles:!1,detail:entry});target.dispatchEvent(event)}createObserver(element){if(!this.resizeObserver)this.resizeObserver=new ResizeObserver((entries)=>{for(let entry of entries)this.handleResizeEvent(entry)});this.resizeObserver.observe(element)}removeObserver(element){if(this.resizeObserver)this.resizeObserver.unobserve(element)}createListener(event_name,element,bubbles){if(bubbles)if(this.global[event_name]===void 0)this.global[event_name]={active:1,callback:this.handler},this.root.addEventListener(event_name,this.handler);else this.global[event_name].active++;else{const id=element.getAttribute("data-dioxus-id");if(!this.local[id])this.local[id]={};element.addEventListener(event_name,this.handler)}if(event_name=="resize")this.createObserver(element)}removeListener(element,event_name,bubbles){if(bubbles)this.removeBubblingListener(event_name);else this.removeNonBubblingListener(element,event_name)}removeBubblingListener(event_name){if(this.global[event_name].active--,this.global[event_name].active===0)this.root.removeEventListener(event_name,this.global[event_name].callback),delete this.global[event_name]}removeNonBubblingListener(element,event_name){const id=element.getAttribute("data-dioxus-id");if(delete this.local[id][event_name],Object.keys(this.local[id]).length===0)delete this.local[id];element.removeEventListener(event_name,this.handler)}removeAllNonBubblingListeners(element){const id=element.getAttribute("data-dioxus-id");delete this.local[id]}getNode(id){return this.nodes[id]}pushRoot(node){this.stack.push(node)}appendChildren(id,many){const root=this.nodes[id],els=this.stack.splice(this.stack.length-many);for(let k=0;k0;end--)node=node.nextSibling}return node}saveTemplate(nodes,tmpl_id){this.templates[tmpl_id]=nodes}hydrate_node(hydrateNode,ids){const split=hydrateNode.getAttribute("data-node-hydration").split(","),id=ids[parseInt(split[0])];if(this.nodes[id]=hydrateNode,split.length>1){hydrateNode.listening=split.length-1,hydrateNode.setAttribute("data-dioxus-id",id.toString());for(let j=1;j1){if(this.nodes[ids[parseInt(placeholderSplit[1])]]=currentNode,!treeWalker.nextNode())break;continue}const textNodeSplit=id.split("node-id");if(textNodeSplit.length>1){let next=currentNode.nextSibling;currentNode.remove();let commentAfterText,textNode;if(next.nodeType===Node.COMMENT_NODE){const newText=next.parentElement.insertBefore(document.createTextNode(""),next);commentAfterText=next,textNode=newText}else textNode=next,commentAfterText=textNode.nextSibling;treeWalker.currentNode=commentAfterText,this.nodes[ids[parseInt(textNodeSplit[1])]]=textNode;let exit=!treeWalker.nextNode();if(commentAfterText.remove(),exit)break;continue}}if(!treeWalker.nextNode())break}}}setAttributeInner(node,field,value,ns){setAttributeInner(node,field,value,ns)}}export{BaseInterpreter}; diff --git a/packages/interpreter/src/js/hash.txt b/packages/interpreter/src/js/hash.txt index 391d8d66fa..fe5b307750 100644 --- a/packages/interpreter/src/js/hash.txt +++ b/packages/interpreter/src/js/hash.txt @@ -1 +1 @@ -[6449103750905854967, 4461869229701639737, 13069001215487072322, 8716623267269178440, 5336385715226370016, 14456089431355876478, 640348513295264026, 5052021921702764563, 17534315583914394253, 5638004933879392817] \ No newline at end of file +[6449103750905854967, 4461869229701639737, 13069001215487072322, 8716623267269178440, 5336385715226370016, 14456089431355876478, 3201860079408422767, 5052021921702764563, 17534315583914394253, 5638004933879392817] \ No newline at end of file diff --git a/packages/interpreter/src/js/native.js b/packages/interpreter/src/js/native.js index e69de29bb2..ff50e65fa8 100644 --- a/packages/interpreter/src/js/native.js +++ b/packages/interpreter/src/js/native.js @@ -0,0 +1 @@ +function retrieveValues(event,target){let contents={values:{}},form=target.closest("form");if(form){if(event.type==="input"||event.type==="change"||event.type==="submit"||event.type==="reset"||event.type==="click")contents=retrieveFormValues(form)}return contents}function retrieveFormValues(form){const formData=new FormData(form),contents={};return formData.forEach((value,key)=>{if(contents[key])contents[key].push(value);else contents[key]=[value]}),{valid:form.checkValidity(),values:contents}}function retrieveSelectValue(target){let options=target.selectedOptions,values=[];for(let i=0;icontents={...contents,...obj};if(event instanceof WheelEvent)extend(serializeWheelEvent(event));if(event instanceof MouseEvent)extend(serializeMouseEvent(event));if(event instanceof KeyboardEvent)extend(serializeKeyboardEvent(event));if(event instanceof InputEvent)extend(serializeInputEvent(event,target));if(event instanceof PointerEvent)extend(serializePointerEvent(event));if(event instanceof AnimationEvent)extend(serializeAnimationEvent(event));if(event instanceof TransitionEvent)extend({property_name:event.propertyName,elapsed_time:event.elapsedTime,pseudo_element:event.pseudoElement});if(event instanceof CompositionEvent)extend({data:event.data});if(event instanceof DragEvent)extend(serializeDragEvent(event));if(event instanceof FocusEvent)extend({});if(event instanceof ClipboardEvent)extend({});if(event instanceof CustomEvent){const detail=event.detail;if(detail instanceof ResizeObserverEntry)extend(serializeResizeEventDetail(detail))}if(typeof TouchEvent!=="undefined"&&event instanceof TouchEvent)extend(serializeTouchEvent(event));if(event.type==="submit"||event.type==="reset"||event.type==="click"||event.type==="change"||event.type==="input")extend(serializeInputEvent(event,target));if(event instanceof DragEvent);return contents}var toSerializableResizeObserverSize=function(size,is_inline_width){return[is_inline_width?size.inlineSize:size.blockSize,is_inline_width?size.blockSize:size.inlineSize]};function serializeResizeEventDetail(detail){let is_inline_width=!0;if(detail.target instanceof HTMLElement){if(window.getComputedStyle(detail.target).getPropertyValue("writing-mode")!=="horizontal-tb")is_inline_width=!1}return{border_box_size:detail.borderBoxSize!==void 0?toSerializableResizeObserverSize(detail.borderBoxSize[0],is_inline_width):detail.contentRect,content_box_size:detail.contentBoxSize!==void 0?toSerializableResizeObserverSize(detail.contentBoxSize[0],is_inline_width):detail.contentRect,content_rect:detail.contentRect}}var serializeInputEvent=function(event,target){let contents={};if(target instanceof HTMLElement){let values=retrieveValues(event,target);contents.values=values.values,contents.valid=values.valid}if(event.target instanceof HTMLInputElement){let target2=event.target,value=target2.value??target2.textContent??"";if(target2.type==="checkbox")value=target2.checked?"true":"false";else if(target2.type==="radio")value=target2.value;contents.value=value}if(event.target instanceof HTMLTextAreaElement)contents.value=event.target.value;if(event.target instanceof HTMLSelectElement)contents.value=retrieveSelectValue(event.target).join(",");if(contents.value===void 0)contents.value="";return contents},serializeWheelEvent=function(event){return{delta_x:event.deltaX,delta_y:event.deltaY,delta_z:event.deltaZ,delta_mode:event.deltaMode}},serializeTouchEvent=function(event){return{alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,shift_key:event.shiftKey,changed_touches:event.changedTouches,target_touches:event.targetTouches,touches:event.touches}},serializePointerEvent=function(event){return{alt_key:event.altKey,button:event.button,buttons:event.buttons,client_x:event.clientX,client_y:event.clientY,ctrl_key:event.ctrlKey,meta_key:event.metaKey,page_x:event.pageX,page_y:event.pageY,screen_x:event.screenX,screen_y:event.screenY,shift_key:event.shiftKey,pointer_id:event.pointerId,width:event.width,height:event.height,pressure:event.pressure,tangential_pressure:event.tangentialPressure,tilt_x:event.tiltX,tilt_y:event.tiltY,twist:event.twist,pointer_type:event.pointerType,is_primary:event.isPrimary}},serializeMouseEvent=function(event){return{alt_key:event.altKey,button:event.button,buttons:event.buttons,client_x:event.clientX,client_y:event.clientY,ctrl_key:event.ctrlKey,meta_key:event.metaKey,offset_x:event.offsetX,offset_y:event.offsetY,page_x:event.pageX,page_y:event.pageY,screen_x:event.screenX,screen_y:event.screenY,shift_key:event.shiftKey}},serializeKeyboardEvent=function(event){return{char_code:event.charCode,is_composing:event.isComposing,key:event.key,alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,key_code:event.keyCode,shift_key:event.shiftKey,location:event.location,repeat:event.repeat,which:event.which,code:event.code}},serializeAnimationEvent=function(event){return{animation_name:event.animationName,elapsed_time:event.elapsedTime,pseudo_element:event.pseudoElement}},serializeDragEvent=function(event){let files=void 0;if(event.dataTransfer&&event.dataTransfer.files&&event.dataTransfer.files.length>0)files={files:{placeholder:[]}};return{mouse:{alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,shift_key:event.shiftKey,...serializeMouseEvent(event)},files}};var handleVirtualdomEventSync=function(endpoint,contents){const xhr=new XMLHttpRequest;return xhr.open("POST",endpoint,!1),xhr.setRequestHeader("Content-Type","application/json"),xhr.send(contents),JSON.parse(xhr.responseText)},getTargetId=function(target){if(!(target instanceof Node))return null;let ourTarget=target,realId=null;while(realId==null){if(ourTarget===null)return null;if(ourTarget instanceof Element)realId=ourTarget.getAttribute("data-dioxus-id");ourTarget=ourTarget.parentNode}return parseInt(realId)},JSChannel_;if(RawInterpreter!==void 0&&RawInterpreter!==null)JSChannel_=RawInterpreter;class NativeInterpreter extends JSChannel_{intercept_link_redirects;ipc;editsPath;eventsPath;kickStylesheets;queuedBytes=[];liveview;constructor(editsPath,eventsPath){super();this.editsPath=editsPath,this.eventsPath=eventsPath,this.kickStylesheets=!1}initialize(root){this.intercept_link_redirects=!0,this.liveview=!1,window.addEventListener("dragover",function(e){if(e.target instanceof Element&&e.target.tagName!="INPUT")e.preventDefault()},!1),window.addEventListener("drop",function(e){if(!(e.target instanceof Element))return;e.preventDefault()},!1),window.addEventListener("click",(event)=>{const target=event.target;if(target instanceof HTMLInputElement&&target.getAttribute("type")==="file"){let target_id=getTargetId(target);if(target_id!==null){const message=this.serializeIpcMessage("file_dialog",{event:"change&input",accept:target.getAttribute("accept"),directory:target.getAttribute("webkitdirectory")==="true",multiple:target.hasAttribute("multiple"),target:target_id,bubbles:event.bubbles});this.ipc.postMessage(message),event.preventDefault()}}}),this.ipc=window.ipc;const handler=(event)=>this.handleEvent(event,event.type,!0);super.initialize(root,handler)}serializeIpcMessage(method,params={}){return JSON.stringify({method,params})}scrollTo(id,behavior){const node=this.nodes[id];if(node instanceof HTMLElement)node.scrollIntoView({behavior})}getScrollHeight(id){const node=this.nodes[id];if(node instanceof HTMLElement)return node.scrollHeight}getScrollLeft(id){const node=this.nodes[id];if(node instanceof HTMLElement)return node.scrollLeft}getScrollTop(id){const node=this.nodes[id];if(node instanceof HTMLElement)return node.scrollTop}getScrollWidth(id){const node=this.nodes[id];if(node instanceof HTMLElement)return node.scrollWidth}getClientRect(id){const node=this.nodes[id];if(node instanceof HTMLElement){const rect=node.getBoundingClientRect();return{type:"GetClientRect",origin:[rect.x,rect.y],size:[rect.width,rect.height]}}}setFocus(id,focus){const node=this.nodes[id];if(node instanceof HTMLElement)if(focus)node.focus();else node.blur()}loadChild(array){let node=this.stack[this.stack.length-1];for(let i=0;i0;end--)node=node.nextSibling}return node}appendChildren(id,many){const root=this.nodes[id],els=this.stack.splice(this.stack.length-many);for(let k=0;k{this.flushQueuedBytes(),this.waitForRequest(headless)})}waitForRequest(headless){fetch(new Request(this.editsPath)).then((response)=>response.arrayBuffer()).then((bytes)=>{this.rafEdits(headless,bytes)})}kickAllStylesheetsOnPage(){let stylesheets=document.querySelectorAll("link[rel=stylesheet]");for(let i=0;i{let entropy_string=Math.random().toString().substring(0,10),[href]=sheet.href.split("?entropy=");sheet.href=href+"?entropy="+entropy_string})}this.kickCachedLinks()}kickCachedLinks(){Object.values(this.templates).forEach((template)=>{template.forEach((node)=>{let walker=document.createTreeWalker(node,NodeFilter.SHOW_ELEMENT);while(walker.nextNode()){let element=walker.currentNode;if(element.tagName==="LINK"){let attr=element.getAttribute("href");if(attr){let entropy_string=Math.random().toString().substring(0,10),[href]=attr.split("?entropy=");element.setAttribute("href",href+"?entropy="+entropy_string)}}}})})}async readFiles(target,contents,bubbles,realId,name){let files=target.files,file_contents={};for(let i=0;i Date: Sun, 15 Sep 2024 18:03:40 -0700 Subject: [PATCH 106/139] drop example-projects change --- example-projects/auth/.gitignore | 4 - example-projects/auth/Cargo.toml | 47 - example-projects/auth/src/auth.rs | 262 ------ example-projects/auth/src/main.rs | 155 ---- example-projects/desktop/.gitignore | 4 - example-projects/desktop/Cargo.toml | 14 - example-projects/desktop/src/main.rs | 43 - example-projects/fullstack-mobile/.gitignore | 2 - example-projects/fullstack-mobile/Cargo.toml | 33 - .../assets/dogs/cute/dog0.jpg | Bin 39179 -> 0 bytes .../assets/dogs/cute/dog1.jpg | Bin 32904 -> 0 bytes .../assets/dogs/cute/dog2.jpg | Bin 46631 -> 0 bytes .../assets/dogs/cute/dog3.jpg | Bin 25476 -> 0 bytes .../assets/dogs/cute/dog4.jpg | Bin 38016 -> 0 bytes .../assets/dogs/cute/dog5.jpg | Bin 41820 -> 0 bytes .../assets/dogs/fluffy/dog0.jpg | Bin 7483 -> 0 bytes .../assets/dogs/fluffy/dog1.jpg | Bin 19694 -> 0 bytes .../assets/dogs/fluffy/dog2.jpg | Bin 39094 -> 0 bytes .../assets/dogs/fluffy/dog3.jpg | Bin 16840 -> 0 bytes .../assets/dogs/fluffy/dog4.jpg | Bin 30428 -> 0 bytes .../assets/dogs/fluffy/dog5.jpg | Bin 419636 -> 0 bytes .../fullstack-mobile/assets/favicon.ico | Bin 9662 -> 0 bytes .../fullstack-mobile/assets/header.svg | 20 - .../fullstack-mobile/assets/style.css | 29 - .../fullstack-mobile/assets/tailwind.css | 664 -------------- .../fullstack-mobile/makefile.toml | 45 - example-projects/fullstack-mobile/src/main.rs | 68 -- .../fullstack-mobile/tailwind.config.js | 42 - .../fullstack-mobile/tailwind.css | 3 - .../tools/make-entitlements.sh | 41 - .../fullstack-mobile/tools/run-ios-device.sh | 34 - example-projects/hackernews/.gitignore | 2 - example-projects/hackernews/Cargo.toml | 22 - .../hackernews/assets/hackernews.css | 17 - example-projects/hackernews/src/main.rs | 306 ------- example-projects/hello-world/.gitignore | 4 - example-projects/hello-world/Cargo.toml | 21 - example-projects/hello-world/src/main.rs | 54 -- example-projects/liveview-router/.gitignore | 4 - example-projects/liveview-router/Cargo.toml | 15 - example-projects/liveview-router/src/main.rs | 190 ---- .../manganis-test-package/Cargo.toml | 11 - .../manganis-test-package/assets/asset.txt | 0 .../manganis-test-package/assets/data.json | 6 - .../manganis-test-package/assets/script.js | 3 - .../manganis-test-package/assets/test.mp4 | 0 .../manganis-test-package/src/main.rs | 41 - .../test-package-dependency/Cargo.toml | 11 - .../test-package-dependency/src/asset.txt | 1 - .../test-package-dependency/src/file.rs | 7 - .../test-package-dependency/src/lib.rs | 8 - .../test-package-nested-dependency/Cargo.toml | 11 - .../all_the_assets/data.json | 6 - .../all_the_assets/rustacean-flat-gesture.png | Bin 49537 -> 0 bytes .../all_the_assets/script.js | 7 - .../all_the_assets/style.css | 8 - .../src/file.rs | 31 - .../src/folder.rs | 3 - .../src/font.rs | 16 - .../test-package-nested-dependency/src/js.rs | 4 - .../test-package-nested-dependency/src/lib.rs | 16 - example-projects/pwa/Cargo.toml | 17 - example-projects/pwa/Dioxus.toml | 27 - example-projects/pwa/LICENSE | 21 - example-projects/pwa/README.md | 45 - example-projects/pwa/index.html | 30 - example-projects/pwa/public/favicon.ico | Bin 23104 -> 0 bytes example-projects/pwa/public/logo_192.png | Bin 11198 -> 0 bytes example-projects/pwa/public/logo_512.png | Bin 48151 -> 0 bytes example-projects/pwa/public/manifest.json | 34 - example-projects/pwa/public/sw.js | 198 ----- example-projects/pwa/src/main.rs | 21 - example-projects/router/.gitignore | 4 - example-projects/router/Cargo.toml | 19 - example-projects/router/src/main.rs | 80 -- example-projects/streaming/.gitignore | 4 - example-projects/streaming/Cargo.toml | 24 - example-projects/streaming/src/main.rs | 41 - example-projects/tailwind/.gitignore | 1 - example-projects/tailwind/Cargo.toml | 20 - example-projects/tailwind/Dioxus.toml | 27 - example-projects/tailwind/README.md | 7 - example-projects/tailwind/input.css | 3 - example-projects/tailwind/public/tailwind.css | 833 ------------------ example-projects/tailwind/src/main.rs | 102 --- example-projects/tailwind/tailwind.config.js | 9 - 86 files changed, 3902 deletions(-) delete mode 100644 example-projects/auth/.gitignore delete mode 100644 example-projects/auth/Cargo.toml delete mode 100644 example-projects/auth/src/auth.rs delete mode 100644 example-projects/auth/src/main.rs delete mode 100644 example-projects/desktop/.gitignore delete mode 100644 example-projects/desktop/Cargo.toml delete mode 100644 example-projects/desktop/src/main.rs delete mode 100644 example-projects/fullstack-mobile/.gitignore delete mode 100644 example-projects/fullstack-mobile/Cargo.toml delete mode 100644 example-projects/fullstack-mobile/assets/dogs/cute/dog0.jpg delete mode 100644 example-projects/fullstack-mobile/assets/dogs/cute/dog1.jpg delete mode 100644 example-projects/fullstack-mobile/assets/dogs/cute/dog2.jpg delete mode 100644 example-projects/fullstack-mobile/assets/dogs/cute/dog3.jpg delete mode 100644 example-projects/fullstack-mobile/assets/dogs/cute/dog4.jpg delete mode 100644 example-projects/fullstack-mobile/assets/dogs/cute/dog5.jpg delete mode 100644 example-projects/fullstack-mobile/assets/dogs/fluffy/dog0.jpg delete mode 100644 example-projects/fullstack-mobile/assets/dogs/fluffy/dog1.jpg delete mode 100644 example-projects/fullstack-mobile/assets/dogs/fluffy/dog2.jpg delete mode 100644 example-projects/fullstack-mobile/assets/dogs/fluffy/dog3.jpg delete mode 100644 example-projects/fullstack-mobile/assets/dogs/fluffy/dog4.jpg delete mode 100644 example-projects/fullstack-mobile/assets/dogs/fluffy/dog5.jpg delete mode 100644 example-projects/fullstack-mobile/assets/favicon.ico delete mode 100644 example-projects/fullstack-mobile/assets/header.svg delete mode 100644 example-projects/fullstack-mobile/assets/style.css delete mode 100644 example-projects/fullstack-mobile/assets/tailwind.css delete mode 100644 example-projects/fullstack-mobile/makefile.toml delete mode 100644 example-projects/fullstack-mobile/src/main.rs delete mode 100644 example-projects/fullstack-mobile/tailwind.config.js delete mode 100644 example-projects/fullstack-mobile/tailwind.css delete mode 100755 example-projects/fullstack-mobile/tools/make-entitlements.sh delete mode 100755 example-projects/fullstack-mobile/tools/run-ios-device.sh delete mode 100644 example-projects/hackernews/.gitignore delete mode 100644 example-projects/hackernews/Cargo.toml delete mode 100644 example-projects/hackernews/assets/hackernews.css delete mode 100644 example-projects/hackernews/src/main.rs delete mode 100644 example-projects/hello-world/.gitignore delete mode 100644 example-projects/hello-world/Cargo.toml delete mode 100644 example-projects/hello-world/src/main.rs delete mode 100644 example-projects/liveview-router/.gitignore delete mode 100644 example-projects/liveview-router/Cargo.toml delete mode 100644 example-projects/liveview-router/src/main.rs delete mode 100644 example-projects/manganis-test-package/Cargo.toml delete mode 100644 example-projects/manganis-test-package/assets/asset.txt delete mode 100644 example-projects/manganis-test-package/assets/data.json delete mode 100644 example-projects/manganis-test-package/assets/script.js delete mode 100644 example-projects/manganis-test-package/assets/test.mp4 delete mode 100644 example-projects/manganis-test-package/src/main.rs delete mode 100644 example-projects/manganis-test-package/test-package-dependency/Cargo.toml delete mode 100644 example-projects/manganis-test-package/test-package-dependency/src/asset.txt delete mode 100644 example-projects/manganis-test-package/test-package-dependency/src/file.rs delete mode 100644 example-projects/manganis-test-package/test-package-dependency/src/lib.rs delete mode 100644 example-projects/manganis-test-package/test-package-nested-dependency/Cargo.toml delete mode 100644 example-projects/manganis-test-package/test-package-nested-dependency/all_the_assets/data.json delete mode 100644 example-projects/manganis-test-package/test-package-nested-dependency/all_the_assets/rustacean-flat-gesture.png delete mode 100644 example-projects/manganis-test-package/test-package-nested-dependency/all_the_assets/script.js delete mode 100644 example-projects/manganis-test-package/test-package-nested-dependency/all_the_assets/style.css delete mode 100644 example-projects/manganis-test-package/test-package-nested-dependency/src/file.rs delete mode 100644 example-projects/manganis-test-package/test-package-nested-dependency/src/folder.rs delete mode 100644 example-projects/manganis-test-package/test-package-nested-dependency/src/font.rs delete mode 100644 example-projects/manganis-test-package/test-package-nested-dependency/src/js.rs delete mode 100644 example-projects/manganis-test-package/test-package-nested-dependency/src/lib.rs delete mode 100644 example-projects/pwa/Cargo.toml delete mode 100644 example-projects/pwa/Dioxus.toml delete mode 100644 example-projects/pwa/LICENSE delete mode 100644 example-projects/pwa/README.md delete mode 100644 example-projects/pwa/index.html delete mode 100644 example-projects/pwa/public/favicon.ico delete mode 100644 example-projects/pwa/public/logo_192.png delete mode 100644 example-projects/pwa/public/logo_512.png delete mode 100644 example-projects/pwa/public/manifest.json delete mode 100644 example-projects/pwa/public/sw.js delete mode 100644 example-projects/pwa/src/main.rs delete mode 100644 example-projects/router/.gitignore delete mode 100644 example-projects/router/Cargo.toml delete mode 100644 example-projects/router/src/main.rs delete mode 100644 example-projects/streaming/.gitignore delete mode 100644 example-projects/streaming/Cargo.toml delete mode 100644 example-projects/streaming/src/main.rs delete mode 100644 example-projects/tailwind/.gitignore delete mode 100644 example-projects/tailwind/Cargo.toml delete mode 100644 example-projects/tailwind/Dioxus.toml delete mode 100644 example-projects/tailwind/README.md delete mode 100644 example-projects/tailwind/input.css delete mode 100644 example-projects/tailwind/public/tailwind.css delete mode 100644 example-projects/tailwind/src/main.rs delete mode 100644 example-projects/tailwind/tailwind.config.js diff --git a/example-projects/auth/.gitignore b/example-projects/auth/.gitignore deleted file mode 100644 index 21fff11dd3..0000000000 --- a/example-projects/auth/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -dist -target -static -.dioxus \ No newline at end of file diff --git a/example-projects/auth/Cargo.toml b/example-projects/auth/Cargo.toml deleted file mode 100644 index a42450dc5c..0000000000 --- a/example-projects/auth/Cargo.toml +++ /dev/null @@ -1,47 +0,0 @@ -[package] -name = "fullstack-auth-example" -version = "0.1.0" -edition = "2021" -publish = false - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -dioxus-web = { workspace = true, features = ["hydrate"], optional = true } -dioxus = { features = ["fullstack"], workspace = true } -dioxus-fullstack = { workspace = true } -axum = { workspace = true, optional = true } -tokio = { workspace = true, features = ["full"], optional = true } -tower-http = { workspace = true, features = ["auth"], optional = true } -simple_logger = { version = "4.2.0", optional = true } -async-trait = { version = "0.1.71", optional = true } -sqlx = { version = "0.7.0", features = [ - "macros", - "migrate", - "postgres", - "sqlite", - "_unstable-all-types", - "tls-native-tls", - "runtime-tokio", -], optional = true } -http = { workspace = true, optional = true } -tower = { workspace = true, optional = true } - -serde = "1.0.159" -execute = "0.2.12" -anyhow = "1.0.71" - -[dependencies.axum_session] -workspace = true -features = ["sqlite-rustls"] -optional = true - -[dependencies.axum_session_auth] -workspace = true -features = ["sqlite-rustls"] -optional = true - -[features] -default = [] -server = ["axum", "tokio", "dioxus-fullstack/axum", "tower-http", "simple_logger", "async-trait", "sqlx", "axum_session", "axum_session_auth", "http", "tower"] -web = ["dioxus-web"] diff --git a/example-projects/auth/src/auth.rs b/example-projects/auth/src/auth.rs deleted file mode 100644 index 9111ce121b..0000000000 --- a/example-projects/auth/src/auth.rs +++ /dev/null @@ -1,262 +0,0 @@ -use async_trait::async_trait; -use axum::{ - http::Method, - response::{IntoResponse, Response}, - routing::get, - Router, -}; -use axum_session::{SessionConfig, SessionLayer, SessionSqlitePool, SessionStore}; -use axum_session_auth::*; -use core::pin::Pin; -use dioxus_fullstack::prelude::*; -use serde::{Deserialize, Serialize}; -use sqlx::sqlite::{SqliteConnectOptions, SqlitePool, SqlitePoolOptions}; -use std::error::Error; -use std::future::Future; -use std::{collections::HashSet, net::SocketAddr, str::FromStr}; - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct User { - pub id: i32, - pub anonymous: bool, - pub username: String, - pub permissions: HashSet, -} - -#[derive(sqlx::FromRow, Clone)] -pub struct SqlPermissionTokens { - pub token: String, -} - -impl Default for User { - fn default() -> Self { - let mut permissions = HashSet::new(); - - permissions.insert("Category::View".to_owned()); - - Self { - id: 1, - anonymous: true, - username: "Guest".into(), - permissions, - } - } -} - -#[async_trait] -impl Authentication for User { - async fn load_user(userid: i64, pool: Option<&SqlitePool>) -> Result { - let pool = pool.unwrap(); - - User::get_user(userid, pool) - .await - .ok_or_else(|| anyhow::anyhow!("Could not load user")) - } - - fn is_authenticated(&self) -> bool { - !self.anonymous - } - - fn is_active(&self) -> bool { - !self.anonymous - } - - fn is_anonymous(&self) -> bool { - self.anonymous - } -} - -#[async_trait] -impl HasPermission for User { - async fn has(&self, perm: &str, _pool: &Option<&SqlitePool>) -> bool { - self.permissions.contains(perm) - } -} - -impl User { - pub async fn get_user(id: i64, pool: &SqlitePool) -> Option { - let sqluser = sqlx::query_as::<_, SqlUser>("SELECT * FROM users WHERE id = $1") - .bind(id) - .fetch_one(pool) - .await - .ok()?; - - //lets just get all the tokens the user can use, we will only use the full permissions if modifying them. - let sql_user_perms = sqlx::query_as::<_, SqlPermissionTokens>( - "SELECT token FROM user_permissions WHERE user_id = $1;", - ) - .bind(id) - .fetch_all(pool) - .await - .ok()?; - - Some(sqluser.into_user(Some(sql_user_perms))) - } - - pub async fn create_user_tables(pool: &SqlitePool) { - sqlx::query( - r#" - CREATE TABLE IF NOT EXISTS users ( - "id" INTEGER PRIMARY KEY, - "anonymous" BOOLEAN NOT NULL, - "username" VARCHAR(256) NOT NULL - ) - "#, - ) - .execute(pool) - .await - .unwrap(); - - sqlx::query( - r#" - CREATE TABLE IF NOT EXISTS user_permissions ( - "user_id" INTEGER NOT NULL, - "token" VARCHAR(256) NOT NULL - ) - "#, - ) - .execute(pool) - .await - .unwrap(); - - sqlx::query( - r#" - INSERT INTO users - (id, anonymous, username) SELECT 1, true, 'Guest' - ON CONFLICT(id) DO UPDATE SET - anonymous = EXCLUDED.anonymous, - username = EXCLUDED.username - "#, - ) - .execute(pool) - .await - .unwrap(); - - sqlx::query( - r#" - INSERT INTO users - (id, anonymous, username) SELECT 2, false, 'Test' - ON CONFLICT(id) DO UPDATE SET - anonymous = EXCLUDED.anonymous, - username = EXCLUDED.username - "#, - ) - .execute(pool) - .await - .unwrap(); - - sqlx::query( - r#" - INSERT INTO user_permissions - (user_id, token) SELECT 2, 'Category::View' - "#, - ) - .execute(pool) - .await - .unwrap(); - } -} - -#[derive(sqlx::FromRow, Clone)] -pub struct SqlUser { - pub id: i32, - pub anonymous: bool, - pub username: String, -} - -impl SqlUser { - pub fn into_user(self, sql_user_perms: Option>) -> User { - User { - id: self.id, - anonymous: self.anonymous, - username: self.username, - permissions: if let Some(user_perms) = sql_user_perms { - user_perms - .into_iter() - .map(|x| x.token) - .collect::>() - } else { - HashSet::::new() - }, - } - } -} - -pub async fn connect_to_database() -> SqlitePool { - let connect_opts = SqliteConnectOptions::from_str("sqlite::memory:").unwrap(); - - SqlitePoolOptions::new() - .max_connections(5) - .connect_with(connect_opts) - .await - .unwrap() -} - -pub struct Session( - pub axum_session_auth::AuthSession< - crate::auth::User, - i64, - axum_session_auth::SessionSqlitePool, - sqlx::SqlitePool, - >, -); - -impl std::ops::Deref for Session { - type Target = axum_session_auth::AuthSession< - crate::auth::User, - i64, - axum_session_auth::SessionSqlitePool, - sqlx::SqlitePool, - >; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl std::ops::DerefMut for Session { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -#[derive(Debug)] -pub struct AuthSessionLayerNotFound; - -impl std::fmt::Display for AuthSessionLayerNotFound { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "AuthSessionLayer was not found") - } -} - -impl std::error::Error for AuthSessionLayerNotFound {} - -impl IntoResponse for AuthSessionLayerNotFound { - fn into_response(self) -> Response { - ( - http::status::StatusCode::INTERNAL_SERVER_ERROR, - "AuthSessionLayer was not found", - ) - .into_response() - } -} - -#[async_trait] -impl axum::extract::FromRequestParts for Session { - type Rejection = AuthSessionLayerNotFound; - - async fn from_request_parts( - parts: &mut http::request::Parts, - state: &S, - ) -> Result { - axum_session_auth::AuthSession::< - crate::auth::User, - i64, - axum_session_auth::SessionSqlitePool, - sqlx::SqlitePool, - >::from_request_parts(parts, state) - .await - .map(Session) - .map_err(|_| AuthSessionLayerNotFound) - } -} diff --git a/example-projects/auth/src/main.rs b/example-projects/auth/src/main.rs deleted file mode 100644 index ec8097997b..0000000000 --- a/example-projects/auth/src/main.rs +++ /dev/null @@ -1,155 +0,0 @@ -//! Run with: -//! -//! ```sh -//! dx build --features web -//! cargo run --features server -//! ``` - -#![allow(non_snake_case, unused)] - -#[cfg(feature = "server")] -mod auth; - -use dioxus::prelude::*; -use dioxus_fullstack::prelude::*; -use serde::{Deserialize, Serialize}; - -fn main() { - #[cfg(feature = "web")] - // Hydrate the application on the client - dioxus_web::launch::launch_cfg(app, dioxus_web::Config::new().hydrate(true)); - - #[cfg(feature = "server")] - { - use crate::auth::*; - use axum::routing::*; - use axum_session::SessionConfig; - use axum_session::SessionStore; - use axum_session_auth::AuthConfig; - use axum_session_auth::SessionSqlitePool; - simple_logger::SimpleLogger::new().init().unwrap(); - tokio::runtime::Runtime::new() - .unwrap() - .block_on(async move { - let pool = connect_to_database().await; - - //This Defaults as normal Cookies. - //To enable Private cookies for integrity, and authenticity please check the next Example. - let session_config = SessionConfig::default().with_table_name("test_table"); - let auth_config = AuthConfig::::default().with_anonymous_user_id(Some(1)); - let session_store = SessionStore::::new( - Some(pool.clone().into()), - session_config, - ) - .await - .unwrap(); - - User::create_user_tables(&pool).await; - - // build our application with some routes - let app = Router::new() - // Server side render the application, serve static assets, and register server functions - .serve_dioxus_application(ServeConfig::new().unwrap(), app) - .layer( - axum_session_auth::AuthSessionLayer::< - crate::auth::User, - i64, - axum_session_auth::SessionSqlitePool, - sqlx::SqlitePool, - >::new(Some(pool)) - .with_config(auth_config), - ) - .layer(axum_session::SessionLayer::new(session_store)); - - // run it - let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 3000)); - let listener = tokio::net::TcpListener::bind(&addr).await.unwrap(); - - axum::serve(listener, app.into_make_service()) - .await - .unwrap(); - }); - } -} -// -fn app() -> Element { - let mut user_name = use_signal(|| "?".to_string()); - let mut permissions = use_signal(|| "?".to_string()); - - rsx! { - div { - button { onclick: move |_| { - async move { - login().await.unwrap(); - } - }, - "Login Test User" - } - } - div { - button { - onclick: move |_| async move { - if let Ok(data) = get_user_name().await { - user_name.set(data); - } - }, - "Get User Name" - } - "User name: {user_name}" - } - div { - button { - onclick: move |_| async move { - if let Ok(data) = get_permissions().await { - permissions.set(data); - } - }, - "Get Permissions" - } - "Permissions: {permissions}" - } - } -} - -#[server(GetUserName)] -pub async fn get_user_name() -> Result { - let session: crate::auth::Session = extract().await?; - Ok(session.0.current_user.unwrap().username.to_string()) -} - -#[server(Login)] -pub async fn login() -> Result<(), ServerFnError> { - let auth: crate::auth::Session = extract().await?; - auth.login_user(2); - Ok(()) -} - -#[server(Permissions)] -pub async fn get_permissions() -> Result { - let method: axum::http::Method = extract().await?; - let auth: crate::auth::Session = extract().await?; - let current_user = auth.current_user.clone().unwrap_or_default(); - - // lets check permissions only and not worry about if they are anon or not - if !axum_session_auth::Auth::::build( - [axum::http::Method::POST], - false, - ) - .requires(axum_session_auth::Rights::any([ - axum_session_auth::Rights::permission("Category::View"), - axum_session_auth::Rights::permission("Admin::View"), - ])) - .validate(¤t_user, &method, None) - .await - { - return Ok(format!( - "User {}, Does not have permissions needed to view this page please login", - current_user.username - )); - } - - Ok(format!( - "User has Permissions needed. Here are the Users permissions: {:?}", - current_user.permissions - )) -} diff --git a/example-projects/desktop/.gitignore b/example-projects/desktop/.gitignore deleted file mode 100644 index 21fff11dd3..0000000000 --- a/example-projects/desktop/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -dist -target -static -.dioxus \ No newline at end of file diff --git a/example-projects/desktop/Cargo.toml b/example-projects/desktop/Cargo.toml deleted file mode 100644 index 008a6a9f13..0000000000 --- a/example-projects/desktop/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "fullstack-desktop-example" -version = "0.1.0" -edition = "2021" -publish = false - -[dependencies] -dioxus = { workspace = true, features = ["launch", "fullstack"] } -serde = "1.0.159" - -[features] -default = [] -server = ["dioxus/server"] -desktop = ["dioxus/desktop"] diff --git a/example-projects/desktop/src/main.rs b/example-projects/desktop/src/main.rs deleted file mode 100644 index f56451f193..0000000000 --- a/example-projects/desktop/src/main.rs +++ /dev/null @@ -1,43 +0,0 @@ -#![allow(non_snake_case)] -use dioxus::prelude::*; - -fn main() { - // Set the url of the server where server functions are hosted. - #[cfg(not(feature = "server"))] - dioxus::fullstack::prelude::server_fn::client::set_server_url("http://127.0.0.1:8080"); - dioxus::launch(app); -} - -pub fn app() -> Element { - let mut count = use_signal(|| 0); - let mut text = use_signal(|| "...".to_string()); - - rsx! { - h1 { "High-Five counter: {count}" } - button { onclick: move |_| count += 1, "Up high!" } - button { onclick: move |_| count -= 1, "Down low!" } - button { - onclick: move |_| async move { - if let Ok(data) = get_server_data().await { - println!("Client received: {}", data); - text.set(data.clone()); - post_server_data(data).await.unwrap(); - } - }, - "Run a server function" - } - "Server said: {text}" - } -} - -#[server(PostServerData)] -async fn post_server_data(data: String) -> Result<(), ServerFnError> { - println!("Server received: {}", data); - - Ok(()) -} - -#[server(GetServerData)] -async fn get_server_data() -> Result { - Ok("Hello from the server!".to_string()) -} diff --git a/example-projects/fullstack-mobile/.gitignore b/example-projects/fullstack-mobile/.gitignore deleted file mode 100644 index a0f8b31977..0000000000 --- a/example-projects/fullstack-mobile/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/target -/ignore diff --git a/example-projects/fullstack-mobile/Cargo.toml b/example-projects/fullstack-mobile/Cargo.toml deleted file mode 100644 index 5d047a3d26..0000000000 --- a/example-projects/fullstack-mobile/Cargo.toml +++ /dev/null @@ -1,33 +0,0 @@ -[package] -name = "DioxusApp" -version = "0.1.0" -edition = "2021" - -[dependencies] -dioxus = { workspace = true, features = ["fullstack"] } -serde = { workspace = true, features = ["derive"] } -reqwest = { workspace = true, features = ["json"] } -tracing = { workspace = true } - -[features] -desktop = ["dioxus/desktop"] -mobile = ["dioxus/mobile"] -web = ["dioxus/web"] -server = ["dioxus/server"] - -[package.metadata.cargo-apple.ios] -frameworks = [ "WebKit" ] - -[package.metadata.bundle] -name = "DioxusApp" -identifier = "com.dioxuslabs" -resources = ["assets/**/*"] -category = "Utility" -short_description = "An example of a bundled application" -long_description = """ -A trivial application that just displays a blank window with -a title bar. It serves as an example of an application that -can be bundled with cargo-bundle, as well as a test-case for -cargo-bundle's support for bundling crate examples. -""" -osx_frameworks = ["WebKit"] diff --git a/example-projects/fullstack-mobile/assets/dogs/cute/dog0.jpg b/example-projects/fullstack-mobile/assets/dogs/cute/dog0.jpg deleted file mode 100644 index 519d90ad1064ed8c4a3f29276612fa7f22124cc4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39179 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 WxzE5#isy|g~S4@_R<6Od+ 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 diff --git a/example-projects/fullstack-mobile/assets/dogs/cute/dog1.jpg b/example-projects/fullstack-mobile/assets/dogs/cute/dog1.jpg deleted file mode 100644 index 12d029dab633a39c95707f25819f11518db47fbc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32904 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 diff --git a/example-projects/fullstack-mobile/assets/dogs/cute/dog2.jpg b/example-projects/fullstack-mobile/assets/dogs/cute/dog2.jpg deleted file mode 100644 index 0c27351dc16368c1b11f0701240b60e28d607939..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 46631 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 diff --git a/example-projects/fullstack-mobile/assets/dogs/cute/dog3.jpg b/example-projects/fullstack-mobile/assets/dogs/cute/dog3.jpg deleted file mode 100644 index 98cf2f25048cd0f54ba44c92613296cc796d2f63..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25476 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�o9%t_!bJ?NmV3Yc@oaBQ@`5qxPR1CVbDvI> z3_?nb@>jM^JAdxSZ)!%t0OSGdOgI1nK{@Ja2zxtcJBSpAeh1tVZBN z0gq5=fgbaNo!^NxzGoQ)hvQ9Vh0Ox6p@K2v9QLP%Tyj^hdW?~{Qj#;%tpQ=gf(a)B-jPmM91~HJbB11f15Zwe zAf9Lo4<`ii_~M#eki>30XCZo19Jb-qQG#Q}?kEcq1GoZQ^#>FHt&iRiGBKX?!0p#L z{3*&qk-*PhXbTQU0eAt5oE)B&UUMtpM&dR+>>qjJ{aw#|`3Sm#r2H4kNI~uRK&`0XzeWHxLx(B#d%;Qy7&aoO8x0 zq)?2CpDE{_w8AmR82VFKoQ#l8X!A3G3FP)YsfC1QA1|@z6w=Ch0|3;H=ly}W@NrQ8 z01ih0pIQKGh0fmRps0vFv&W@9!2m8t9SukKhfYUP>p)mfm3Sl{#}rVco|P#o5N#ZHqKcBo4B;TEBSP}{CMHC1`EHSWT4x_zJ zNF){qXFLziiYW+Kq*nQeJw`#PH=ORrOwmOEHCzG|01rK>4iJ|heMJ;f0ylLlhR-Jl zp0t6+;8z(mQ9#h4_Ln0B^fWBzEzbgqC<_W?J9zvkR$@w=7wwuTqym@u!R`k%Jfnfm zPp4Wapa{X*bGV!goYL+&>PYL>iYb8Y`RskUr9A+_9D4Plib4Q3oRRgV4mT3tg%nUR zu1jQg^%V3{y-rRjqL_zp+>G?%o4X1!KZ_kb=%RoC#4BVBk9tVicXQl*Xrh1*kUt08 z=}vR#2Tw{UphRJU=LZMAJ!#7$bOanxMF0%Mf`1xd9FfR0Q9#3uqpn3N;~aLPieMpO z*@>lG9DK%#C;@YW$;Bg*2d^|yK!n`eNGG14`%-zC?adTW1JQ|oc&BgLIVOrJjAIp1 zllW5|gYme6D58N7+N6Gxyx*{l3rqxANZ>fKE?KR|`Nu000pDI{^Q! z0UiR#NJvOYh{;GvNy*8{D5#jIsVFI_Kn#quOzf zl#!9)6p&YjNhyg-%SiptAOz&(&x1ok!@^@<$HgZkCLuGkvU76Z=H(ZZl~-UY ztEy{iTUy(2?H!$6?>-I;4h@ftj*ZVREG{j7TKT-Xy|cTwe}Mmbc=Y4^;_~YH=I8CN z|KTD40RK1Ezw7@7`+soJ|KlPgA_5YT{tp)cVaUG|NKZt}ElI+lZc6GDz{n#NO~#~= zQQF*34v{wd&g>jGM{$o=W}ENF|DgRZvj2BrG5^2F{x7iqn`;$71tj?Q@__UJReQlbwkOwQWp~_L8iw+7HR=dn1 ^`W?tDh;l)jzSBaiVxEHqez=bVT~a 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 P*WU#daok~4w~PM^iF>U1 diff --git a/example-projects/fullstack-mobile/assets/dogs/fluffy/dog1.jpg b/example-projects/fullstack-mobile/assets/dogs/fluffy/dog1.jpg deleted file mode 100644 index dd6043485e64702b6f8c68a5c8ba38a27583554e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19694 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

      XWd)q{&(J4kt*WM3pME^KcNe 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 z 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 z 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�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

      ZhS>uFx;AG$tNeu5ee;-x#5-^i^<(S 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 AO8@`> diff --git a/example-projects/fullstack-mobile/assets/favicon.ico b/example-projects/fullstack-mobile/assets/favicon.ico deleted file mode 100644 index 4b2cfff1ed3b1292ce273d0dc5a5ab3ae2a28f39..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9662 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 \ No newline at end of file diff --git a/example-projects/fullstack-mobile/assets/style.css b/example-projects/fullstack-mobile/assets/style.css deleted file mode 100644 index bf9ae32440..0000000000 --- a/example-projects/fullstack-mobile/assets/style.css +++ /dev/null @@ -1,29 +0,0 @@ -/* html, body { - height: 500px; - width: 500px; -} */ - -body { - color: "white"; - background-color: black; - /* background-color: #111216; */ - color: white; - font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; - /* overflow: hidden; */ - height: 100vh; -} - -body .sparkles { - color: yellow; -} -.container { - height: 100%; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; -} - -.dog-pic { - border-radius: 10px; -} diff --git a/example-projects/fullstack-mobile/assets/tailwind.css b/example-projects/fullstack-mobile/assets/tailwind.css deleted file mode 100644 index 8030ad5220..0000000000 --- a/example-projects/fullstack-mobile/assets/tailwind.css +++ /dev/null @@ -1,664 +0,0 @@ -/* -! tailwindcss v3.4.10 | MIT License | https://tailwindcss.com -*/ - -/* -1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) -2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) -*/ - -*, -::before, -::after { - box-sizing: border-box; - /* 1 */ - border-width: 0; - /* 2 */ - border-style: solid; - /* 2 */ - border-color: #e5e7eb; - /* 2 */ -} - -::before, -::after { - --tw-content: ''; -} - -/* -1. Use a consistent sensible line-height in all browsers. -2. Prevent adjustments of font size after orientation changes in iOS. -3. Use a more readable tab size. -4. Use the user's configured `sans` font-family by default. -5. Use the user's configured `sans` font-feature-settings by default. -6. Use the user's configured `sans` font-variation-settings by default. -7. Disable tap highlights on iOS -*/ - -html, -:host { - line-height: 1.5; - /* 1 */ - -webkit-text-size-adjust: 100%; - /* 2 */ - -moz-tab-size: 4; - /* 3 */ - -o-tab-size: 4; - tab-size: 4; - /* 3 */ - font-family: Inter var, sans-serif; - /* 4 */ - font-feature-settings: normal; - /* 5 */ - font-variation-settings: normal; - /* 6 */ - -webkit-tap-highlight-color: transparent; - /* 7 */ -} - -/* -1. Remove the margin in all browsers. -2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. -*/ - -body { - margin: 0; - /* 1 */ - line-height: inherit; - /* 2 */ -} - -/* -1. Add the correct height in Firefox. -2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) -3. Ensure horizontal rules are visible by default. -*/ - -hr { - height: 0; - /* 1 */ - color: inherit; - /* 2 */ - border-top-width: 1px; - /* 3 */ -} - -/* -Add the correct text decoration in Chrome, Edge, and Safari. -*/ - -abbr:where([title]) { - -webkit-text-decoration: underline dotted; - text-decoration: underline dotted; -} - -/* -Remove the default font size and weight for headings. -*/ - -h1, -h2, -h3, -h4, -h5, -h6 { - font-size: inherit; - font-weight: inherit; -} - -/* -Reset links to optimize for opt-in styling instead of opt-out. -*/ - -a { - color: inherit; - text-decoration: inherit; -} - -/* -Add the correct font weight in Edge and Safari. -*/ - -b, -strong { - font-weight: bolder; -} - -/* -1. Use the user's configured `mono` font-family by default. -2. Use the user's configured `mono` font-feature-settings by default. -3. Use the user's configured `mono` font-variation-settings by default. -4. Correct the odd `em` font sizing in all browsers. -*/ - -code, -kbd, -samp, -pre { - font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; - /* 1 */ - font-feature-settings: normal; - /* 2 */ - font-variation-settings: normal; - /* 3 */ - font-size: 1em; - /* 4 */ -} - -/* -Add the correct font size in all browsers. -*/ - -small { - font-size: 80%; -} - -/* -Prevent `sub` and `sup` elements from affecting the line height in all browsers. -*/ - -sub, -sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; -} - -sub { - bottom: -0.25em; -} - -sup { - top: -0.5em; -} - -/* -1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) -2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) -3. Remove gaps between table borders by default. -*/ - -table { - text-indent: 0; - /* 1 */ - border-color: inherit; - /* 2 */ - border-collapse: collapse; - /* 3 */ -} - -/* -1. Change the font styles in all browsers. -2. Remove the margin in Firefox and Safari. -3. Remove default padding in all browsers. -*/ - -button, -input, -optgroup, -select, -textarea { - font-family: inherit; - /* 1 */ - font-feature-settings: inherit; - /* 1 */ - font-variation-settings: inherit; - /* 1 */ - font-size: 100%; - /* 1 */ - font-weight: inherit; - /* 1 */ - line-height: inherit; - /* 1 */ - letter-spacing: inherit; - /* 1 */ - color: inherit; - /* 1 */ - margin: 0; - /* 2 */ - padding: 0; - /* 3 */ -} - -/* -Remove the inheritance of text transform in Edge and Firefox. -*/ - -button, -select { - text-transform: none; -} - -/* -1. Correct the inability to style clickable types in iOS and Safari. -2. Remove default button styles. -*/ - -button, -input:where([type='button']), -input:where([type='reset']), -input:where([type='submit']) { - -webkit-appearance: button; - /* 1 */ - background-color: transparent; - /* 2 */ - background-image: none; - /* 2 */ -} - -/* -Use the modern Firefox focus style for all focusable elements. -*/ - -:-moz-focusring { - outline: auto; -} - -/* -Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) -*/ - -:-moz-ui-invalid { - box-shadow: none; -} - -/* -Add the correct vertical alignment in Chrome and Firefox. -*/ - -progress { - vertical-align: baseline; -} - -/* -Correct the cursor style of increment and decrement buttons in Safari. -*/ - -::-webkit-inner-spin-button, -::-webkit-outer-spin-button { - height: auto; -} - -/* -1. Correct the odd appearance in Chrome and Safari. -2. Correct the outline style in Safari. -*/ - -[type='search'] { - -webkit-appearance: textfield; - /* 1 */ - outline-offset: -2px; - /* 2 */ -} - -/* -Remove the inner padding in Chrome and Safari on macOS. -*/ - -::-webkit-search-decoration { - -webkit-appearance: none; -} - -/* -1. Correct the inability to style clickable types in iOS and Safari. -2. Change font properties to `inherit` in Safari. -*/ - -::-webkit-file-upload-button { - -webkit-appearance: button; - /* 1 */ - font: inherit; - /* 2 */ -} - -/* -Add the correct display in Chrome and Safari. -*/ - -summary { - display: list-item; -} - -/* -Removes the default spacing and border for appropriate elements. -*/ - -blockquote, -dl, -dd, -h1, -h2, -h3, -h4, -h5, -h6, -hr, -figure, -p, -pre { - margin: 0; -} - -fieldset { - margin: 0; - padding: 0; -} - -legend { - padding: 0; -} - -ol, -ul, -menu { - list-style: none; - margin: 0; - padding: 0; -} - -/* -Reset default styling for dialogs. -*/ - -dialog { - padding: 0; -} - -/* -Prevent resizing textareas horizontally by default. -*/ - -textarea { - resize: vertical; -} - -/* -1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) -2. Set the default placeholder color to the user's configured gray 400 color. -*/ - -input::-moz-placeholder, textarea::-moz-placeholder { - opacity: 1; - /* 1 */ - color: #9ca3af; - /* 2 */ -} - -input::placeholder, -textarea::placeholder { - opacity: 1; - /* 1 */ - color: #9ca3af; - /* 2 */ -} - -/* -Set the default cursor for buttons. -*/ - -button, -[role="button"] { - cursor: pointer; -} - -/* -Make sure disabled buttons don't get the pointer cursor. -*/ - -:disabled { - cursor: default; -} - -/* -1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) -2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) - This can trigger a poorly considered lint error in some tools but is included by design. -*/ - -img, -svg, -video, -canvas, -audio, -iframe, -embed, -object { - display: block; - /* 1 */ - vertical-align: middle; - /* 2 */ -} - -/* -Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) -*/ - -img, -video { - max-width: 100%; - height: auto; -} - -/* Make elements with the HTML hidden attribute stay hidden by default */ - -[hidden] { - display: none; -} - -*, ::before, ::after { - --tw-border-spacing-x: 0; - --tw-border-spacing-y: 0; - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-rotate: 0; - --tw-skew-x: 0; - --tw-skew-y: 0; - --tw-scale-x: 1; - --tw-scale-y: 1; - --tw-pan-x: ; - --tw-pan-y: ; - --tw-pinch-zoom: ; - --tw-scroll-snap-strictness: proximity; - --tw-gradient-from-position: ; - --tw-gradient-via-position: ; - --tw-gradient-to-position: ; - --tw-ordinal: ; - --tw-slashed-zero: ; - --tw-numeric-figure: ; - --tw-numeric-spacing: ; - --tw-numeric-fraction: ; - --tw-ring-inset: ; - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-color: rgb(59 130 246 / 0.5); - --tw-ring-offset-shadow: 0 0 #0000; - --tw-ring-shadow: 0 0 #0000; - --tw-shadow: 0 0 #0000; - --tw-shadow-colored: 0 0 #0000; - --tw-blur: ; - --tw-brightness: ; - --tw-contrast: ; - --tw-grayscale: ; - --tw-hue-rotate: ; - --tw-invert: ; - --tw-saturate: ; - --tw-sepia: ; - --tw-drop-shadow: ; - --tw-backdrop-blur: ; - --tw-backdrop-brightness: ; - --tw-backdrop-contrast: ; - --tw-backdrop-grayscale: ; - --tw-backdrop-hue-rotate: ; - --tw-backdrop-invert: ; - --tw-backdrop-opacity: ; - --tw-backdrop-saturate: ; - --tw-backdrop-sepia: ; - --tw-contain-size: ; - --tw-contain-layout: ; - --tw-contain-paint: ; - --tw-contain-style: ; -} - -::backdrop { - --tw-border-spacing-x: 0; - --tw-border-spacing-y: 0; - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-rotate: 0; - --tw-skew-x: 0; - --tw-skew-y: 0; - --tw-scale-x: 1; - --tw-scale-y: 1; - --tw-pan-x: ; - --tw-pan-y: ; - --tw-pinch-zoom: ; - --tw-scroll-snap-strictness: proximity; - --tw-gradient-from-position: ; - --tw-gradient-via-position: ; - --tw-gradient-to-position: ; - --tw-ordinal: ; - --tw-slashed-zero: ; - --tw-numeric-figure: ; - --tw-numeric-spacing: ; - --tw-numeric-fraction: ; - --tw-ring-inset: ; - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-color: rgb(59 130 246 / 0.5); - --tw-ring-offset-shadow: 0 0 #0000; - --tw-ring-shadow: 0 0 #0000; - --tw-shadow: 0 0 #0000; - --tw-shadow-colored: 0 0 #0000; - --tw-blur: ; - --tw-brightness: ; - --tw-contrast: ; - --tw-grayscale: ; - --tw-hue-rotate: ; - --tw-invert: ; - --tw-saturate: ; - --tw-sepia: ; - --tw-drop-shadow: ; - --tw-backdrop-blur: ; - --tw-backdrop-brightness: ; - --tw-backdrop-contrast: ; - --tw-backdrop-grayscale: ; - --tw-backdrop-hue-rotate: ; - --tw-backdrop-invert: ; - --tw-backdrop-opacity: ; - --tw-backdrop-saturate: ; - --tw-backdrop-sepia: ; - --tw-contain-size: ; - --tw-contain-layout: ; - --tw-contain-paint: ; - --tw-contain-style: ; -} - -.container { - width: 100%; -} - -@media (min-width: 640px) { - .container { - max-width: 640px; - } -} - -@media (min-width: 768px) { - .container { - max-width: 768px; - } -} - -@media (min-width: 1024px) { - .container { - max-width: 1024px; - } -} - -@media (min-width: 1280px) { - .container { - max-width: 1280px; - } -} - -@media (min-width: 1536px) { - .container { - max-width: 1536px; - } -} - -.my-4 { - margin-top: 1rem; - margin-bottom: 1rem; -} - -.my-12 { - margin-top: 3rem; - margin-bottom: 3rem; -} - -.my-20 { - margin-top: 5rem; - margin-bottom: 5rem; -} - -.mx-auto { - margin-left: auto; - margin-right: auto; -} - -.flex { - display: flex; -} - -.h-full { - height: 100%; -} - -.w-full { - width: 100%; -} - -.flex-row { - flex-direction: row; -} - -.flex-col { - flex-direction: column; -} - -.justify-center { - justify-content: center; -} - -.space-y-2 > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(0.5rem * var(--tw-space-y-reverse)); -} - -.space-y-4 > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(1rem * var(--tw-space-y-reverse)); -} - -.space-y-12 > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(3rem * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(3rem * var(--tw-space-y-reverse)); -} - -.text-center { - text-align: center; -} - -.text-4xl { - font-size: 2.25rem; - line-height: 2.5rem; -} - -.font-bold { - font-weight: 700; -} diff --git a/example-projects/fullstack-mobile/makefile.toml b/example-projects/fullstack-mobile/makefile.toml deleted file mode 100644 index f4b94eac19..0000000000 --- a/example-projects/fullstack-mobile/makefile.toml +++ /dev/null @@ -1,45 +0,0 @@ -[tasks.build_ios_sim] -args = ["bundle", "--target", "aarch64-apple-ios-sim", "--features", "mobile"] -command = "cargo" - -[tasks.install_ios_sim] -command = "xcrun" -args = [ - "simctl", - "install", - "booted", - "target/aarch64-apple-ios-sim/debug/bundle/ios/DioxusApp.app", -] - -[tasks.run_ios_sim] -env = { SIMCTL_CHILD_DIOXUS_DEVSERVER_ADDR="ws://0.0.0.0:8080/_dioxus", SIMCTL_CHILD_CARGO_MANIFEST_DIR="/Users/jonkelley/Development/Tinkering/ios-binary"} -args = ["simctl", "launch", "--console", "booted", "com.dioxuslabs"] -command = "xcrun" -dependencies = ["build_ios_sim", "install_ios_sim"] - -[tasks.serve-sim] -dependencies = ["build_ios_sim", "install_ios_sim", "run_ios_sim"] - -[tasks.build_ios_device] -command = "cargo" -args = ["bundle", "--target", "aarch64-apple-ios"] - -[tasks.code-sign-ios-device] -command = "./tools/make-entitlements.sh" - -# god bless the crypto bros <3 -# https://github.com/status-im/status-mobile/blob/6a5e718cd389f61d3708c4372856609afb40d11f/scripts/run-ios-device.sh#L6 -[tasks.run-ios-device] -command = "./tools/run-ios-device.sh" - -[tasks.serve-device] -dependencies = [ - "build_ios_device", - "code-sign-ios-device", - "run-ios-device", -] - -[tasks.serve] -dependencies = [ - "run_ios_sim", -] diff --git a/example-projects/fullstack-mobile/src/main.rs b/example-projects/fullstack-mobile/src/main.rs deleted file mode 100644 index ad57565f07..0000000000 --- a/example-projects/fullstack-mobile/src/main.rs +++ /dev/null @@ -1,68 +0,0 @@ -use dioxus::prelude::*; - -fn main() { - println!("[server] Launching app!"); - - dioxus::launch(app); -} - -fn app() -> Element { - let mut favorite_dog = use_signal(|| None); - - rsx! { - document::Stylesheet { href: asset!("/assets/style.css") } - document::Stylesheet { href: asset!("/assets/tailwind.css") } - - div { class: "w-full h-full text-center my-20", - h1 { class: "text-4xl font-bold", "Dioxus iOS apps!" } - button { - onclick: move |_| async move { - let dog = get_random_dog().await.unwrap_or_else(|err| format!("Error: {err}")); - favorite_dog.set(Some(dog)); - }, - "New favorite dog!" - } - } - - div { class: "w-full h-full flex flex-col mx-auto space-y-12", - if let Some(favorite_dog) = favorite_dog() { - img { src: "{favorite_dog}", max_width: "500px", height: "500px", margin: "auto" } - } - ImageList { dogs: 3, style: "cute" } - } - } -} - -#[component] -fn ImageList(dogs: ReadOnlySignal, style: String) -> Element { - rsx! { - for i in 0..dogs() { - ul { class: "flex flex-row justify-center", - img { - src: "/assets/dogs/{style}/dog{i}.jpg", - height: "200px", - border_radius: "10px", - } - } - } - } -} - -#[server(endpoint = "get_random_dog")] -async fn get_random_dog() -> Result { - let breed = "husky".to_string(); - println!("Getting a random dog from the server!"); - - #[derive(serde::Deserialize, Debug)] - struct DogApi { - message: String, - } - - let dog = reqwest::get(format!("https://dog.ceo/api/breed/{breed}/images/random")) - .await - .unwrap() - .json::() - .await?; - - Ok(dog.message) -} diff --git a/example-projects/fullstack-mobile/tailwind.config.js b/example-projects/fullstack-mobile/tailwind.config.js deleted file mode 100644 index b3af0ed75c..0000000000 --- a/example-projects/fullstack-mobile/tailwind.config.js +++ /dev/null @@ -1,42 +0,0 @@ -/** @type {import('tailwindcss').Config} */ -module.exports = { - mode: "all", - content: ["./src/**/*.{rs,html,css}", "./docs/**/*.html"], - theme: { - extend: { - colors: { - dxorange: "#E96020", - dxblue: "#00A8D6", - ghmetal: "#24292f", - ghdarkmetal: "#161b22", - // ideblack: "#222529", - ideblack: "#0e1116", - // ideblack: "#0a0a0a", - // ideblack: "#0E1116", - }, - fontFamily: { - // sans: [`"Poppins"`, "sans-serif"], - // sans: ["Arimo", "sans-serif"], - // sans: ["Lexend", "sans-serif"], - sans: ["Inter var", "sans-serif"], - }, - boxShadow: { - "3xl": "0 35px 60px -1ww5px rgba(0, 0, 0, 0.5)", - cutesy: "0px 0px 40px -5px rgba(255, 255, 255, 0.2)", - // cutesy: "0px 0px 30px -10px white", - // cutesy: "0px 0px 30px -10px red", - pop: "0px 0px 30px -10px rgba(0, 0, 0, 0.5)", - }, - keyframes: { - fadein: { - from: { opacity: "0" }, - to: { opacity: "1" }, - }, - }, - animation: { - "fadein-medium": "fadein 500ms ease-in-out forwards", - }, - }, - }, - plugins: [], -}; diff --git a/example-projects/fullstack-mobile/tailwind.css b/example-projects/fullstack-mobile/tailwind.css deleted file mode 100644 index b5c61c9567..0000000000 --- a/example-projects/fullstack-mobile/tailwind.css +++ /dev/null @@ -1,3 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; diff --git a/example-projects/fullstack-mobile/tools/make-entitlements.sh b/example-projects/fullstack-mobile/tools/make-entitlements.sh deleted file mode 100755 index 50d68b9fbf..0000000000 --- a/example-projects/fullstack-mobile/tools/make-entitlements.sh +++ /dev/null @@ -1,41 +0,0 @@ -# get the mobile provisioning profile -export APP_DEV_NAME=$(xcrun security find-identity -v -p codesigning | grep "Apple Development: " | sed -E 's/.*"([^"]+)".*/\1/') - -# Find the provisioning profile from ~/Library/MobileDevice/Provisioning\ Profiles -export PROVISION_FILE=$(ls ~/Library/MobileDevice/Provisioning\ Profiles | grep mobileprovision) - -# Convert the provisioning profile to json so we can use jq to extract the important bits -security cms -D \ - -i ~/Library/MobileDevice/Provisioning\ Profiles/${PROVISION_FILE} | \ - python3 -c 'import plistlib,sys,json; print(json.dumps(plistlib.loads(sys.stdin.read().encode("utf-8")), default=lambda o:""))' \ - > target/provisioning.json - -# jq out the important bits of the provisioning profile -export TEAM_IDENTIFIER=$(jq -r '.TeamIdentifier[0]' target/provisioning.json) -export APPLICATION_IDENTIFIER_PREFIX=$(jq -r '.ApplicationIdentifierPrefix[0]' target/provisioning.json) -export APPLICATION_IDENTIFIER=$(jq -r '.Entitlements."application-identifier"' target/provisioning.json) -export APP_ID_ACCESS_GROUP=$(jq -r '.Entitlements."keychain-access-groups"[0]' target/provisioning.json) - -# now build the entitlements file -cat < target/entitlements.xcent - - - - application-identifier - ${APPLICATION_IDENTIFIER} - keychain-access-groups - - ${APP_ID_ACCESS_GROUP}.* - - get-task-allow - - com.apple.developer.team-identifier - ${TEAM_IDENTIFIER} - -EOF - -# sign the app -codesign --force \ - --entitlements target/entitlements.xcent \ - --sign "${APP_DEV_NAME}" \ - target/aarch64-apple-ios/debug/bundle/ios/DioxusApp.app diff --git a/example-projects/fullstack-mobile/tools/run-ios-device.sh b/example-projects/fullstack-mobile/tools/run-ios-device.sh deleted file mode 100755 index 80f85d2718..0000000000 --- a/example-projects/fullstack-mobile/tools/run-ios-device.sh +++ /dev/null @@ -1,34 +0,0 @@ - -APP_PATH="target/aarch64-apple-ios/debug/bundle/ios/DioxusApp.app" - -# get the device id by jq-ing the json of the device list -xcrun devicectl list devices --json-output target/deviceid.json -DEVICE_UUID=$(jq -r '.result.devices[0].identifier' target/deviceid.json) - -xcrun devicectl device install app --device "${DEVICE_UUID}" "${APP_PATH}" --json-output target/xcrun.json - -# get the installation url by jq-ing the json of the device install -INSTALLATION_URL=$(jq -r '.result.installedApplications[0].installationURL' target/xcrun.json) - -export SIMCTL_CHILD_IP_ADDRESS="123123" -export SIMCTL_CHILD_DIOXUS_DEVSERVER_ADDR="ws://127.0.0.1:8080/_dioxus" - -# launch the app -# todo: we can just background it immediately and then pick it up for loading its logs -xcrun devicectl device process launch --device "${DEVICE_UUID}" "${INSTALLATION_URL}" - -# # launch the app and put it in background -# xcrun devicectl device process launch --no-activate --verbose --device "${DEVICE_UUID}" "${INSTALLATION_URL}" --json-output "${XCRUN_DEVICE_PROCESS_LAUNCH_LOG_DIR}" - -# # Extract background PID of status app -# STATUS_PID=$(jq -r '.result.process.processIdentifier' "${XCRUN_DEVICE_PROCESS_LAUNCH_LOG_DIR}") -# "${GIT_ROOT}/scripts/wait-for-metro-port.sh" 2>&1 - -# # now that metro is ready, resume the app from background -# xcrun devicectl device process resume --device "${DEVICE_UUID}" --pid "${STATUS_PID}" > "${XCRUN_DEVICE_PROCESS_RESUME_LOG_DIR}" 2>&1 - - - - - - diff --git a/example-projects/hackernews/.gitignore b/example-projects/hackernews/.gitignore deleted file mode 100644 index 12cf96b284..0000000000 --- a/example-projects/hackernews/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/static -/dist \ No newline at end of file diff --git a/example-projects/hackernews/Cargo.toml b/example-projects/hackernews/Cargo.toml deleted file mode 100644 index 68c247573e..0000000000 --- a/example-projects/hackernews/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "fullstack-hackernews-example" -version = "0.1.0" -authors = ["Evan Almloff "] -edition = "2021" -publish = false - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -dioxus = { workspace = true, features = ["fullstack", "router"] } -chrono = { version = "0.4.38", features = ["serde"] } -reqwest = { workspace= true, features = ["json"] } -serde = { workspace = true, features = ["derive"] } -tracing-wasm = "0.2.1" -tracing = { workspace = true } -tracing-subscriber = "0.3.17" - -[features] -default = [] -server = ["dioxus/axum"] -web = ["dioxus/web"] diff --git a/example-projects/hackernews/assets/hackernews.css b/example-projects/hackernews/assets/hackernews.css deleted file mode 100644 index cd01ad879e..0000000000 --- a/example-projects/hackernews/assets/hackernews.css +++ /dev/null @@ -1,17 +0,0 @@ -@keyframes spin { - 0% { - transform: rotate(0deg); - } - - 100% { - transform: rotate(360deg); - } -} -.spinner { - width: 10px; - height: 10px; - border: 4px solid #f3f3f3; - border-top: 4px solid #3498db; - border-radius: 50%; - animation: spin 2s linear infinite; -} \ No newline at end of file diff --git a/example-projects/hackernews/src/main.rs b/example-projects/hackernews/src/main.rs deleted file mode 100644 index b675b6003d..0000000000 --- a/example-projects/hackernews/src/main.rs +++ /dev/null @@ -1,306 +0,0 @@ -#![allow(non_snake_case, unused)] -use dioxus::prelude::*; -// Define the Hackernews API and types -use chrono::{DateTime, Utc}; -use serde::{Deserialize, Serialize}; -use std::{ - fmt::{Display, Formatter}, - num::ParseIntError, - str::FromStr, -}; -use svg_attributes::to; - -fn main() { - #[cfg(feature = "web")] - tracing_wasm::set_as_global_default(); - - #[cfg(feature = "server")] - tracing_subscriber::fmt::init(); - - dioxus::launch(|| rsx! { Router:: {} }); -} - -#[derive(Clone, Routable)] -enum Route { - #[redirect("/", || Route::Homepage { story: PreviewState { active_story: None } })] - #[route("/:story")] - Homepage { story: PreviewState }, -} - -pub fn App() -> Element { - rsx! { - Router:: {} - } -} - -#[component] -fn Homepage(story: ReadOnlySignal) -> Element { - rsx! { - document::Stylesheet { href: asset!("/assets/hackernews.css") } - div { display: "flex", flex_direction: "row", width: "100%", - div { - width: "50%", - SuspenseBoundary { - fallback: |context: SuspenseContext| rsx! { - "Loading..." - }, - Stories {} - } - } - div { width: "50%", - SuspenseBoundary { - fallback: |context: SuspenseContext| rsx! { - "Loading preview..." - }, - Preview { - story - } - } - } - } - } -} - -#[component] -fn Stories() -> Element { - let stories: Resource>> = use_server_future(move || async move { - let url = format!("{}topstories.json", BASE_API_URL); - let mut stories_ids = reqwest::get(&url).await?.json::>().await?; - stories_ids.truncate(30); - Ok(stories_ids) - })?; - - match stories().unwrap() { - Ok(list) => rsx! { - div { - for story in list { - ChildrenOrLoading { - key: "{story}", - StoryListing { story } - } - } - } - }, - Err(err) => rsx! {"An error occurred while fetching stories {err}"}, - } -} - -#[component] -fn StoryListing(story: ReadOnlySignal) -> Element { - let story = use_server_future(move || get_story(story()))?; - - let StoryItem { - title, - url, - by, - score, - time, - kids, - id, - .. - } = story().unwrap()?.item; - - let url = url.as_deref().unwrap_or_default(); - let hostname = url - .trim_start_matches("https://") - .trim_start_matches("http://") - .trim_start_matches("www."); - let score = format!("{score} {}", if score == 1 { " point" } else { " points" }); - let comments = format!( - "{} {}", - kids.len(), - if kids.len() == 1 { - " comment" - } else { - " comments" - } - ); - let time = time.format("%D %l:%M %p"); - - rsx! { - div { - padding: "0.5rem", - position: "relative", - div { font_size: "1.5rem", - Link { - to: Route::Homepage { story: PreviewState { active_story: Some(id) } }, - "{title}" - } - a { - color: "gray", - href: "https://news.ycombinator.com/from?site={hostname}", - text_decoration: "none", - " ({hostname})" - } - } - div { display: "flex", flex_direction: "row", color: "gray", - div { "{score}" } - div { padding_left: "0.5rem", "by {by}" } - div { padding_left: "0.5rem", "{time}" } - div { padding_left: "0.5rem", "{comments}" } - } - } - } -} - -#[derive(Clone, Debug, Default)] -struct PreviewState { - active_story: Option, -} - -impl FromStr for PreviewState { - type Err = ParseIntError; - - fn from_str(s: &str) -> Result { - let state = i64::from_str(s)?; - Ok(PreviewState { - active_story: Some(state), - }) - } -} - -impl Display for PreviewState { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - if let Some(id) = &self.active_story { - write!(f, "{id}")?; - } - Ok(()) - } -} - -#[component] -fn Preview(story: ReadOnlySignal) -> Element { - let PreviewState { - active_story: Some(id), - } = story() - else { - return rsx! {"Hover over a story to preview it here"}; - }; - - let story = use_server_future(use_reactive!(|id| get_story(id)))?; - - let story = story().unwrap()?; - - rsx! { - div { padding: "0.5rem", - div { font_size: "1.5rem", a { href: story.item.url, "{story.item.title}" } } - if let Some(text) = &story.item.text { div { dangerous_inner_html: "{text}" } } - for comment in story.item.kids.iter().copied() { - ChildrenOrLoading { - key: "{comment}", - Comment { comment } - } - } - } - } -} - -#[component] -fn Comment(comment: i64) -> Element { - let comment: Resource> = - use_server_future(use_reactive!(|comment| async move { - let url = format!("{}{}{}.json", BASE_API_URL, ITEM_API, comment); - let mut comment = reqwest::get(&url).await?.json::().await?; - Ok(comment) - }))?; - - let CommentData { - by, - time, - text, - id, - kids, - .. - } = comment().unwrap()?; - - rsx! { - div { padding: "0.5rem", - div { color: "gray", "by {by}" } - div { dangerous_inner_html: "{text}" } - for comment in kids.iter().copied() { - ChildrenOrLoading { - key: "{comment}", - Comment { comment } - } - } - } - } -} - -pub static BASE_API_URL: &str = "https://hacker-news.firebaseio.com/v0/"; -pub static ITEM_API: &str = "item/"; -pub static USER_API: &str = "user/"; -const COMMENT_DEPTH: i64 = 1; - -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct StoryPageData { - #[serde(flatten)] - pub item: StoryItem, - #[serde(default)] - pub comments: Vec, -} - -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct CommentData { - pub id: i64, - /// there will be no by field if the comment was deleted - #[serde(default)] - pub by: String, - #[serde(default)] - pub text: String, - #[serde(with = "chrono::serde::ts_seconds")] - pub time: DateTime, - #[serde(default)] - pub kids: Vec, - pub r#type: String, -} - -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct StoryItem { - pub id: i64, - pub title: String, - pub url: Option, - pub text: Option, - #[serde(default)] - pub by: String, - #[serde(default)] - pub score: i64, - #[serde(default)] - pub descendants: i64, - #[serde(with = "chrono::serde::ts_seconds")] - pub time: DateTime, - #[serde(default)] - pub kids: Vec, - pub r#type: String, -} - -pub async fn get_story(id: i64) -> dioxus::Result { - let url = format!("{}{}{}.json", BASE_API_URL, ITEM_API, id); - Ok(reqwest::get(&url).await?.json::().await?) -} - -#[component] -fn ChildrenOrLoading(children: Element) -> Element { - rsx! { - SuspenseBoundary { - fallback: |context: SuspenseContext| { - rsx! { - if let Some(placeholder) = context.suspense_placeholder() { - {placeholder} - } else { - LoadingIndicator {} - } - } - }, - children - } - } -} - -fn LoadingIndicator() -> Element { - rsx! { - div { - class: "spinner", - } - } -} diff --git a/example-projects/hello-world/.gitignore b/example-projects/hello-world/.gitignore deleted file mode 100644 index 21fff11dd3..0000000000 --- a/example-projects/hello-world/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -dist -target -static -.dioxus \ No newline at end of file diff --git a/example-projects/hello-world/Cargo.toml b/example-projects/hello-world/Cargo.toml deleted file mode 100644 index 16bb88cfff..0000000000 --- a/example-projects/hello-world/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -name = "fullstack-hello-world-example" -version = "0.1.0" -edition = "2021" -publish = false - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -dioxus = { workspace = true, features = ["fullstack"]} -serde = "1.0.159" -simple_logger = "4.2.0" -tracing-wasm = "0.2.1" -tracing = { workspace = true } -tracing-subscriber = "0.3.17" -reqwest = { workspace = true } - -[features] -default = [] -server = ["dioxus/axum"] -web = ["dioxus/web"] diff --git a/example-projects/hello-world/src/main.rs b/example-projects/hello-world/src/main.rs deleted file mode 100644 index b99664eb04..0000000000 --- a/example-projects/hello-world/src/main.rs +++ /dev/null @@ -1,54 +0,0 @@ -//! Run with: -//! -//! ```sh -//! dx serve --platform fullstack -//! ``` - -#![allow(non_snake_case, unused)] -use dioxus::prelude::*; -use serde::{Deserialize, Serialize}; - -fn app() -> Element { - let mut count = use_signal(|| 0); - let mut text = use_signal(|| "...".to_string()); - let server_future = use_server_future(get_server_data)?; - - rsx! { - h1 { "High-Five counter: {count}" } - button { onclick: move |_| count += 1, "Up high!" } - button { onclick: move |_| count -= 1, "Down low!" } - button { - onclick: move |_| async move { - if let Ok(data) = get_server_data().await { - println!("Client received: {}", data); - text.set(data.clone()); - post_server_data(data).await.unwrap(); - } - }, - "Run a server function!" - } - "Server said: {text}" - } -} - -#[server] -async fn post_server_data(data: String) -> Result<(), ServerFnError> { - println!("Server received: {}", data); - - Ok(()) -} - -#[server] -async fn get_server_data() -> Result { - Ok(reqwest::get("https://httpbin.org/ip").await?.text().await?) -} - -fn main() { - #[cfg(feature = "web")] - tracing_wasm::set_as_global_default(); - - #[cfg(feature = "server")] - tracing_subscriber::fmt::init(); - - dioxus::launch(app); -} diff --git a/example-projects/liveview-router/.gitignore b/example-projects/liveview-router/.gitignore deleted file mode 100644 index 21fff11dd3..0000000000 --- a/example-projects/liveview-router/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -dist -target -static -.dioxus \ No newline at end of file diff --git a/example-projects/liveview-router/Cargo.toml b/example-projects/liveview-router/Cargo.toml deleted file mode 100644 index e6d20a9135..0000000000 --- a/example-projects/liveview-router/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "liveview-router" -version = "0.1.0" -edition = "2021" -publish = false - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -dioxus = { workspace = true, features = ["liveview", "router"] } -axum = { workspace = true } -tokio = { workspace = true, features = ["full"] } - -[features] -default = [] diff --git a/example-projects/liveview-router/src/main.rs b/example-projects/liveview-router/src/main.rs deleted file mode 100644 index 50eee45795..0000000000 --- a/example-projects/liveview-router/src/main.rs +++ /dev/null @@ -1,190 +0,0 @@ -use dioxus::prelude::*; -use std::str::FromStr; - -#[tokio::main] -async fn main() { - use axum::{extract::ws::WebSocketUpgrade, response::Html, routing::get, Router}; - - let listen_address: std::net::SocketAddr = ([127, 0, 0, 1], 3030).into(); - let view = liveview::LiveViewPool::new(); - let app = Router::new() - .fallback(get(move || async move { - Html(format!( - r#" - - - -

      - {glue} - - "#, - glue = liveview::interpreter_glue(&format!("ws://{listen_address}/ws")) - )) - })) - .route( - "/ws", - get(move |ws: WebSocketUpgrade| async move { - ws.on_upgrade(move |socket| async move { - _ = view.launch(liveview::axum_socket(socket), app).await; - }) - }), - ); - - println!("Listening on http://{listen_address}"); - - let listener = tokio::net::TcpListener::bind(&listen_address) - .await - .unwrap(); - - axum::serve(listener, app.into_make_service()) - .await - .unwrap(); -} - -fn app() -> Element { - rsx! { - Router:: {} - } -} - -#[component] -fn UserFrame(user_id: usize) -> Element { - rsx! { - pre { "UserFrame{{\n\tuser_id:{user_id}\n}}" } - div { background_color: "rgba(0,0,0,50%)", - "children:" - Outlet:: {} - } - } -} - -#[component] -fn Route1(user_id: usize, dynamic: usize, query: String) -> Element { - rsx! { - pre { "Route1{{\n\tuser_id:{user_id},\n\tdynamic:{dynamic},\n\tquery:{query}\n}}" } - Link { - to: Route::Route1 { - user_id, - dynamic, - query: String::new(), - }, - "Route1 with extra+\".\"" - } - p { "Footer" } - Link { - to: Route::Route3 { - dynamic: String::new(), - }, - "Home" - } - } -} - -#[component] -fn Route2(user_id: usize) -> Element { - rsx! { - pre { "Route2{{\n\tuser_id:{user_id}\n}}" } - {(0..user_id).map(|i| rsx! { p { "{i}" } })} - p { "Footer" } - Link { - to: Route::Route3 { - dynamic: String::new(), - }, - "Home" - } - } -} - -#[component] -fn Route3(dynamic: String) -> Element { - let mut current_route_str = use_signal(String::new); - - let current_route = use_route(); - let parsed = Route::from_str(¤t_route_str.read()); - - let site_map = Route::SITE_MAP - .iter() - .flat_map(|seg| seg.flatten().into_iter()) - .collect::>(); - - let navigator = use_navigator(); - - rsx! { - input { - oninput: move |evt: FormEvent| { - *current_route_str.write() = evt.value(); - }, - value: "{current_route_str}", - } - "dynamic: {dynamic}" - Link { to: Route::Route2 { user_id: 8888 }, "hello world link" } - button { - disabled: !navigator.can_go_back(), - onclick: move |_| { - navigator.go_back(); - }, - "go back" - } - button { - disabled: !navigator.can_go_forward(), - onclick: move |_| { - navigator.go_forward(); - }, - "go forward" - } - button { - onclick: move |_| { - navigator.push("https://www.google.com"); - }, - "google link" - } - p { "Site Map" } - pre { "{site_map:#?}" } - p { "Dynamic link" } - match parsed { - Ok(route) => { - if route != current_route { - rsx! { - Link { to: route.clone(), "{route}" } - } - } else { - VNode::empty() - } - } - Err(err) => { - rsx! { - pre { color: "red", "Invalid route:\n{err}" } - } - } - } - } -} - -#[rustfmt::skip] -#[derive(Clone, Debug, PartialEq, Routable)] -enum Route { - #[nest("/test")] - // Nests with parameters have types taken from child routes - #[nest("/user/:user_id")] - // Everything inside the nest has the added parameter `user_id: usize` - // UserFrame is a layout component that will receive the `user_id: usize` parameter - #[layout(UserFrame)] - #[route("/:dynamic?:query")] - Route1 { - // The type is taken from the first instance of the dynamic parameter - user_id: usize, - dynamic: usize, - query: String, - }, - #[route("/hello_world")] - // You can opt out of the layout by using the `!` prefix - #[layout(!UserFrame)] - Route2 { user_id: usize }, - #[end_layout] - #[end_nest] - #[end_nest] - #[redirect("/:id/user", |id: usize| Route::Route3 { dynamic: id.to_string()})] - - #[route("/:dynamic")] - Route3 { dynamic: String }, -} diff --git a/example-projects/manganis-test-package/Cargo.toml b/example-projects/manganis-test-package/Cargo.toml deleted file mode 100644 index 3e8baeba20..0000000000 --- a/example-projects/manganis-test-package/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "test-package" -version = "0.2.1" -edition = "2021" -publish = false - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -manganis = { workspace = true } -test-package-dependency = { path = "./test-package-dependency", version = "0.2.1" } diff --git a/example-projects/manganis-test-package/assets/asset.txt b/example-projects/manganis-test-package/assets/asset.txt deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/example-projects/manganis-test-package/assets/data.json b/example-projects/manganis-test-package/assets/data.json deleted file mode 100644 index 0a9f9ef376..0000000000 --- a/example-projects/manganis-test-package/assets/data.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "hello": "world", - "foo": { - "bar": "baz" - } -} \ No newline at end of file diff --git a/example-projects/manganis-test-package/assets/script.js b/example-projects/manganis-test-package/assets/script.js deleted file mode 100644 index 2c9e898449..0000000000 --- a/example-projects/manganis-test-package/assets/script.js +++ /dev/null @@ -1,3 +0,0 @@ -export function hello() { - console.log("Hello world!"); -} \ No newline at end of file diff --git a/example-projects/manganis-test-package/assets/test.mp4 b/example-projects/manganis-test-package/assets/test.mp4 deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/example-projects/manganis-test-package/src/main.rs b/example-projects/manganis-test-package/src/main.rs deleted file mode 100644 index 46683870ff..0000000000 --- a/example-projects/manganis-test-package/src/main.rs +++ /dev/null @@ -1,41 +0,0 @@ -// The assets must be configured with the [CLI](cli-support/examples/cli.rs) before this example can be run. - -use std::path::PathBuf; - -use test_package_dependency::*; - -// const TEXT_FILE: &str = manganis::asset!("/test-package-dependency/src/asset.txt"); -// const VIDEO_FILE: &str = manganis::mg!("/assets/test.mp4"); - -// const ALL_ASSETS: &[&str] = &[ -// VIDEO_FILE, -// TEXT_FILE, -// TEXT_ASSET, -// IMAGE_ASSET, -// HTML_ASSET, -// CSS_ASSET, -// PNG_ASSET, -// RESIZED_PNG_ASSET.path(), -// JPEG_ASSET.path(), -// RESIZED_JPEG_ASSET.path(), -// AVIF_ASSET.path(), -// RESIZED_AVIF_ASSET.path(), -// WEBP_ASSET.path(), -// RESIZED_WEBP_ASSET.path(), -// ROBOTO_FONT, -// COMFORTAA_FONT, -// ROBOTO_FONT_LIGHT_FONT, -// SCRIPT, -// DATA, -// FOLDER, -// ]; - -fn main() { - todo!() - // // Make sure the macro paths match with the paths that actually exist - // for path in ALL_ASSETS { - // let path = PathBuf::from(path); - // println!("{:?}", path); - // assert!(!external_paths_should_exist || path.exists()); - // } -} diff --git a/example-projects/manganis-test-package/test-package-dependency/Cargo.toml b/example-projects/manganis-test-package/test-package-dependency/Cargo.toml deleted file mode 100644 index 3114a601a1..0000000000 --- a/example-projects/manganis-test-package/test-package-dependency/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "test-package-dependency" -version = "0.2.1" -edition = "2021" -publish = false - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -manganis = { workspace = true } -test-package-nested-dependency = { path = "../test-package-nested-dependency" } diff --git a/example-projects/manganis-test-package/test-package-dependency/src/asset.txt b/example-projects/manganis-test-package/test-package-dependency/src/asset.txt deleted file mode 100644 index 793aa682b0..0000000000 --- a/example-projects/manganis-test-package/test-package-dependency/src/asset.txt +++ /dev/null @@ -1 +0,0 @@ -This is a test \ No newline at end of file diff --git a/example-projects/manganis-test-package/test-package-dependency/src/file.rs b/example-projects/manganis-test-package/test-package-dependency/src/file.rs deleted file mode 100644 index 8a4ca522a7..0000000000 --- a/example-projects/manganis-test-package/test-package-dependency/src/file.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub static FORCE_IMPORT: u32 = 0; - -// const _: &str = manganis::classes!("flex flex-col p-5"); -// pub const TEXT_ASSET: &str = manganis::asset!("./src/asset.txt"); -// pub const IMAGE_ASSET: &str = -// manganis::asset!("https://rustacean.net/assets/rustacean-flat-happy.png"); -// pub const HTML_ASSET: &str = manganis::asset!("https://github.com/DioxusLabs/dioxus"); diff --git a/example-projects/manganis-test-package/test-package-dependency/src/lib.rs b/example-projects/manganis-test-package/test-package-dependency/src/lib.rs deleted file mode 100644 index c311568a33..0000000000 --- a/example-projects/manganis-test-package/test-package-dependency/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -mod file; -pub use file::*; -pub use test_package_nested_dependency::*; - -// const _: &str = manganis::classes!("flex flex-col p-1"); -// const _: &str = manganis::classes!("flex flex-col p-2"); -// const _: &str = manganis::classes!("flex flex-col p-3"); -// const _: &str = manganis::classes!("flex flex-col p-4"); diff --git a/example-projects/manganis-test-package/test-package-nested-dependency/Cargo.toml b/example-projects/manganis-test-package/test-package-nested-dependency/Cargo.toml deleted file mode 100644 index 8fe24b81a6..0000000000 --- a/example-projects/manganis-test-package/test-package-nested-dependency/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "test-package-nested-dependency" -version = "0.2.1" -edition = "2021" -publish = false - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -manganis = { workspace = true } - diff --git a/example-projects/manganis-test-package/test-package-nested-dependency/all_the_assets/data.json b/example-projects/manganis-test-package/test-package-nested-dependency/all_the_assets/data.json deleted file mode 100644 index 0a9f9ef376..0000000000 --- a/example-projects/manganis-test-package/test-package-nested-dependency/all_the_assets/data.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "hello": "world", - "foo": { - "bar": "baz" - } -} \ No newline at end of file diff --git a/example-projects/manganis-test-package/test-package-nested-dependency/all_the_assets/rustacean-flat-gesture.png b/example-projects/manganis-test-package/test-package-nested-dependency/all_the_assets/rustacean-flat-gesture.png deleted file mode 100644 index 866412dc60dda68a204978cb15726b22c28567d7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49537 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 F{{ZTl;J^R? diff --git a/example-projects/manganis-test-package/test-package-nested-dependency/all_the_assets/script.js b/example-projects/manganis-test-package/test-package-nested-dependency/all_the_assets/script.js deleted file mode 100644 index dfb90b8d27..0000000000 --- a/example-projects/manganis-test-package/test-package-nested-dependency/all_the_assets/script.js +++ /dev/null @@ -1,7 +0,0 @@ -export function hello() { - console.log("Hello world!"); - local(); -} -function local() { - console.log("minify this") -} \ No newline at end of file diff --git a/example-projects/manganis-test-package/test-package-nested-dependency/all_the_assets/style.css b/example-projects/manganis-test-package/test-package-nested-dependency/all_the_assets/style.css deleted file mode 100644 index fbcfd084c1..0000000000 --- a/example-projects/manganis-test-package/test-package-nested-dependency/all_the_assets/style.css +++ /dev/null @@ -1,8 +0,0 @@ -.foo { - color: red; -} - -.bar { - color: red; - width: calc(1px + 1px); -} \ No newline at end of file diff --git a/example-projects/manganis-test-package/test-package-nested-dependency/src/file.rs b/example-projects/manganis-test-package/test-package-nested-dependency/src/file.rs deleted file mode 100644 index 0bf083b4a4..0000000000 --- a/example-projects/manganis-test-package/test-package-nested-dependency/src/file.rs +++ /dev/null @@ -1,31 +0,0 @@ -use manganis::{asset, Asset, ImageAsset, ImageType}; - -// const _: &str = manganis::classes!("flex flex-row p-4"); -// pub const CSS_ASSET: &str = asset!(file("/all_the_assets/style.css")); -// pub const PNG_ASSET: &str = asset!(file("/all_the_assets/rustacean-flat-gesture.png")); -// pub const RESIZED_PNG_ASSET: ImageAsset = -// asset!(image("/all_the_assets/rustacean-flat-gesture.png").size(52, 52)); - -// pub const JPEG_ASSET: ImageAsset = -// asset!(image("/all_the_assets/rustacean-flat-gesture.png").format(ImageType::Jpg)); - -// pub const RESIZED_JPEG_ASSET: ImageAsset = -// asset!(image("/all_the_assets/rustacean-flat-gesture.png") -// .format(ImageType::Jpg) -// .size(52, 52)); - -// pub const AVIF_ASSET: ImageAsset = -// asset!(image("/all_the_assets/rustacean-flat-gesture.png").format(ImageType::Avif)); - -// pub const RESIZED_AVIF_ASSET: ImageAsset = -// asset!(image("/all_the_assets/rustacean-flat-gesture.png") -// .format(ImageType::Avif) -// .size(52, 52)); - -// pub const WEBP_ASSET: ImageAsset = -// asset!(image("/all_the_assets/rustacean-flat-gesture.png").format(ImageType::Webp)); - -// pub const RESIZED_WEBP_ASSET: ImageAsset = -// asset!(image("/all_the_assets/rustacean-flat-gesture.png") -// .format(ImageType::Webp) -// .size(52, 52)); diff --git a/example-projects/manganis-test-package/test-package-nested-dependency/src/folder.rs b/example-projects/manganis-test-package/test-package-nested-dependency/src/folder.rs deleted file mode 100644 index d6c1a716d1..0000000000 --- a/example-projects/manganis-test-package/test-package-nested-dependency/src/folder.rs +++ /dev/null @@ -1,3 +0,0 @@ -use manganis::Asset; - -pub const FOLDER: Asset = manganis::asset!("/all_the_assets"); diff --git a/example-projects/manganis-test-package/test-package-nested-dependency/src/font.rs b/example-projects/manganis-test-package/test-package-nested-dependency/src/font.rs deleted file mode 100644 index bc28c77dc4..0000000000 --- a/example-projects/manganis-test-package/test-package-nested-dependency/src/font.rs +++ /dev/null @@ -1,16 +0,0 @@ -use manganis::Asset; - -// pub const ROBOTO_FONT: Asset = manganis::asset!(font() -// .families(["Roboto"]) -// .weights([400]) -// .text("hello world")); - -// pub const COMFORTAA_FONT: Asset = manganis::asset!(font() -// .families(["Comfortaa"]) -// .weights([400]) -// .text("hello world")); - -// pub const ROBOTO_FONT_LIGHT_FONT: Asset = manganis::asset!(font() -// .families(["Roboto"]) -// .weights([200]) -// .text("hello world")); diff --git a/example-projects/manganis-test-package/test-package-nested-dependency/src/js.rs b/example-projects/manganis-test-package/test-package-nested-dependency/src/js.rs deleted file mode 100644 index f6f4c9779d..0000000000 --- a/example-projects/manganis-test-package/test-package-nested-dependency/src/js.rs +++ /dev/null @@ -1,4 +0,0 @@ -use manganis::Asset; - -pub const SCRIPT: Asset = manganis::asset!("/all_the_assets/script.js"); -pub const DATA: Asset = manganis::asset!("/all_the_assets/data.json"); diff --git a/example-projects/manganis-test-package/test-package-nested-dependency/src/lib.rs b/example-projects/manganis-test-package/test-package-nested-dependency/src/lib.rs deleted file mode 100644 index 5be1c68cf6..0000000000 --- a/example-projects/manganis-test-package/test-package-nested-dependency/src/lib.rs +++ /dev/null @@ -1,16 +0,0 @@ -mod file; -pub use file::*; - -mod font; -pub use font::*; - -mod js; -pub use js::*; - -mod folder; -pub use folder::*; - -// const _: &str = manganis::classes!("flex flex-row p-1"); -// const _: &str = manganis::classes!("flex flex-row p-2"); -// const _: &str = manganis::classes!("flex flex-row p-3"); -// const _: &str = manganis::classes!("flex flex-row p-4"); diff --git a/example-projects/pwa/Cargo.toml b/example-projects/pwa/Cargo.toml deleted file mode 100644 index bee204f20a..0000000000 --- a/example-projects/pwa/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "dioxus-pwa-example" -version = "0.1.0" -authors = ["Antonio Curavalea "] -edition = "2021" -publish = false - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -dioxus = { workspace = true, features = ["web"] } - -log = "0.4.6" - -# WebAssembly Debug -wasm-logger = "0.2.0" -console_error_panic_hook = "0.1.7" diff --git a/example-projects/pwa/Dioxus.toml b/example-projects/pwa/Dioxus.toml deleted file mode 100644 index a831a28ff7..0000000000 --- a/example-projects/pwa/Dioxus.toml +++ /dev/null @@ -1,27 +0,0 @@ -[application] - -# App (Project) Name -name = "dioxus-pwa-example" - -# Dioxus App Default Platform -# desktop, web, mobile, ssr -default_platform = "web" - -# `build` & `serve` dist path -out_dir = "dist" - -# resource (public) file folder -asset_dir = "public" - -[web.app] - -# HTML title tag content -title = "dioxus | ⛺" - -[web.watcher] - -# when watcher trigger, regenerate the `index.html` -reload_html = true - -# which files or dirs will be watcher monitoring -watch_path = ["src", "public"] diff --git a/example-projects/pwa/LICENSE b/example-projects/pwa/LICENSE deleted file mode 100644 index bcdd828e9c..0000000000 --- a/example-projects/pwa/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2022 Dioxus - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/example-projects/pwa/README.md b/example-projects/pwa/README.md deleted file mode 100644 index d501df2126..0000000000 --- a/example-projects/pwa/README.md +++ /dev/null @@ -1,45 +0,0 @@ -# Dioxus PWA example - -This is a basic example of a progressive web app (PWA) using Dioxus and Dioxus CLI. -Currently PWA functionality requires the use of a service worker and manifest file, so this isn't 100% Rust yet. - -It is also very much usable as a template for your projects, if you're aiming to create a PWA. - -## Try the example - -Make sure you have Dioxus CLI installed (if you're unsure, run `cargo install dioxus-cli --locked`). - -You can run `dx serve` in this directory to start the web server locally, or run -`dx build --release` to build the project so you can deploy it on a separate web-server. - -## Project Structure - -``` -├── Cargo.toml -├── Dioxus.toml -├── index.html // Custom HTML is needed for this, to load the SW and manifest. -├── LICENSE -├── public -│ ├── favicon.ico -│ ├── logo_192.png -│ ├── logo_512.png -│ ├── manifest.json // The manifest file - edit this as you need to. -│ └── sw.js // The service worker - you must edit this for actual projects. -├── README.md -└── src - └── main.rs -``` - -## Resources - -If you're just getting started with PWAs, here are some useful resources: - -- [PWABuilder docs](https://docs.pwabuilder.com/#/) -- [MDN article on PWAs](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps) - -For service worker scripting (in JavaScript): - -- [Service worker guide from PWABuilder](https://docs.pwabuilder.com/#/home/sw-intro) -- [Service worker examples, also from PWABuilder](https://github.com/pwa-builder/pwabuilder-serviceworkers) - -If you want to stay as close to 100% Rust as possible, you can try using [wasi-worker](https://github.com/dunnock/wasi-worker) to replace the JS service worker file. The JSON manifest will still be required though. diff --git a/example-projects/pwa/index.html b/example-projects/pwa/index.html deleted file mode 100644 index 44bfa59cc4..0000000000 --- a/example-projects/pwa/index.html +++ /dev/null @@ -1,30 +0,0 @@ - - - - {app_title} - - - - - - {style_include} - - -
      - - {script_include} - - \ No newline at end of file diff --git a/example-projects/pwa/public/favicon.ico b/example-projects/pwa/public/favicon.ico deleted file mode 100644 index b11015bdbacdb3b99e03555edb85d78467b37c7d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23104 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 W+`s-{ME)24jHW23DqAIO8vH-~t)grI diff --git a/example-projects/pwa/public/logo_192.png b/example-projects/pwa/public/logo_192.png deleted file mode 100644 index 112eb5ee49ca4e69d742a99269d1ae08a970dd7a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11198 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 diff --git a/example-projects/pwa/public/logo_512.png b/example-projects/pwa/public/logo_512.png deleted file mode 100644 index 43129772a892a5ca82dfdc8bb4d9027cc830ddf5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 48151 zcmeEtWmj8W7j1B-xKk)rT#HkzSaEmvB7p}jZpEz>FPh@+?(Wi}#ex)IjD$nRK6|gMIoDirVl~wj@vy0|0RRBrMajI7G(A6lQXVza2tcLUQv^*F~E9;rku265ykpjPB>P zGnj9;oq_SHA3VRsN9^yxq!vhb5)@H9JFgajfx8;YXi2>;U@5U`^Xhdy2DQ8Ovly=Z zhGEvbK;MK`k!YqN zk8ARyw*5@JQpm@p%eWGwYJBO~n2i89ldy&cKCt$OaJNy>0bFRY+zCWb6Pkh>e$R%5 z{`kJP0pQB`Z$$J9PT^)EFiZG~bKjv6>kRb{nRp1^mnS~ha;o;q5cwbs&63d3&1NY3ecKFkcE__iH@u{`D)Jx7ciNE2kOsy zEz_)&_J!o}^p#_TS+GqUH>+T?h!7(HQV@;|clJ1D1!@WtXo!h$iR$tX@iN9!2#e za_--j^2m}>9xzpCu!7?P0rf4_Z(pb9!G-Vvi}aYwQNtf&@Q`&~f8Rp^{p*$1^Afq-MHq_LSTseR8 z>fw?@l^ZyEa`?M8x91a*YW&Q9#WjbpW_<6Yjktv)2_dfn!SC6*g>{DBGa&;&QQXm! zMe-z6x5V_k@Z}=p^a`ne`9~U^*jRbsk*&x9PovP)jvuwUbb@6RBVZ_{BIO5e1c0*S z^rz4sO6Uv_HtJh*ZNbsb$4)1L)KFNYdEdYOXL&170hQ4yR9dgbJcpE*p+MD$4$Ylm z#k{h+W)K6AlNTVbmckDpV2cR-`7oSqbqG16=}{}9K_ZBE+E%FIKcp(@79&+(BE|P+ zBzkapi#YKa`p|b_?83M#WE-4t&4#;~=RY;OYaUfapFy`mZx~~cst78Hj>@?rbec6c z=vRK^iDrtvqp2~lG`U3Tw_gF)$DWZ!{^MO-;|E*wA z>-Yq#;?7??^6@W{M@5btuZR+}9;gTx(_7(r=T=gyl;^~JN!YLd&s4P(8KeefB8QVR z|HUD1ZneQa41MeOm;pL7{E6V_nZE||w)JjlkW}joT9cFkCig?+LEp>N<7GhH*B=2U zNv{Ax;@-}RBOSO4JKqB`=-A5GKrl|Q6%e#NJp-<4r zynmcAg!ATX;HEaDun?%f@bVg_^qFohk&tZaumS^Pksva?HaY;=sP?-mBIYX ztl11wD8n2di~OcBfLFFalr)ah((a7{lbj%RimcKW3sH6z8eA157xsqmjeQVfVG8{= z?zi}_rIGfq4bj67pTbuP|5GQ*Vm7HNT6# z1?E+1F(GzG%rRk1v^`FcOUPe#GuLW{-L8LtpeO`^=9sw|V%3kQHy5CwG`fT?gg3s% zT7PQ&P%Fw2M_|B|pf=J>hVsKUN6ODlVAaF~vVJ;52$08BcEM{|RUVlKfZ*V{)1Fo{&B4|K7W7KfV+yN!r`}w zZBtBRkhTg%4ra`5A~03hx-tw@Pe4`89xJUT$3B!8*4oojl^D~$sw9-1)j|kxP_TZs z380J>KF`P~+vl4~Yc5DB3)02lwMH;i4t_#~X3g97jbB;PVqDWFL|QGaKhLjQE9CX$ zNF5kQ8B}aI{|uUfQ5A^7s5GsH5nmn&DBXpnp`!BOlDysb=}@@SXp8#KKjd~9zg`IY zWj2HPL()%(f*XYoe$3c?dz_>6izPAVtOlZpW&4H5-EU2D()L$1h* z_8T(*LK)2>?K-Ur#e+!2gn;{%CTQf_WphmUWpdlIR(VQ3`nRuCkH^pysk^Q_<++iH zgMWgi7wJ{+)fY*Z}ez88&x?aoU_D^}SGBN5+A&A%mA%d>wd6HpCzsx^oXB{K= z?BoMn)>URQb#Rp!gvy=M7L}kW0XG77nNpAOk8Q9bh@h7^BQcgAwQOd8%(EhjE>G*i zn(jBIWhG1VeMZNqE|l0K_qCzh`>PeJ&)G4kb2shj` z#_umI^U~M8s*dM-Km5mK^K|@{mqC{)W|X;B)zP5@wCe6AXvZNYVKeGs75`bToV zb}*&u&JEiowzq%c3msW4D*zOtNNuQD)O{j9?z=O1QHkF#4k>T=YK>I>6&>GNS0ro^ zS)Yn42gV(Eq5x?j-?MwzWo(zl?{5Lpv2s{yl$c+TL98ikbBRY8IU#!eY(~b8!UpH% z-{Pw8pEY=jsW;^yc>ic^g?hH8hLiO?9Vx;bgW8KwTwCu_jwy{k7yH{u&3`1xOOYR1 ziW_b1*nNd~W5AT-0qdq_Ffvg*PHw-6j!$is(GKWTuY@tX#bmgV$(VVS8I+gUXZra_ zNzO2FSY#DP(iTt-rh*6T1*_XjIv7t6)@Vs;byzD1b)UD_Gxq=ar~oHg;A1t4_R}W5 z*2YM}28lGij0kX7nG18M2lUm*#?uCcl={K>D?%Ryc(RSsBRBr|GI#$j3H?iyK`_CT z7GNY!n{U!^I|~cvZbY<)sfgZz!CjY2?uW@iO;wz|dXxHv*!iNj2Q!{+ajneyzj)O3 zQcvQZ!TweIj~V;z>t%b)q9&ZS7|RIhA`$u+X;r@}@7vLkDpQ4?8kO4KO%Hl5+*(7r zr)UFp6ij88H2ob%@+jaNFB~goVVW4vyHQb_Fd~=8whMPmdIPn9Y##&d6*byVw(~Xf zfsmy~1oLpzs^5z1?ttnbTGXdjE>YSp&|t_3sh+z`OgvE>tAeZh4Eg7+#J4}P>aOmH z3pGI|6xgI#@@8qGnQ`X^#A*@fY_0_4@f ze=K|(y^d7J9$1Oi*27q$O-;`)-7<}`y1W23UIq{$QEPtT<4(|Wo`N6pNN%CGKg|Xt zwiO^(U>m(@#u*AMZT9#u!n$B3Gs2oWRFqOByq5FJ9m`UPwRwHEd40z0JDNhG%Q9I& z-T#0wSaS71JvQ|-^#1uxv{KT-Yguwf%_KEoOYyF7Aq~1F_4W^bfV&W98B+fkV5_tg zHy(ve1h)#!RGEwCPBwv+;j0{)K1o@QFo0sxqb$#SXYE{7$9>l2l=|!W%s-ulH)WF_ zF*P7Meob>^rAloxRxf1VW#CPMWb>0=b_f$sHW!a~be9p}CSXF%rO9@UOjG~WNFA|s zzoBLlI#b;^%W4{l63)>L_PLjbFlGWB((2?t8wrk1*#uOap{BCq`cuvitmzGwNAn>u zti!?DsDfnVUr4mV<2l+nhR2Y$eT4}#fug%NJaoopl}wOV(~+yJ)*C1YSwa3e zPrkG9?*@9++;s&G5x9J=PLKTQ{uoQ=eKZ7G(EzDg8X*>7ITIy=5n$8a z9!C{XO=^AL%_SR(3_6@%QVx5x55~dqb1ViYs6w@c-B2O~szjkDz0|)e1CLtuybM<8 zYDRLG_Juz6MJ{mjGa&WXpEcmf#wll`Xfl!VB9zA5Izw#+zP=Kvzq*p1yT#poPOLPQ z@$rnV%b|7I(FwWKnZ9;ege)b=%XJ$m0{&G;tt)gKIFwZ?u8m%HhPaOJkVD_J$aG*A zt`){`lp9rHANiRKU!I?^5+e(w^YdLg*}-NOKt z%Ym_Yc-=gQW5CupX9FZo(^;iT*EknSo6eX71weg9oC}w{E=b9VL<^1lOF6}53!s~8 z{8Khgy^-6Gyl-WZJ_2Pb>Ypgzz>Z%{U*?-(jRpVtoe0doXF*7$D#KTRw5BnE@2OQs z(98xaj1=p4MB7mYN;W^|(;`E6l{1+KtJ>+3ME8ez&_oz{{g>hG+(976DHPBh< zu^C9LWX)Z_8Bnjv(52!}6;`QHu+uUYH ze{irXETDbw{~5F$f^vFT0F&(qc64E9o)aT^I%M2kDz5oCx82=g~hMs zw#TY%S2HnO9WG4)L{KX?QM44&T9I;zqi3sc|K6eEu>Kg%9$}^jEe&pN z6a9CKqvqwgE?4O{)U5J92eQxx9aD;_tXx9*3|6MyEU*>awPeky8S4byr~W>HAGg5k zXC%ldc&Ay|G-{_YwHKXDeiU!;5;<>hdLA~cI}5*}v#cH2`?(?gGc^31d}U7Rl51aU zX(x_b$goh`1fhK}_ns7lZ=*Cd`AB{~VTR)DHNNh5pF_Z+8g&F)H^GL}T{Q}Arf?c1 zRE@N+iu8$;V+z|-gR7WBy0d9l5zvZ)SX!O~2Nyy}^J$Gajx0>RGSMvOE-t41$ zhCPqu#e6F${D0S;y585yD*~TuS8x$AtC0A?onF>kg*J$#|D3RgcK<5Gv{}ru>Vt@f zxOozV8BU7BE9x!zIVo^xNOgigue_#Kr4^Yx38`9=)(49c``);LK>&+uH+5GpXm?xa zKfY=13-0B|1>|}*XNtdLA*0AKmutar8SIe*qj&c5eU1NSrBzs*i>`#>_B0l-WwNuT z0m2uqPAFD=TBmbBNQTnEAErq3%3&^Xd=h9gxpx9(9FaDZM>N2w-+N=mm;@AbmTnjb zT^OfNHffV0PR8xcdWCkk)r^UYu&Fy*2O+`@-(mizrAlbP_Kix2mR>$eB)R|_QB(^4 z%|~MAVCZhbyr(2+BgR^f3{3TUK^7Uo^Ej_A2KHul$z$~Bvv=x0kFCE(a6M^D6q0m? z*enf%Kmn&8c{E{TRN9Wf(%s7Rg22$#663v#1!3mX3wWJZ)@a9#Ylo*$^(qIO7GpOd zhhrbJ-*B6k_1_KB{F;H=%}wuE4>B=E0uPQxecsFL^yNWZy>xp1XR;qHjr~1Idy|j& z;`eh}HFLxYv~miZay+R>f|wIkYK;d>KJ!DiY=4H3Vx$qy4S&>DVLrB?~d;PSdecW!(N5cD*tz1c`N-x0g*PgrfS zgI*(my@<$Hn+|E_t*n-Lg?gk3YLQWNsi`-IsqM4)N`#o|1V85Jpl<37s;eAl#-k}z zOE=z=u|l@=INe-KV)>^mu$I}M@#3FVqtUvGa#23cZizUEu~Oiq#j#Dnft}T~Bcdgn zGq;ob=6r46J7;YhDOp&%+rA-OV_sKXbUO}xT~U~i5G?UXw4>-7urX+!0R6Ob7IpNW6xzk2Hd{a98H@)bR3TmNTd9R zG-nQE-JMJgWD?tm*R*Y+c=oXz-rpugmRHq2yZt5OD9FJXWT+Ct5UaSZlz8oBQcyQ- zAz{-PHZ^rMo`0{GW+PuaPCT>FP$9B@JzKfu>uCmYLuI-gEk#ZUmB&)czO~>>s??)u zm5BP0<8Q4jjlxK~JsIi4cR#&bsPkLbJFcfK{GUN8HuwL6rSJ`AheA3qHJ*WVDSkg5 z7;k;&{JC8omTWHH1EYo4j?_&QZ778Y&N@+R0x9`31 zro*uD{$BRV#&^qQtEa&9L1#+VhVqj}> zDL!0t92rDQWhBz@jXU3!yk}^0BN^Z#fI!)#B-Fyvg(&}#Mip|5Vo)wLOUNwHet;-R zZ{abI5`Af)GSPA#k|_a=&Gu~6J#Uc5{eeG$8-W>Ca)A&C3<(MC-ZQC5J`x`EWa>4z zl$vcU(ICK1wHJokkbW9hRClZz{i?$l$Fj-$xz4dIyz&l(v5L6CDEL2SHzZhO@Q#$! zdO`v9<|MkAVJ=r2cJF8Z{*i?y&bhodDs`cE;AfsySvlNMISR_R8ouuPXXZ)0&U1TU znC8`y3pZE8&GY_L0I7}8VML-3`9f&0jlIXc;cZ5P@@H~ud|UQ()kxLIX|u5pk*1Nd zXl3>K+15=SaZ`F29VeB9#$C=bI7dfPJqCE?qyxPv)ki*wSB4AFlMh>>mnsj%=Ze>GJS| z%r$#T!WQnY>0a&_Q)u#YbFpW_l<9@X!p=~wg^^nXSMm#V)G#7j(IdGy;p05=}8?YY2+|NOohBf#=5b$b_ zk8jgD&^~)vW_=#ic=56n$Eh=Op*GoIjA>f-BQ~<8#fk$#n{e>aXxvh8@{WUOk(89jV+t9p*ZB^X~rB>VOmmI9J6-ejpXyuIeT~IqfLtr_%F3{ zQ$yRLa3z07)mYvZOV7^w{%M8BZ0(3FfZ8#Hi()o_SpBWNJwt07#f~OOOlZT!qgV_M z3O;D}sK_Y@0P>l|)D#P(|7`cAkpG9eQHSNZ0;shgiB7$U2|8eALWL>)V|fzF&YgxG zy9JU9fz>(c=tUv;cOqZc>VJhFv?07l#esj4KV%HXYovq!`w4NCWEWeRwxP?8!9>l_ zAr~1_B^p;toTq7KP@9~TE-K}fU7SuO6lg+)!i10uy#Dh;gpT` zf6W>Cu^i-B;qfF3P%Q;EZ{PM+3(k}8#V}ZZP4~64w&{Nv@TZ2fQPgGwq0YQ5^!X6~ zxut6x3u6Y}!N5Mu>TzBAjWkpb-iBXtyz5f>!OVnl1aD`hLx{m0%1ErRpXwO79N?0@eAi_27M?Q8= zD^KOI!u(r*UzxZ2zpR3LsC%IVO(9P>OL^Ynw3K(0DQ4&={Kkv`V=_31WBz;0`LA>0 zpWn;VAK`1IIY zf~L=0wde}U)`Mf=hY(}QyHn9K<;DAGS$#p8i$KEkcFoIbuLfq}gbgg}K zZNxv};gN{T=1Hlu@t475Ksb>nvOA8Of|U1}AxEFSZByo(DhYSSZmkbv0MQ77ll%L%9JI3(=NI&vb>EN&fCoq-~~6ivbgU_z6F(~ zg6-{PIikAd^WR0uPAq#RikyD=A~}H(!<>mBtzv?_LU~dd-y1Cnbpld~u2(WA=jW!0QI_qa@o>xFKeoXbQp0%hrCsr4to(1qXqLe z%_IFv$z$+;K#+y$bZ#q$zSk$iTgX~TDy;>sqsc#5@%aXyyPOlRjiFcFbX}7?+6}g= zlL$A+@H12)v9?(g^VY_3*egM!e|Lmr)WLl0#(FuL4X~fp2N-ucN^nEhM?WwyKF8K= zR=^(x45*NW1sOa8xOf8+)7D>-zFDo9lT2E3cstCtU3+et8aS{oGcn$N z(T86l>qk-kZcX4G!;8e*d$80{AjjU}%esp@R8EADqJ!<7hl$`wT@L}CI}iqcbg>(k#<5DkGdy&DZ6dF1?fZdaXGtX+US zD%@E?-EST7k!r6EfG`cYVQx@%$X#Ex$Lohf_^CqMTtJQ*%l<~+^PhNhUT}b-=anYo zYDbav-ALFOR$@X9Vb|+gHyzb6Y|qNv5G&EH=RePdu;^FWRj@v~J8-YU37!;@DN@s+ z<$T0G2cnCRQIS_xF6;~PUr-Q+MCqUn5RB|4sL_^cL*||79FCP~Q_yr3D-;IDjZM%l zU(Eh{xN_Q>&bu)AxcQQ&*Cimt50ocL`AI=;vX}Nlla^#s*>-_skOi$k$T} zz>AGQnpw3g;qy-^tR{!4>qix~hzruB-aTBX+j~_bJTTIMSjZF^Gr<=<7XpG&4v{u; zo3e%-A_m&mPf;`mtk|X$EILoN{ag@8x99H3ov(!1tqPS< z9y>=0ZI9^MWnxNs+@+D``dOO>ugGr6U!Pr#Ge9051>9bQKj-8lC@!aj-wLc1?*miBNKs@ z_dbXaVynq25R2EP1tON3Pts)8%)|b`eaScm zE&4*9PgdZ^8s;5a7W=Lq2-X9}1;Qle_;m$dc9YFOqN33aQ{|`O)g`^jzi2fkI=(P~ zKjhNbL1t*LnrHSsxqRr@dEjHOH;>5t-6Gb>M>x&E5_R>BO5 zn*&~-e0to;B>=xa{Dpzfsxf-Ptc_5Q?AUkiidxDSbhq-7o%2#t@U}dCBF*DwD-l?6 z&jQmP<6~hfpgY?gdkw=4@t0bX3Ybfx6?fiw8U7xTlAf0Waw;_Cd$N-W4UW970UF*3 z`_})OyWRIZfBsXUFSMSZYK()+pb6v9=zFyIog*7?$cxhm1ZlewW3gQ!=g>Up=30>MhyeQ6b36ZE<4D!N36d zt+yoNQ!rH}`Dn67MV+m7G+ zjGMJH6i0-`q)=g~xWr0YG^{{~RAvIvX}Yubsr2Jn*Hbp7{P$CYEk-Xl*EROoBJ6!50uw<4YNUQ-L$G-zC5KLA!>w zSaQqS4wT%|k;eQb2vC1pAt)041tqq$OW|%t)W-*^I$9BR_x()>5+^|n5oL)}T0=h{ zC5dX}{?9nzBTlK#FP{T@9`EbS42%m^W5?6whWYnbf$=7@_d=b1TX?NY;JK~LQ{R<2 zi$JGyRZ-9Gsq8(^?&~3P_ebVNuh&Z~qr)+j4V?gh;c$YpcE^Mui%SM z@3h>InPk&{&%sV9$3AHa=l%U|hfgqz_gbeXgGWKf{GspceJwCkV*Og&HVGL4LI;Cc z!~jF3D1)m@y|lwE@@$W)cFk8^2h2S&hQVRULj>Y=48m!;nfI}3ft3jt!)oVi7kcQi}>D7Gt?17y_m zfjH)Lj4?6sRmFP(H@>f>bFJfWbM6QX3K6XOYx(6i&BJc&lhO}SMz zYdMQKO(o}Q?$OW0L}JHjrIEzK{Q!3IB!Wmro^cnKN!&-Z1hw36{91qF-E7I$iKnIB zb^};ZqFV)~?J{sa^?UWvlc9N($|}-#esY*AcIhiknsqwi_V)DcwKz)uJKlQxGnwRh zC}3R3d9SA}4?)mPd&zYLb(<5tmxkz^*46t-z6C|&hvQ9Tqw4sl`^mFhkTu;`@C)b*E>gD zw7#rkVQFbP&*Y4}3?p)ON%k1NmMjskneoy5`bM_%C)XX2o(_-@NB-JG+_PNl!cQZf zGq0$}Bh1k~rQJFvRm^%w_}aOZiI5;7-ulWobzqS@+?<;9jT4vkl}8Ag6fJKj39GIl z7jCAGiU8)S8Hos&BDSvWf;?b_?47n_i@^O=htJEd$4RZ~C`%4Re2|dl{(2eF3DBkK zEs}V7^1Osg;@x-qO7}I%<>!I4*Ht|E84?&=G|>S?BAl$~nTr&vXM@EkOmp=S+$_}e zC(bBI>>yK0z`W;RATWEJ=6q0-K1T@4y(XbSr%~3sA@BJ3F!NVt5|^DP29-?y>dd+i zWVD`o697T~9YhdLz&$rOlDPwN(=-o$0|Kk@vXrh-FjmZ1y4n@xbiS$W>1rb*9VkUX z95M$_dWC(+Exp?fQo|xGV}w<2o!;{wqV82@O{y!j4gjeCm+kbE#|xBmd~M)Ak}2s8 z@;ysE42Wm_U8vb6;vN|6)9+Gu=?qu=N{2)t=tXC{rtwLnoFCCnGC1Iikd$fDLr31vsuQ+eULLn)_sRq5Q25Jti1oWR8UvhN zxpbR=rB&zCW@mpc0&V|T*G{A4k!J{ePoaa;c}oH)NS&b@uM{6dUnnH5AMqK;A3Wg! zcm}y;Z(Vaq>c(pA$39&mRSfvOn{C%p1uA_Yu>lh|l_AjlWpNYyth zjpqIK4593>;2m!6vI{w3PadP8vk&5_OMSQfUoNVRga4?+Q*WxeMIgS=#qQ7E1#W2C zx(1qgHuGIdUEcX3l#I$Om_rl^zrC{6gufKZ$O#Fl`5MpxQp;9@F>;^)%Vw;&T>=kM zey(JXnWQl4msm=4Muuv!{bA*k*V(80bCkA!ubYDMlyN-|VeWm1vAv%HJmGWiYDV*=+Ykn7g;*=3dv?4a00rGOG^p_J}tx>S=UmVE<)Cfr@wGX~UORB%k zyQMb(S?ZW6dAHBWRMwZfKYIv=+^kq!bgxc$-9C7Xnx&@jKu?i!PJ&K132y)1UWdSv z-^^JCc250DR~b&9#BOaE6a8Z!O1Y(DcGC}^iBp<3@Y z3^6$3g5;Iz8(cl&PRTpFCEOkKb$p!!Ju_`MTxWXUA9~`9V}v*q*AiP5G6fMls z!@JmuYSzAsEx$F2y2qG_`>-#^`s0h2HTr*OEa$|{29rI=ffd^+ zIzG>Fpba7s5@rISv0#85vTO=jXZtFTy4eNU+kg)9-v&5O1rHhEGWW^%I=v?aZL^3Y zolXC}`nRt71iq4U_gJ$|u zz+Y~96J7xnjI_-V3^r)?szoc$&hCX7y=C(q3#ZUVa>I_&9ejlok5iSD1Uw<(=I`mtNHlMO}+B~cNR=;|Ln+-H7z~na3y+qSI5XcwwSAaYKhi=n? z3)`Z1Fn|}eG{J9{${Z~ON$e-ZyaX&tmWNdFxIO5rPz3r0kH#9YU z`|00EAWX=sfkZkz#Oy`>+`GxW7%Z*CE+TI|s zCX!QV!oRq@Q%(ex3ms`dWD>bhLM6}$!+}*BQh-e+HmfZYdCF05basVoT%6Lu@bCX+ zwP1gvUTm!*#m>7!h^j%4lcY8Q-^$eEZk~e$$pZSJ3(1S? zaqjJl-^vx@#08H_x1MGA1AomzY*J_g=Dyw~r7Tu1QngvKi}PA5c$D=ETGR8Aq>DvN zm>Gx%rj{sWWPA)ei4sL-q~LJXasFU|ce+Zvf0l?diV{uxvZ)Znkmm7VVfDMBxA~p> zTrSYA>;q#}$ZiHY1~n3VD2)A3JopFa|cW7b=Gjnj?2>SDPLd4{>XpmzSJ9SLDvZx|dDs4xt(ai?$_*qnB(#N^p)UK{JyF=7E zeD)5d(}vl_2XoD|29nAx`Q3FB*XwWLLo>l~8$ZvGz?^0Nuw zy7*AuU{@0w+%}DOS-Y+frS7b9;xVZfT&eJzc7G$+mL^W1G<^32J2a8vC3A?N&a)loS=vqsren5*E4x-~`BP+wiuKld8GXgTy&8Ly?N&NbML{ zMMxYr^goJz{qqhPA;YX7!Dw;=6?XxVU7s6tBtk0FY26%vy@cO)Z8SJ-P`^3fv0jiD zJ&E4-BF5f7*Gseo16}+iyu_co;24rOiXL$;Df;&$R)GmmhH@k$F--kOcW`&_nLhM4 z9SusJ-v(}g;Xs^(|FoP3(0W>l%YKAcgS-4zKDYSJ*OzZ5R=s~SgukgQ`iD-%=%~z! zHefH8F+JhK$5~ZFO+jiCk`H0BElPQ<9%GT}rDGZ`SE-p|kAv@Z{(JeY5=2stju?;c z6+>*Rj$6g*0{KSla*&vIG0)aoF(OD9(Vp=MSAXbrQ7WFsis85^^NA2Mr4aj1-U*|} z)KqdB{f2y}P8zh~FcB&kE=WM}lUmz%cg>NJUwK4iwAzM+bCs6#EYTB@Ep+|xIJRKz zd;|aH%!nVk@XmXUlEh#Br$D;2dcM)j?8|b;*2#dvw!XmOf=2fWBI(=W2zqvy+>3@A zQUU;`BAaF7JnnEPajEY)gGRFDIqs-Ffx!2Xu-;ru?u79i;v4yXQHOx@#bTJxywRr| z3FmkYgjBy(wo9QU#j)D#RA8Ksjr-|OZ^$Tok;weRkX}f6Nn{jdthWB_*;m1Cry$B3 zb%hDGc_iucAZ`X~#)!;^9QaFc+}LuZ;q-aeww1%5B{yLo=1gHv@)ply0eA?znQxvw z-d>Zx{uqXqBuS7`Que9MoMrBK@mN^BADDw=0VY z)pRr%wTX6EJKPl{RL#sKzJJWl+e_2&7i|NYC+#`ble zZ-sB|>+ct4seFB&pfQX4yxx2C2S9&-w^n!X3rtFD>uJL$B-)e5oasn1x&iC9fqgcS zk^Aq^+K17?(ZY{2A~rUaiB!bmZ~evbRk^``^HoH8u4;IKYHxKV6g)0O1_t*V4JgC( z(ktUUlblGd)Sw@F4~rm&nIR`*W2LVI-Wu!{|_68T4~pv|w5{XjirnwYmxovZ1sLKxP>4g^MNgB|`TXGP>Qx19?rHJRFHaZM*Rk_my87+5 zIkaOxUhueRIzMFw8~@zC2g;jxAN_qmks9O|;%Hk(`Z0k3=mizdkpX4$_{Psk_3%F2 zc*Y;6o=@jhN89_9TF1|0N`+XHUgp3)yCLQXFU{AlEsVu=bV*aVIpUiZzt5%Ok5(kx zEryGy6ygFD{C%KQ`&a!j^fVFJ6zlTdDcLyi!KcpP}V0M07okJU3ZUvv2WaBnCFHrSIFXb1E8X)V*U>Vv-JGCSYa!M zIA5yq_&yTzVX$7MDRVJk%GgHoo@kO>uJmEO+|9Xp#e+RwL2BoxXy0a_kAp*G0N2E? z)Dj^zXo{l@*DtG+u#}V_{o)gGxc87C{Cm~iQwKLbc8}$kOkacZsREJp9$eeLE01Vn z%kiN8^M6BHpX*%bc5bf=o@5^024!_^*jx-UOJ0rP3IzCQI=@EpA^4Iy6c~D5uyp57 ziBd@@!~2m0{H4-5!p?tFlskW>BM?ZqgcJhFL_8?gaNH641zKp$#YMQD|^@n2lwuOP!ud&BAXOf?jAn>tu$Vewt zI1G#)%qB9A=|k&;3ZY1z5obh9GN-`8Qlbj3;St3&$eLwhVCCCmotkHrc-XEkS6c8+ zKLu#N>A6RE0ug7NyFUBFo=^LlBwjB2bd9Ibc;2u7Sm}_?cwNM2{uYyQx7bGzaM@0U z6hjeSAI>L0@>nDHH`Mq;xvtL3i4;WV6K5musbRK7Un9?4y3FFu#Vwt1$fu%$d5-P? z#q93Sh)S5K9SAaC(2ns4Dtn~EUmg^w@_Wv_a8bXgLu(U~y}pPOd=;sHZ)~;kGz(vH z1Uc?_`DwZ%+FTNF@SJY}G*MtvMe*$q7mb?guG?4v#Itc_Z6l9>{w~P^7+yWn^HH+h z^Fy-vn}mGET0`s=`Z-^E?Q0bji%3iglQ#*z+H_N~L?7~T{Pas&9*BA_&~y#_&>u!@ zF2~%!M`V4#yy=GTY~a9h>{l9~ryPmyQu zWTv*QRZn+k+`q%b<=()HF)|OHY6X2OEL7!Q825Vw5#rXYK&CEt{beEq zzYiX3hY$A4%?^q?0XE+7&eIns;W0dWEOF`7(R+6hQuH%-+X27-;)iIgGTL8$Rh|kN1@krXLI91WBQ7_lUj>p7r>tPx=0O z1v`B|efCKI-#+7TdO-OiK9NFdOv0TxQk}i4CDU90?o-oESD6ww8;~G4?WwhXfYwkQ}<+uvWID_bpueP)zik9xzIUK|PQZal5^2 zwzLhqoewOu-p^qAYH!Q@Za?F0OEC9GTO%`^UZwaiRMV zsg++cpuqop0R4!Tx^U;j7TKX9p!?L*HTor#Im;?HOai%|puK$G}S!90tQ(J0RB(M}w(UY_g$?x5m(P$A^>AWSMJuc{NQGa`1 zzzC_?Ly+OqJC^0Sz6%ZM8mzbZ6z=v4;NY z3j)2bf?vqI+-cA%hEZ+@ii3|1{MWxA(g1|{fM7S6?C%y|W6^rqWnr42F*f}%i{f$n zn5rb!*JEq^d+Qn)-QQv?Rb>fOL9y~o7#Uexn}=N??XrCVNl?l8-Q7=yW}MM4dLV5R zIHj-&itbS?WVU71oX5BiL)aNfKuY%Yg7ZjQ!&}~lezz3&jC@m~F|K7hg**ang_}{+U zLc_5`KP>$>^}eb(lgH!lzRkNaYSnXo)YQLA2n|xw5<0&6mG$*d7K#Y1W zzTZFv-tpnqm440UElRHQ;XB45oGHrC4Zhb= zVE20C9#@I)?cNd+jYZadt=9V=Kq)!MjCdRmdh~VHT=eW|z&>|Rn5+d2$YV$gi&0~@4f2LYwF*~e4@gKaP@XdeaKC~F!N8%y~iU+ov< zLZ-!79SxO`A((x836q|MC9W(=5ap>j;{Y6rxRJ6~)_wvYZif&=*Z8a;A%8UA}Z zQeEr4*lXHePyWsT388y^CbMk@-}jict1^8}?%hdkaV9in!RjvabN#x620#Yc~BFcEf8PZrju$J zL|M=ZEHPN@J+^dRnXT+;BAmqF(7%XJIQyFich=S$miaDvps#(uL418oPM3X7r;%6t zG9n(56Y{XlB&91D^USrK&IriA*Z&J)htznU)56B)q&4*1<)H;wYO1d=z93a@Jomp&p zPEAVbZ64g9xpm6o#H*#?bl>M7UlKek^~g~sUI6<|u0My;Q!#9-(_pooRwDD5?Nrix zlzhL87`WS7-EwRKIJmqNbKBcP1FkV69~kGA$sytu+o14d(?duYOGX zQrv^xGm9Vh%=Vo)xqh!7t2Wn6rr7e{s$}i6NzT~a5P_&Q!c>sG@ zhoDfSIIlK2_%TFYmU2Z@ack!0*FNy&Cf)q+Dk7P%`obZN;T|a~?)ktB-&&su(d=nc zPpd8fdpY>Eh~JeLR&IDXOcy-nC{`o~(f+p&({)l*s_eoFKj2s)@voaCWrLZ=n?s3U z7YogqA<4fzHQXQ1(2V1!p4%+WkoH}7Ya{km#3Y+uU}LZS%=!+J!P;Ao;*l~tG1Zp^ z<|i?Pf4Y(uQ|HHrXO-*kFDi~|O~!`mvSVff7#O>1lx$IG7%M7lvbhn^wlIsV`4{V<=;oU`}7W8G`5eb9*}g|yWvu@IRr zrO(nWfLD@7d66FUm)8OAMzMpBLau4jyDS=d%hRJ@d)G!rM4(`;LE6_+phY@V%)HzR zXU%&$z3#EK=sD22e;Ab_L+Fsfy1{lW7mA|AUSrrkkyh)lh9yBH4)|lG)&V2UZ9jPHj_29Nu;;PrVKE zv6VOVp&Kmm+w3&gb-Lv5gONa7g2PHPXw77fyEfm}OlwZkd5w;HiFR{BR1{F5Bez%F zy>FEMBE*ow@TPykw}xJS1t^A_mq5d9t^S;K?y}+6`E9=;;K_|xm7&*&`+h<}UQt3> z|KLEcKc5Ae$@4jrWDw*L6Wn}uyKh6{xp`05;7Eev(pm#=Qjs&3Tqtgg1~9A`;<4R| zu?RJ)nnhfHj?><6MBghj-#@FH{N2^IHSB4pjckppJwXt^D7>;4e>sx6u!hg{r|3^l zSMD1yKJCK&_^#HIR^KNST+KWum@b)u??PJD_?Aj;js&!X&F);K&FI4hoEmSBhi03Ac`-bI4=L~{@$a-e}b@6y$} zbeoXJ=ca2PJ{3k|(Q<`-4 zCLTmo+s}m^kURE_{4RVZXn>zvkZ$_5FYF{ebIo!DjbkeJ)a~-y&K3Dy=O)HfZ~px@ zFH6PTeMCAq%a4W1SPAh{c%<=grEo16x=z%%oVO}MliB}xvMoVW=c~xa3q|ky_K@{b zll|^_?a1Beuyx`HyZqrPLe1^3BCV+r0xS~wWG@GPvYfEGu}rB7Y|<||B~s`tD(4gT zeJ(Yw2kq1Dhl!#UsdA+HY+qE>cZhfXg`?=_Rt|s^B*Nx(N@KVpF-^1}@94r*Rk+27pd_!dm zh!J!s^OB%0e}ZHW_9;pAZoVNH&JtbE3VA*rOf&|OF-^KSc?EgN0%@L!&p%a+mfdH& zP9zAnD*9&L{hKeePzuKQv!h6Anc`wAQE8R}S50pJ4v^VYug`Dg&P(GOCo@^(K<>>+ zOKc(>g*^OP)7wk9eqF0eHU1|FuFf^jjI9y_7x(Kpl|HC#u1W{3S3~cN@{GZ2kCLT= zW6n-e&%_g_tQd?%4ev^||GuHasE8hrj^-Jv!SSJ%LrKPG)E$_^uam<(?Ob+W42v8! zRZ0`pdrhTyu8&6Uzn^ri7?(1~I1YM&inQ9YSDscx@jagOsy-fXOSG5w2J(G$b>ZVx zWMt!0z_=WJsc{hgs1 z9(`u3o&l6zl=+9VaLwHP-JsW;Qlm!)`SQE`n(pK*+7q@*hKMnPI!Md(BhCpUYppe} z*Q98&S1Ur6?d2(gI^eg4*$S*W4ZnUXD z7O#O2v@V?wgYI2U!*j35bhkF2K8>O7!xZ7arjNCxYqS}&xlsz~V;KkHw;|%@v;;Cv zp>_V4-^rc0->;}=tT_yFoEq2Y(%{#B6PXeGYD}de$k7Tf51#9)}n_ zrVVWH3=6S>y5L^gSF!@Cxl-cXh_xFX?5(Uuuvm=Sf&d zU|bneruV`GE;}%l0#3KkMu_p^G#F-{-mEHhzo>VQ4L)E`HB!m3xoMAm70$A(NG!x7 zcxQ~!%~_l?myRX#E9wCj94LgYGBM}CvC!gvDL<9l;`+eXy^ISf$fKd6S~D!80@eqF z$^e>w-&#bNA)!}FfhctVU$KEjX#<^@y>XdX6F&=0>)fb0tSDWS)ZTw^>}Y;X06uqM zIC?Z1@oHn~|B0~ON`$!jXn8b;%uk|19y6%XFa@98XK7Mk7P}{QckkNlO zDiMx5+?s1|l$b_l(LNSUotxG_cAEQN%HG_Y%HT_682ExX_!Y}TPO%eIP!-A-U&tW0 zZsk>K)x&BvcHRDMsZxQh+4-4jDv$0$Tyo((7%{h8<;}hTBH4Z!^fX*T2vn(MDdC*(;9tsizy&x@-AiiHhr<-wRyB#fwrFoUOa67BUu3k&VIgIc~W&b64wAa$_2pq zue)xGjUK9m=3)I&(BG5 zn@;*L7!wh6ITrxTxE;2p#NBOc5pI&W?Ao$YjrmZ5Wa64ff$y*?{+csge|{QnH&v&| zbqz4?)II8DYZ{WiXUNIb0wcvYE-&c`(T#3Tbd?&P&(q%IVbw2e=d4&13KCV`*d4ce z6LLI+#A?a}a@~rilEXdyTiaa=Q>46{cmh4U%yTf}Cn_AF9-D3HVBbo49`+nh$G59M z!?wZpzeT8C=Rb_p^E#6(3v^mLzOFa7yHM~w`kY9jOUHm{ax8o3X{||4>5{_Mw>0h1N zR-#^GH*990#C2MZk}C!J<>nRJgVQ~V_w1hyNOe0Vty=;SNXr%q7w?ng4%*68XD5IdY{gciqk5|Q^Vw=`kX zaOk@W-p%Xg)Y078fl*w9%JYF0G^@UZFLw)t!YKB#MwaAaBvyxb=CVH$Ww>kr?-;K@ zr1Zyg_&BkWp77ZnwnTSU>p{&;9P{gk!3H&v>*VM82 z0jw4-*BFt3_kC{-GP!8W$C926dN;DEthGgy@P76z85zF$@O`@t3P1}B^hg+LIAxee z*%@jYg(P!_s~I2o9DVag_Q;5Tv~?{7qG0ZEcOgB?lBS~#jt++S13SEH4yb&_%fw7s z;?yT!@uE*avFa@mBc>-GEWs|yKH->mztTXg{dZeHB@v7&?dV zfM1Io%@g!y%S#(w`U`u7)>}Trs@kDFpV-0L7)?d3pQRY7R?@py$x~>*w`n8(ob8|Y z6@9s!i|~GtYj#y%K=N>JBV-@civ`0*kfiQ@^M|da>Q+g(r2I=B`^olu){8P9-r+M} z2!Tu2fLA4ojbcobv}+?o!l&H=BKZxO^)`H ze(^$KvfaYI?r)7BZuy>1De)FG3J5C?e!h$yO(_U%UYn)lBLR<3&o`HndPv+JHH!K{ z-hH9IAEMi}!`0lf(;Hk@hGxOtE9u0YIP>X8e&0f!RehwS2SSIr5{7b$yXWA5`3$&>PXe0=)_ z@&W-ADwBE8$bV6WqBKjuYsv)!u0_A?_@{ zi&4g$+qQAmviNND*|)G|!lB#zaLsYw%!Is!8JJvYKg+70m7`pP52Yp05BC!-G90hM z^3B%{_x(4#fHtGqiv$Y|pWO9wPgxj>K_sSC3<7=10MTpp8=Qm%pZvtdZxh^B@ghYg z`qPLRNCdoWv*%QUJ$I59xdm&}#R+@A*Nb6c*j`AssZw0Jo_1hIZu>M>$vMCqFs=TO z0gZLex!p>j0%{E7{yn~vgLb?$MYfB^O#g@rIY1E3uFA9Gg>KMut(U-VZOCzWGuf*H#YT?IHxe0fTbnS; z6#J`K>v3c#uslhPTA<^-d8h6C4sg0;!|zSPA7pu;&L>d^my@jSUoXNwlyg}ZBVaDL z|K=oW=TtCr5QxBhe&k>_>JyXinT1RQ);SQpqQp03i$Mw)CxnScfnlgH{Q;QUszZr5t`ZGj7; zPWXr|v6Ug_$y(O~FS=g?|21b}T11#<*fDD5vqDKr$n3mXb-KP5G(gNFX&M^G%;kBd z9dZ0;Tj8)vc$N7|H>iUEn6$b)-iP&1BCAP(B7aG3^(5Q=#X{L8lf{ui-BCoU9v4VO z$R#t9N!UpU1EOkL9~wgP{s&qL-*ki71k*5j+dgwKFVcgD!8*NT8py#z3rV8s@xF3Q zYQWivxC=21JHJ`N#F)p`j;jSJD1d*+H*O)tqA5n*bJJ` zq#c)Tk7livdBtNLEf*cFejUVzGN2Ky5nX6cBq|dj%U-xYkVRnHP{l1|A!>1!&*#%1*_7rF=4Dn0)b~Vp%6(4k-{VUP908bcAq*T!y z5wyo{n)CHn+U?ZCG^T^CZz)nx-E48Jv+3w9@Np!ue2B){A5~=a`b61esF@>|Y*CEx z9o0%v(FW;Vpk%>FyGkY5C(EsQ{`&yKHNatuhV9!ywP)P;^i)Zh7?U+VH^XkfEVAswz;Pz@x(#x+Pe-p7e^TP9~n5^WknB zWs_TeL{<8WEGM(<;Hz4rS_&P*SPH6BQ_d4q8|SazC$L>zEGHW8QrvC1SvJF#BE{5(xwrA<Oii?@Z_g%n{ET&fWrxZ+e>-i;mm%FU zDjT=cHd5*xXvK(%czCLMKZyf3)*gE=Tzd+=Pl;`I?NzhbNDkgr$IR}(J=}J4^>A-A z-!p|pC5#l6Sp2l6&W_;xuR;-dzy-MIGH;kMaIH@H0Ya1Es&tS4`!Tgfd}TJLfA9S2 z-HnsVrh+g*`Y0aen-E4%lT6Wf8qU)H!g>Msm%3;OApw zb$dGc+QS=uxq8Pi#%^EVALi`StN8zxV!Y4xuxa}}m{^UL4gctB;=pd%e^z^79?{Y> zA|#M`iu!Fl5X~AZY{LN9!RtTXWpyMY%UqAsH>Ap&a+PQTUq0t=A0Ct6L96lGa%9=h zkL_e#4x8za)RF-+1Yq;^{VDL-SE}(j1%BJJq<%md-S#l-;_!(}|9HlZO?}r_2++!P z%n<4$ga9-Rqw0L_&gSu$60^P~-%8T~5&AJ?jlqoE5m(K25cPr(;h1V`mFx1ozF~aU zuh4hN;|}|#D*M&`rSef=Vx@}g_Su7#<#aQXDH>qjGfY`10twfZW@P-udoU|5f!B@I zLrL=aZ5T61Rx5&861if0Z}T}x>Kt|lnZEDB^MXxIAE40F+13)ZwEfCvm2*|h4JcZM)f%r9yFH7* zr**Bpg9t`YmBI6n%na=c@-SJ4B7mZ>?knm2{Y?4Ek8wN+!Dpk-*1!;f34j>!eRCL$ zZ5o@SKji;Ro5@&GFA|KXJYzWYk2}%!K13#Jz)vXjDs-{t98Ov5b`J$rc1W*HelTgR zx-deAlbD}voA0ZUzD6IkK3KhH1e5^{0PoR8%p+aBxgK~q+_ii$BSjKf^S_JN)&Cll zPll#uU>!>Qp$q?Rv~Jk6 z*1nvy*2(ONO*C)@A7j2^R&q#*N%CBXrjCY`VU9Zlqa+I8612wa85}g-k>lRkkx%Qz z@N=bOVF{0+$mp6BuBheao$WoyvUyEUc*hOrnX0IRExozXZ_aa~`FVd&Q59ZZW*r2I z043c<ku_>JEB#{Q*H2>$L`MoatFDefRDS?;Yv7P>iSKj zHgX`*s}p3F$x7u@UL0K1oUgCT*C&n!Q$zg!PB^~!Gp98ia6zxGt&$))3wgylK-h|dq9-bFcXyCUQTgdDgrsTY@o(&)$*pY7)<5lnubtb) zO;V7^uxsz86v0GxcF2E?`jpC9OH28 z60Gkn*zA0VO^4OqBMsl^urPN-t*i$?FN-xeT{&bq2E5t}I2lPphOf$5(B?>pea#zs zWs{4znzW&h*JY2VgKC#^9&yX}T$8Vir0>9B*DS?q99OK#&kGOTwK>i=DK{Oe!~a|4 zmp=b7>y;>TG4#z2TEn>z71z<{9x21^K4mt!%h+Xq?cqonl~2P0@z%^tu2unFnJ4u$ z;v`m|Pz8Xb&NhQmRiH+nCfQ^#>7Wj+Ulyj$F*w~%3OLq!SW=hj6bD0f{*&n7W0eHm zhdsk;;RyNW?>+3GNa$3EwUC z$*vzQChKDU_bN>*!k(5_#rv5@jHyL*mBdW=oA=%1{kuJu`14pNIi<8)0o-{)rq;gY z)Fzdnz0>-r=wU#KiAWwjxcZ~U7-93h zD@Y6?T84y*L7@37t|!qF;1&IdSH+ZJp=w>6$;q&v-p)x;4@qhghy3d$g|2AU2+SqG zRzcEE5$aEP?I9T+6|MJo{f(C($cH=ixBDMB_(8*Z=T!!+MOI5BpUv>Ds>${dMpUA8 zhvSxmrgXh|X_IF8c;4A@l=nc@7v=Gh^~&W&X|hs4R!%(y#*EbQb@e`3DbpsqDR9e7 zdEG8yyv+{udYt3Q+am-ik}Y3-psl{H=1DfF>_L|d()=%^^(`ehm0ZGrP$W%=y$ccl&tN&-pbTB%d!y-byr`9GosotcYeI+e z7M2!~)tp;bec0fq`1t}QM_DY^=C(^lM3=dXTS7|06}R{*En<>CGN{p|I(%KXV684V zGpF0j?z^xcT3vaR6}CO-x#X{x9nQ{<8TFBC!GAj(kBsr=Je;p@_#K=q`#%%{N{Pv3$Av?EW|BjG}E&BDPFtsr%4|k;( z#FW{Z;jc~XK8j8`y`dic8HBD(a`NiQqsLZSa}ct+mg~3|=ZarD5kyZCYCF?$c?LJ=H>* z)FikPwyK4y^8|&RsVjAEPVXA&*#Hm(#*mIC(E? z3PKy#g0Fzx&bTL*G*^?CZ#G$Gu)W;vNp?SDjWDwrU}3eNR=1S~jKr!(m%8|x+~4rm zFH(H8@u9t+Z*vZ_ngeoT*1N)e8^Wyt{PuWhYCY=f8DZ+|t%(c9(l0;Ev#LhP5E(3x zym%~lnw0Uq@0%b=_apfMG3xMx@uzff`W427qD#azGkyF*3G$Opu+Vp)Lr7p3JDu56 zOv1#ca*UjOKe_cg`SP&SI@uQ_@SiaQqgd64H~lL0i*5G$()h$-L~lJs{2#90^sfc~ z-ZMzIK0zf-A#dxl_?`V@k1a8pKKdP**n6^d`ELU{$0=_F#(Nnnq{rF~^0ByI{v7ze zXm2HiH;Tc73W>`BC!K&(Ib*=N?fIoWJ5|Yo33o0mFp6?pVkgKC-hny0~34p-V6qh*y_X~l71ym z`=QY4q?%hp;tC{u_Bb8^44iTy0tJYb723Ja1^M?eH%WMQI^u9m9*6mXQ|(OEUB@dR zn7aHtEkm_YJ0f+IY+)`FVr;mUmT2)}s>4%dzU!<@%|;zUk=>oFq0iV=lA>-a7yLbm}9* z$47nP1>u3~gnp9`c8A6us@efF1w#ABAA;XTmydg{b__U;%1vPD`i5M*G%)rolQz-Y@>rA0y?nXkfn|rhT7gDW6ruA- zh`2zD0-yrP#N>_N)f-ueX3>}!jM=C=nlk@`4t=Z1q$8vwv{v~*CeP1Qe!4_TpwFB4 zXqDf34J-yQB>C+i^Q%kdTMf~cvwjdt&uY_3+4~SMG+;bvwr~X|=Y*@qd^ZJfZnxJ3 z9;*k&g4ndQ$6_yq>Yevq_wM<;Kjkg{^ZH{lPcCOeiIjTdp?35j{jWYz3CgesgsoS` z>NxCiXdjmb&_IOx#nEg;H|>eYidrkG$t4`7#Kb6}b>7$Oo!tcDwLcsopB3&t*x0I!P4}SPR@zD+#4*l= zD)AKLL8DD0W4Bliiuj2B%M6x1oqs@*6uA(M2CC`>J^WP!b_Mq=BGgBhQH&on-S-}W zYEmTumnh^%b>rph%iuTrfkEKVVD*e;0ZIE;UYJ|aS21|<_OZ);!u8T>0tG-WM*0XrY za}@E@D4u%yA)E{nFD0~WAcC7&&JDqRfP={K`K%iL|BL#a7w0=*zKrq=Zl^YSxhbR&o0iuZ+to(4;im271E% zwsU@IU`0UMszzh>WPrhUX5j5saeu-^h(zPoI#FF`JH<%hZzT^O?PgYg4e~@3U~uD@ zNWOsa!9<||6}OPPY0Y8;#n!z5B!Mzu@kZ_q0+puIQr$GSVD8m2{Ldab+$ggA3jTy{ zU>do<`vCfPbqU%Nad0ySN`JkC6sUuQiad^K*xKKN>L9W-s;eT*mJe@zk%p9@&H=CRi21r_GKtz4yNd-dZ?Bq_}l8PA2uL&h2l;P+%4LPYi7Y{RpC)9X4^ z86gDi-}($znw0FU^J~4=f11S8GxaQGuzb-CgeO?=>n42b^wa<-5dp zp|N!3bEf$*PN|obs#PrM*u{kP6go#)7*TZJjc?1oRPyd@TBtZ zzO#8c22aC=q>No3+qZTBJFk=f|LC_*jgLdVKKgzA?Sy_2e@0XJmG`vM(NY~Pitz;d zvhjra(Y^JKh>%2R`ErtqZ2f(gn@EiCU8CCo#^flXc01KnBgPNJTc)3^Hb)B0 zE7#Tkbf&6jKskqKa7Yp@Z6qADJ(x&E5qD}XY((1~$RH|AlL>5be-{Z}kPfoP64vNy zXWg`d!7A~HqYMXEBqJg#5W)|!`~;Z<=t)x?R%8-FUTH2Uj!-+n>3*7xD@}75tIOndT?+j4-2VK6xbgZQbgn$2J7d|msltI#Ld3Vgb<@-3 z=Q>@jbfLlbRg6vVj+~x6V%-Dv?Ocw+hI}wEm?U$Q&$p)(a_eyWkIjxS-sIZI>6&z} zN>`1hY+%WPIU(jzBtUUwqAR0;*-x4>nFe89m&LtwUg-Y~L8%a2wA=09A#GU?gR}YI zm+1gibpOzYKd69!N_Yw5yrqo1%in%p(g5$QN+$CRZ#C%2xcVH^LQ|F_k_|47C;>S%}3`LkQKt)OJx09jrp^a zmxWR@#9SQFAi1$;&mZX?k+qqg60Fjjn%>uHD*n~7`$KZ>lwWLiv$URnaCy1v)8$5_ z#uWL8i4xyOJi7YcdVuPu78p+qs*h>CJyD0t5U7J4-|IrR>kJqiUI-@Oow3GM0PP!! zuqgRu9rLOV(u8?S=Q1|~fWe)ewU^&*T#jsL<4rp2l{ycHcQ1af@~lND+@Fi1(d7D( z=~aqXg3ODFVLBBcKINzZ@Dglahd|L#$@tzA8#}7TjbPyg2F979v(vsL+H&Te7aG;N zlII{37lUfR(5Iq=FWm>7p#Z`pnD9Pyk8` z_WA-o1pqIyFnLsewsJ(DF4ys!}n*A1!brBi*QHMBt0# zjxzGtz$yFr;pn3QdzRw@7yr29QGxz)s=XUyADTdXOrTZI$r~gq_7Bbm9NY>Oro~Y9 zs~M?1pW!I;-5ZZ|SPkt7pgtD(*eHJ2PWj9(j`uHv$IQt#G*#6g_PzP;7=FttcH{nr&RXAvvt4Xa*MY0%XX9Q`tKG^;{MlzMN-=5T!7ezM zkr=->10fVm)17Z}G_>W>zC!bp4hFrZKjgs4MwI zO>R9K+jv0>c6@#mcOm-s+J*c6wo`LbKCYpV`v=pDT&TyedY4YSHA9=%J03_@Sq4Z! z0hip;1Z4;EV!mnw)nc(x0|=0S`Fu$zKluB8Y)a$lSY_2O?D`x0Ff#-cxl7K_TDL}z zE3TUpTYT-_SovE7OHb02_O$ypDe~kJW-=T7Pl>5D2YAmD3eEG^W`JwjM&yb<*JddE zZ>w$)411c6VP+caGd!U>xs~VlNfO^!#4*C52T!wG<{ZAc5jZ@Wl^c-@mpJGsbU$!? z+!ynJOsrv2sA?#LP^DQI{Hi5u7`Li*3m*L3p=L0?QtMO4;nZOfmTYTX8!p4NLMTf| z5dQZQzsP#dqjz=nMdbAXY4mY_YOPECEQCWA8auWB+Pi?CRDp80nx2spZtXlYCEoxmX4 z>vWe|Z?DXVesWEmP58b{^W1gbKSlm4?WV!?z5B_46R`F(=hCM-Zg_UDRENv=OJzt# z=BhdNd1=J{lsjE>xWXMpG8uQ5xu4~;LqN%Ht7FND!I6z$b>=bAq>dD*tSBiEPHX1# zAOU%vxgqU7h}x#@p8h?0+_Gk&f_9AY!#A`xvW}hspiRjW$*81quPK*(Cu`{4A2M9f zOS|s`acJd#fNg+xHBhaaHt!%C0*8_Qj1wJK;J$0K7?AX{WDX&9el#wPL%0ih+=RZI z!Z}U?;7j~9T1u^(*wWuoMEmR)uc;H$`Dnu2^O2(v{G!PFrEGTc=6iWDse0aSS2vW1 z%sO2oF#6{;b0zx=W*n*HEZp?SAL?Q2x6m@k_>3`xmynpJPPoLY9m>QRaMR zG)WU7mrGQ%Nt2zaJ!=%k%cNuu(fF`+2d>IZKKpV9w%I-J;)C{9$z0LEU)d+tlx30+ zFD2Qev>gw|*%xN#)lcubAIx7U16EZl;j~iNv=8cu;LCotfO$}!jbBpX%E+f`kpkti z`h4!Zp%r|<$c0OxE>7T0ba9#bT4>$azTeQFB3wf|n2=P{#0IkS8!As_3Bb;1BY{!s z?r_25IobMR`h~&2`vsm&7B`W**V9o%F-E=DbDG315EkBb4JPn2*ZSH}w>FyJx->?6 zZ1Z68_FI%=p$Ij(VM3+n_UB;p?)?NK&qJ*6x*rX`B+21-c5&nOV!s5lR3);D24xhpkkrBqB3G5c~78aW$z!d~TB1#~Hw>`51{(g3n8g8itwj+5gWDifL|8WdTD^ga!oY^$_!IR9# zvr{U#bwxh%=dVOkpyTf-^vQ)`2%)&A}RZb|H*>uG1ZhowP1@4N0ir|ND<1)EcG1US*3lUuuKGX00G{Ea#Iu?v&S zVe(+E*ihhFluD+DrO|{_BK2yfd^4x^>0v#!cWn z#&iq+uoND%=q3^lKGwBj3fqZOn|3Z@d3b2U1P5RRP zL$L0-qm}pY&c8haXkJdrUt`Vp{GOt&$&XPENp>I*F~OXy%dGEgq{w(*Si~6$n}SE& zUSGXQOl3QJO7@sM4FNWT1*2OMlgNR(vrUz{=~368&HCp!xfL6bspS#Fo3n3a_qumf zYc^I*p^aYM;fM0av~w~VC6YndMM@+4FtpqS2Mf$(rrVEEuVVG@7Lj~5y!L@=L)XL8 z>Cx5Ck3$|sa${@;M9uutP1_*l1;vPR+ zkB!(o)Xg0Z@FahI3%Hw5c<`YbX^mL|ZhLsa0=srKG`^g=fCKIapLd><5rSQwd;e;p zocn&B`g_F0ESaBDWNHQ%FjY$B-CJR2cndH`KO8o!2C#NA8mq|ZWWB{eo%aAdK1PAq znF~DTr)1aEvw3Fn!9~iZ&>@;XBXDo&hF9Iv6sF&LXvc|Wu#byCMyIWKhjqP;nGj93 zv26d6o+bb0qZHTCbGnD;YNl92S_`vY+j$`n?|ZD6{hzk?cK>E3{JQNN9zNWyvASJ$ zc^G#+;$M64gRMh7gWq!IaI>2_;fD)Q5K%ksG;5Zh4@a}U1id7|LgiQ#3{xa)W1dG`S+?VCpILml2mgZj8P)r>&(x;QB2QcYmEJzzxfv|Ob2bc@cW{pi?R z*a?gp{NB_gR4sp&QwqYmM9OsKYVfZWADzwx5l7|pIxz7~h^ ztzz8sOWQ~@wIR}09tfwcXe0W9xe6M48~60<`fS7GHYyl&ovV1#C^VIR$Dwmt7vbo- z>ABp|NSLu`U^x*Dd(7sNMdXNmsQj52cQzs6Tb2ucXoy=^hmQ_U@!4lBr$XAw>V3aw zS_ij4g15=4}6~W*T*=Z9WkTJi`wN>^zrt!O@Fa5 zIx)%<+0VXu>wjoVs&Bod=ghq)E6Ef7m{rLmu{ww{c&BofaBL!`y5n0z2#6j{Ng1RV zFZk|U)|ScIoyEl^O`BXQP_DAR3Mf`;+~vYa^*vvdial2{^GuI-^IL!vWZ-m=RO7vV zlv-{4a613roQ_$T` zEhC2!O2|M&_vqlVA&=jbu}=A2Tx|siO7v&18F}(RxiE3SI4RBH^7>pz{qmuetYjoF zHU^R#!@N;n;<;-f5zueov}uGjarv9uK~DDahq`%u`B$67gz8!Bld{LB*^{IEm-C~A z67ciDe#{&DBj|MFiiUy{V z-OuRc%^PfKP%&#m>`X&vY~J1NaE}V2F`z zxJ2ph{A+4ELx0zAZaX=oO*ZlFmt64irzT$LuF+o_fH_Soyf{H~|9g3%HLfb)Y-aDv zgG967(g(9aR4~`zd|Yi44)|)7V+%_#xKoI{+)FWyUAuZ+iI|pM3;mI73zg&^v0TRy z9bZ#v@3nGz+9#p*fMp@mVx8sPi4>6q^Wx5vep>YQ`0{F>^_F2!Bo;KmwLnN)unG)r=~K3Bw}8_6=TLUw74lCKZW z4d)>!8pE+9ws20_Um?7W2}k`VE;e1bx>DeJG*MuDdL|3hb7pDn#owzAKq?Br7o-e2 zl)3h|H`;~=EiQf2#?^c8p$ts)tk|&iN;5Xj3d|YqoSsplGwc6M-I(xsC9yYMPe0%9 zP5tPGEe#o3T>HCxwxNvqL5NYY{`VYSBGv3?O#$d>SA$;mHqFFoI^;)Zb7~%yRbQr> zZI*yhGuT|sxkCY8msOwW`xR0M`Fpviz8)@FIqo5=2^6<`d~5B`(h44plbIz8zHq5? zq{P7HzM**r*JwZ1usCXpJZL?DrvL&gndBaDwNt-~h)yPM0@dg@>r`pnc}6~33I{TR zo%Ud^xy^$brL}yptSEm67Bcf=>-Er z@$q|m$@hQMGM3wWVW*TBp>txbOFhqd@qn$jP$G3J@jfZ2sm)cc9}nUpJ`O%Z|5+QtEc7>;@(`Qc~?%h7w8u`+-fJ zXGW>fwv>|eTIw3TH^WM`$GDq-=H6!LChuF6RM*Gm_R-zSB0<4~SHlE<&15OTI34)b z41J$$1?Gei5X6u^%E{c&8&?TR_I%ss@Z47~^HG+5cu##vq&2j~ zGjuMB0BQ}br*a^UN(_$f7)RK1y=#CSHMj# z%=w1OQbBE*5#hOnYaE}|_r=l|J#_W>_yJMQJHQ=VB4u{j^wfEF|fB6*w#AEd8z+;W-Fzmw=q z-jQ{#R4V%;O%hrVl5x2A{KvFfikCqSWt(cxF|jeuW?6d;o~nOey~yFd8pyi&^ekeD zQbM~K1SydeKV0w^`$ua?|JnT*;L=F;^`)xYub~UxpI(g22vQ%o3ql?aM%!uzOAHH; zzpMZWv|-LGzXSNcAV)_3-CaLFL<4l*;M7FxHt~V}muY+FGO|}KJ6^SIO{2RLzOQk6 zAKWnsl%0VAfg84_??wqu5TX@QUukTH;T*N>`q@A#6SPahB4D2(UHxF7NT&JEDk?xM z=zODo`#S%5UCLYmzHIt*1u-p$lq~@RkM{A9wa@anp=soImM4_zx}&^Bvy-=f{Q!=e zqriOwUY;}fB_?%uEV-^0^QT7q1x+cWN81Bl_t$Nk304Lwp-R>@gU=_%l^`#qTb@g+ z;iuJFqJE|fQr#k@XIScCC{{>2e;c2uP3uC0VrtSg1ix>fmV80K*gZg7*K&!vwl9Fb zeU9i)tvVPUdM`*gZ1OfbP1JX1$;Wdm?egMPO5O9Z5?u`kx}9hfp>glSW4kP5YO^S@ zNN-<_UmJR8B?ccD<^sW=hNU{)376)QIwiVqvtNiyTo1FRz=x49<|EDU$+LrnyNXRi zy&5%Mk>>noMHhST?N>U(=fyfA(mGi~XoFRiw6A=cUNAoXF}3q}=C4YKa;Sc$FQIw1 z-ndquLSRktXuIw7@*Do>Z~Yt4`20uhlfC`w58!=MAL;k$U12g~;kH1}IuINeW%)?R zSmR(o$~0n!rKlWvx`QmsQovV7ji);$LVt#*g&MG>DMt*~i{#Das2u8Jzcw^|n_Zr8 zP~p#92H_F$fvlWIH>1Rpf`)0ry&SEhV#aZ;r>#OuO6L|GoGa@NG(zvpe6H$bnwM5X zo-z+4YOVx6`a|+K7n~x{B>lFH@qNuN_+0W%cg1cK1++Bkt_}~P0*Wkf}yGxMZ?(Ph3A-KC+a0w6~ zKoVr`$@f*=s{3$X{^!4nDW1-pIj8sT-MxBsua%G=y??agH=m$Z_uYwV2lVzTGO^Qy zSH6E)o|}H)os9^O@hT)ZwJUgNi zrPI+^iPn@-+{YQEo_F$=9}Wh0`$Qja=>;~uQY*Yl+KuQt^h(!8Di*Nu)2wml6-a+z z7jjUl(Zyo%G~K^qzQsrr@Olzpy^7jqYef885O8S;z?t`So1)~12||i2^J{E-vj#NW z5%gxRZXFg*Xh2H_um_jcFJwwP8Z%DR%a)qni!^We{j?p<@jqAG@ZI0uO1n94b)KH! zXsYXodH=0g-%Vu-Y$0+pgJ%5f5Pw^OC&ysrLS&DFFXhgk`8%Jel`6B-nE;tVdDTkNv!puYYplpQy6$ff1IhgY@5{toN>(S`8&cZKh=ebu-*v7$?ZLeZ|LjXDj9$C! z>H!1j>$Ce-J)Gz5JkB%9Wjn)CS!kX9A{qGMJrxA71Kxj9wPJf9SF+C3<5ItZ_kt$+ zu)!47yt?l7Pl`%G-6c8&BDsz{E5ARUpLUx9#`2!0q^<5@sC(IJFWB=o{Xb7*?)ag0 z{$Bd#LuYg&Dd6z-bHE)$SzIo7bY((D>!LQy<^2Z>j^B&V(=_k1jdXwPV;k68<{6&0 zA+Kc!H>^#jrQ$DtP1Th%VcTR!YW6`5#idLiyh%8U?JcTQFuoP#XlVUXWV>RJN38&i z%f(Fj%SvDS_ZcoIg(0poG(zCjlgHZrBGEFwM~hK=YTnUN?HZABOW_}Gt{Wdu7y(Qo z8fE~-SnFKu`|emMfNq+M`QNtJSz#|AxY{u#6ag%=KF&~2xIWLQK)nMR79D_Q)G%C3 z03pp!hFZ7RzP5aVv!m_@Uk`Y}Qr9hhE?zU+GnlFLKmUj#nxO972hhFH`duaJ74LY) z#C%ICMO~;Ci)j{nwOneAAIr3a0P%<~cVkh%Q7TkRn>s|ge8}K$7jo>)s|k76L9d&@ z@$$!8?0_V%X}h=17RtKsDYm_f)#P1r49*V)=mZ7QW_g^{g#37tn8>Sj`CMs~LR7NkRy^p8ft|~BgF+vY)|%p_v%Ws! z+e?LiTcqpV?>O|wjc<03*vM7B*YahRAM1%S*QMW(k!G;zR*;d#yUfYgHOYKL%Rq}l zs|e8u!)UJsvW7CPB)g6k)2my{uD$$!^y_t}AShPKI(FeTs6GC)X`pj2au5uFGSw5putg zAo4>eC{#~Sl3j=YG&} zEf_w>Nz2$3YyeQD?1y3<2j0G^UicXgVP<=Mx}LH-!jwB=Dvtt>fScw=X8kj8t@R~~ zSmD4K6M9wq7k#bxH#9XQa=Tk~_u#%8)e-~e0+_hl!4Ii5hiMOiRI)5DqL%)V@`g6b zLIL=BlcMpj5Ydt$rOqjq6B^LwFiFDN<-S%+>CzaC5|@5t z9_yhN2>hDPW$-R$92+MzqV2W^V$Q+SD!S=?elm=_spo_O$TEc9qCfjyyAb1kD2>O? z@niUT`KR|HuliM9?BQ8~AU{z?);F{10M3!O@a|oqX4mg7PE&9Th8=snk93$L?ww9J zzA|0v8LZNc1kQq922?hRdxdzTE4*Iq(Ma8ti*?Ay=K(NW{&d<}3bXB5{qk6c;98!} zIyoQw)4iXBm#ju`>@7h+7EuxqzIzn}W*JOqAMwHL68M|t86T%Kgc5#X93e2ch%l#HNa*`J-q1I&+2# zA7^sJ>DM4{TI6A~Ceapd7HPb!piNgo{pZkHd@b&870M&(=Lsw}V#|b*&w#v)5+wUu z3x6c>bW!ELb4Ig})(^CLe{srsKPZ_#fI7s$Ue5jEJwJqq-g|QV{B@*mbidfYqH)!K zm2rMw%Z}A?yA}A)3$nnjztm%eTda#&o?@@Aaq0?wt#bSOhmyS3*pL#l;2{Ff z_9A}>2|gZlFY*V~^nFuHbDp0}9p1pW?}2cqn_j!I@b|Ys`vWxHcexCt@+8ys-RTL< zWv6$28$R@QK1J+;UH7JRWvoWbkEB%Z<)A;js^Y-bN_QN?!Ft3y<=110L)ytD#t4tS zHfomLmsIwhev*$Sta2o(_LvmGh;B1dto-$TkYl5{i%G?a>Z_qT&B{1wX4pm^-Igox zK+5)q&9gI=nf`r44$bh%F7I+AAuq}(Gp2z7xKEmn_tThj&C)3~z}EI7w#CmMf#@`uk-u@4s;_D}N3Yfl%62d)KnX?z`| zdrTBdjF?-&3sOWt-~S^%cR{uFh)F8v88-c>8c_##T+s;&qZ#yJ&n9uJ>IMT!j*(vD zPDUHv{@;DD)X*`p+2~a5E#Glcu;>cn>5>v7ZU4D zweY^=Hio|y+;%3{3HbBc{TYRX>xt^7j}oCIi~Q?AMV#B^SMU0=S4hbPiJ2oGUlBiT zZmqFg@5M1Ln)#TNNq7085~p$L%KKb^Mh94#sdk{kC?HPpF!?=DlX(BiFTARl_b#`O zx;=aRdH8*Zz^Zh?S`0DLdb^*6rDY3KxzkIesW2B0?Z6FwPI#z4ak^(Y{fc3Z-c2sy zEgfT!X*3>2xvCH=;j-`j-IkvuEiNHzgyh< zOj%it*ZimF2GgF-x^jjAbD33Y>?`N7kyoR5DoOM1S}5)uu8h5e&HUiQ!S2YJhVd-mwx;+ zsW{2ud_)c$Dh_jw(N}zg&KKINqo%s@d$^T zI^#9ndz`e-oK5u-evi!Ty(2H3>>GM8E`9Fv$xkpx%JW{*6eTldFmL-81ANqnn%lY+ z?bXiAHp#e=A8fiASQxN^Y<^7@FpK=85PeAYRUaNHTdBWkJzsP;m<5VpKSyO?3>{2J zv7kU)uTHWUHM87=8QQ$x9U^aGXa~MN$LjF=uPIqIVhVb! zs;}$^r#{OZ>RhzxCbZAUPL6!ME0S+`J&V9sr5GldY^prS2=$%~66mO9A|q(#7o)0* zPgI;p7`DV=a)FqTjLuRoQVXh(EH>;tFfA{gG#~1As7UTJR|>i!w-^Wf`3FyLiUG&o zU}o=Cc+FAo{Y@Doa{r(fQAMK)=A;7Vk#{4azMc}@{2;H5~f0Ku`dKQ@e``v zRZ-MqBSSTu-Ugnxpe&~`8g@sbn*olrh+E%BKl~R-?-ayb5*P|Gfw7id1mjZh^95?1 zTfg0zo}7k&wFA&tI@5jUhg)-v{}^L&sWe);=kr3(yw zqDLH7Oy|KrRD=j-G-?u&7!!SJj-s5xb*xqIa6#e=?^0uMP%P-K2`M52{~EI; zn4Wcbz5n}+A0Rahm1-exia2os`gbvYw>{TOHyQU`sENi60gr4x!&bH)R4p!2q1_0C z-iTo#j+f}Tl)Uno!xqOnkuIrw7=Z`XFn>v?BKf!(MTB}TY;gIOBem<2Y7M}lT?XI6W|xTi6KV1A(~i|VNdav@@(Cw|&A}~h<3c1j4g&-H60Od6Sk~^A;l-q zW?aJW5gt)%RH&v+Z?!WzO;{)N=Qw};WE12MF-h}!kgz%X^Xwf69-^jEr&F(k9E#C! z%LM9%&5b|PAx(BZw|~( ze$jxX)8Bc3k0^p6)sK!nN3e{Oa$I+qm{5Pge>LnNkR<;8Jo3f5vrgq$k<6D%@1QxC4 zH_|CFSmu(pd-FmtYYwk>Vd-dtFsEP78xnz-wvicB(}$}Dh9>4-M2XoM-3|%%GfKZ4 zfzdWU%(i{>Ll$fILag<_=SJtC#8I#!yS(A&W}_CqbK1U~K@5uOn??EZR&@$W5B!?N ziTYI1weGN>i_A0MQ`xMFTK1@1bndrJiS6^7Ek@^W8dX~OV|;}5oD^isYC2l7likd7 zwmQ=s%al@SNqAz?k*Fmz9(MoYeI*kwjvrzDNBelWe`}nq^14^iy&46Sz3xAb7{GfB z^;sT`KZkT3dA4`w+>1O4FhF*I(rUTidg2-|gy}A|npH|`sJQjnJOT>=B#k6yPT7wu zmpGAcmeK)eXNK3}UmKm6oJ>A=x+bv{9(6=LY?xi-($6>PR*r8P@bCfPp9k(Jk78AZ zd5BJy-4U$4L#o|_kjoP%#SuzNv04*EO|_(~RXr#7G%<09b)Lu&WCks&_FptRuV`^smn+E6C z77+@0?z*M=U(b0z57;iVqM6Q{x_FZhcIBLB?yA@>RFL7fmBd*8+BdfrCqgaa7HFcP@qJ(LEPyNbv5%*JAv{k~vH>LM?D`v8*0B(w-v>~RQ`9SQFhM4Ft zffjj#t(XS#;?Y-2skV>bF56<6A0P54+*rEF`;5;$GHA82mL#&7_Y&jq%6NF_eEOJf zD)kb#KK_pHeF}Kxa=yo>3)Y<7uqn%FUsNt7c0FQ`kLW8>a}VdNr0O(P{6U8s%Y+M6C*gUYJK5-B zNl{KccewI)*=ps^qhdDNSElc^(eDRV4L0Ocs)#djUssy!9QIYoAk)thHUfEzPt%8t zrARL4Z-YHt9BSQeA5-FmyEBhVD&)Ir1K^)}p7R1?Vvcf6l$V+8xQ{>YbVm!RX59WI zuqNlp_Jf<}XL7&m9vV8;)gsC(ggJ+cY_8;d{~2QvONF8U`3^be-CP3Hs7aCK6(>;e zc{sUvjU?nC;Mn zI**=J&5>8ce@Aj)i)i`E!hc5TWn|3&v0DE@GNaEM-*wBOvJS<`2ePj;eZWE*(7z)< z_e7xiQTM{45(1^xv_hakW)7OrEqFu=m{cxN*0m|UI4N72V_=6}kMme&xw{NrM5o|v zrMcZASTL~~pEP!S3i{TXmcA++_mP6mT6WxYo5Xf>6C&Q0*Z!nUl4Vfv9UT$}$kYD7 zyY%WpB?!p~c)TbY@$}vB|CL32noKABk~AZRHCZdt)C~zlFhXHbIXQ8XqE%Y<%1Bw+ z|CfaINo@rqc}%ojPw&}w$*bSTcQaP(!gPX#kJU3G1rU92_}NF6)ooUNDeJn36pY-;hUk3Vnc zW^YeyTG*$@J~46P-MwP+*GR!;`v!n4_5JI9+JAI^|BWiN$*X&{{gL4TL>=6ompZP6D^rqFx{UJ@s+{AziQ z8~Z65$ec59@@J7Y!2YfCG)ZC!zMajr!t3^a_)WOnJNLmlS2EHAvqaYId-h0$pPK;; z2iO6AFSr=e(=&?s`7t{E zalS^$0Ec{ydTlOkv|S%nf-ur%7nq9#ky`WGKK5>iHinoU#|9Bq{P+m$!2~QelWmu_ zj$7YaHQEL)6S71A2Vn)6@+MMbfut3}0b+zlCX-Coz?x z?y55ieI*g8NraSsmAg$(an0(VZ}XTE8rwVKVcMfjEN<)CBR4kv)MQS#nW1@et$!T-O%X4Yi-%}5n z*dv-l+1_%N4@FL>yxtz}|IV^vlBi}mQ*&L5|L^6pY@U1l=-j$L6U#CFG`wDUKRvLQ zGNTv~J~{)G2y&P}ng&T{f4z679B41xYQ5bH%+}$z#v*aJAEap7Df-W<;v7Wb2gbo7 zVSlA!UJ- zOgu9X*kXA&5D6zPBa1!WU*@ED*)eC5@hA>JPocD5HUn<9AMikfbjoSO>wlsEMu-%P zt9F?@zy3wr?CR!0_Z#B(L=PLwscM;3Y2|UEU$))^ndxGiu`3`Gw;=tF2JWI}1+FoF4| zQsb|sv&;cK6hu>8kdh+y#?|bt@B^~-dX=$Heo}=0dEUK2+BDJU@igwKJcE*@HxUii zs@)Q)wdPS=R=jhF_sc@SApSvo5@Cf*#rh5%Q$^NF!GIdnoK3i9EZ#pGqp$nGNk79_ z>#Z9t;&n`OjZU@=1J1VR*Z(5VFzf1O)Io@+`I;0+?O8ppYYg#nTj`EVg5L$q6|C)I z8aK(BpHh4IM>$$2$|{7azGx_NU1y=EjI<>Y4(dP^sv%R6C>{*b2Qf`G9>n}#lm5C3 z!vjH0IxGa65}k{2zYrXbFHtZfb8~d}iwYlHzz>j^m-nCEt1l`ax-q%3Di1aO8jTxTy0MYUFqCLMlhOQbd8ia>hU~Y+y1K^EBaiqWLDvl0(8vjdZQ)ciMJs~ z;+WEUKqBi?2xMX6)~>-Jt?|x(ud+|xqCT;VWdPW=+NI_WVDRc>CUv@T$#GO5adEy# z=js2Nl(H(uh}q%CF{CAZZKqZ@q7bPE5m)7w@#GmLUdDf$CFogU>%Ldlb*1J17(}F? z6vO4=7B1OEEThTCA#l?C;$Cm#X=de2EI~cv*)Twr*!*xl3AAn1x)N zvR!!l)4UtMFfToKU)jutG;nT(Jnak_}H%C<-nH928ij zT_h&n+VQ+g{mX34k#VavqkZ2cVljrz)1A$^CS@U16fQ;7Ad+&q+KLyiMn!vB(vXw- z>hDb5p#t*f7bV!wGFe)uRUMayH+q#ui+VIvC^BVYDgj$6nj<%4oZl8UFovpU(lEYuB&C})C=}PTkt!Wij3(T9PNV&!dgV6q5j^@ zGrLlv#s5x|`*ucFF1-0B!p@PC@nWM>S!WbuJj0TP$6iQs7&XK3GnOxxG?S~irj4a7 zu`qj20t7#;!~3^>|~f}2@q1Gl8BW!Jhx1X&st%504T zAB`9%iGGPemZ^(>rd}&xLF(83ZU7vHoXJG&vv!)u#jxBomMhN*%Q)4@v{ORtH|>Vr zULHSqK4rDnXOa@Bk1tL%-c^IdK5`WLI*XlsdMOfa*LAml{N)COlJw%hvD zrK6a|LgYG6`L#+n4)vrA`c=h4>IL?B2XOCLmau0B0UmH=9H~h-YZYEvC@YhZ$NJN{ zlh+UR5Xa_w?tgD5lbsMOcF1`@Jp@~A%o)|R<}N` zHuk_y+dNL*VyKY6%_?pTC&y&@D26jsHEwN%Rn-P*5e>#=dL{Dgw)PfSz=*eqfd*XA z^7m=y*|=8OYTX`}a8AQQ0$Cr=r!@R=pd=E(Pc!9#xa6u(IB3}0GvQH%C-mV>K_wCl z>1g<=J57;LHKIJZ4QAQ|lRlFtG%%q5JWdIg9<2};`1RKxxf?f-`CyGe)eFs?Z_csr z`}%*p_CBzQ%K^YP2J@HEzvxEHyn#;JveH6{VLlp{o`xm4A*FFdLB812 zFiNa;P#)g28OXDlz*1pJ!_fja%+epcf7<$pgCniMC6|^D6Q5b@qjwlM==4SIBmVPl zL7H~cIX2?}h-w1A3<2YRJiz~M5phaIJu|wffyhj)h!U%ts1yDHA{MA<$^^^)5t@Ag z0e-&Xfo|PR*LOAh+hmbJ551Ks`qFjrIRb-s$3vSYXj5VPs}NNYAas#7i5nWoCqL{) z0~Vnq@BI1q;Xe=bCIMlhewP);DsRg}p{HV)B#o7p@`o5z8Q*=)+7lqvM}srHN0`E@ z$X?NMoj?I)-QoK#HyNb7A$B5IERxeI+txfj!|y0Ca_snv%*y1yuZAqsH^I;Hq@^Z& zdd*ryj2j49)L{KH6Y%>AiAn%KVP^oxRFtG-ol6IVIOZR9qtH(SMW?TaLhQ`RX;uG% z!>3+ZS)6$Fymax2H#m5MA&x2k4RvO>AFwH@i5LwXj$j_8u*Sut#P5|_YQo7V} zwpexm%q0@qOjtF#Ij^dLX*t29zG+~m+)%f_-Sf(Yjrq~xurcR&*zBg3;fg#B zH+N#ylG_LZ=!Hx46WoxY7)`nEWzo;Xz*fXxSh~)n0E7yx@3~dh7DTM&*md{kSG0Xd ze609wUE)4X6Yx7E1$9?j1Rp0dH}vi_Kp0YZz`|GQbzNvdB9xC4pL!vm7=-cl%f1$I z-Hvw(JR3{N7Gc_FwwV|#n`_?57Q7_X;&V>WHv$18;fC0zh}1Q-7!;^+s9HAt4t_l8 zRtJ1>M0%GMgpwCeuRsgkQUs?$7Jm80qO9QEk#pxl`O!JWu8U%;(#%$$$2zbUY;Sa> zZKl#$YwFvz%)Q?OjcI>E1XG40{YX0_fK3uvVdnwXK){SsagwrPJ`KP}ABT#yxcKrY`{>bvFKZn1+9ip8u1l`p^c+uwsF3sUR@QZ!MG;gqP$tIt3=FIo@3xIR=N>rXxJs2P3>fSNzAytW z{wq+|+xPzyMGq2vNG0uPzn*cSM%N@q(~v4BFBWN5Pe3S8rAHWkep9puNkfU!TZo%l zsAwhsWvgSsR4RaA(pcPf_B!mj7fwfs%U`ZqxuD9XT1gTsn&`>=x&yPl$ec=MSYpWk zsx#xQ+TQlN4k7@ojr>w0yNUu0zq@3FAS4&;i7{CQFqw0YL=Bqy@rgMx;EEBB&ehEI zCDDysyuJ&#Yo)})h4PKTgkTNn@=A=$`9H=HFQouXe&m)POnJRm_&37)dfV5}29}j( zJcpt_m!&C$nArtChhx%P31&f&heFZ4^T=J93UOrbxE-; zg60mb02rAt&=?nPK*I{Xzt^Y+nn=GjrpOD=yVQ~eW1P&u`N0T9BqsD@f@)*OLR16? zzIcsuEcicGa+ROVzJaf^Wu`m1T8^q{r@7>i=g&d=z4uZWJ8bE8vYYD8)wYI51BITzWzIkwJV2c6-`gD**%W{9K`m0}R5+tz~R}8i%?2-u~$m9n{n%Ikxq&0|>F5)DfCwqJ! zxf|yIYWX4E#6Xw4io&IJ-13yW^%5XONwS|RHKZUUHjF?E3X-Z)4eN%cut-3k zh+arAK@o-!hJ)Vh+1j>e>Me19E9G&^PGEX-zHAY(VAGO%{I}3Gjn`dUII7l7eRCqE zE3ItOIH>DP_N&eU$s}&_;U5jF5OZcV8F!7^qTqB;BAz@hD1WyKd|^=JvU8yW+oBd@ zkv)v8EMOu}#1sDQWL<)Sz&xbNCqKxDdG_J(P`3zlk$LatI>{cRFNg10nbFB(;mxGr z&New3Yx0O0>ACsqzL^Yt>R?JIE9|OAq;>(PiFk2t3hkRQ_=FBInB+);f0?z;2jAXm zXM^SWsGHnw*n_&rtXlg8XA239RNROLf~gELXgzd>Kpmk$m44y&8d1dOw=ram)JEy1 zs%r9AMU3uq+vFtatO=8QQaFN|7GMDMhcocuus!s<}2rNDWvwo{7r8|E;v2l{%9$s zDou=EHI2HlXnHv9DlF8(W?;-rgB7ANh-D~;_m2zOmw>JmfQpBw-w$MY`BHU|pN=NK z`x{T6-gm?9x67-@mgDJ+FFl0pCSlcoe*eC@Fl@>**&Ngrs$Na#HY~Xd5e=d>xkmtTAXCdkL_^973U0R{a87Jj$r=9+ z(4Z-X7B_9UZ{Ef}_IfG|H0$IK799zNIQ9sjRhkc)yD9}?4e>_p5>dknSud2=({At_ z%B1=Cb!Br?7#(Ht4(Y7dR{sIu_>-v`Obe;zi;rEJ=C=t`#Ys zL0u{5yXZ9n+5B43GHJpZ_`&tkuq`SSR}?~1NVGmmey}T*nOS9A!lb)o$-ColFAkun z8E#OrJb47A(e}+7C61K}V{qnR!s)XNk zWpEK{M_5T+j*f%R^$s5|H2goPq7n7KU---(XplH!x+hjRfFvOE79% zZ~`)N{A4`Imz6zy3zpgRLNQ(vLG1M zu(zPas0l@yxX=Utq_g=jN^hP8s;KM`F544t9VO>mr(=34kKl%LLd`7%4g{6|;--WS zlz@&D>$rCqHg3MP9J6E4v+!|pyeC7@W;~68dICGpD)tf-D=SsY{Ki@} zj+4I+smSB)8sWd_!J7xf7)d=u@C^f$M^IRdnJ9udHTIp9Z_ z;b#XOqiS>tb7s$ZUut$!I^m%|aQlGa>fb<`S1^#rtAA!r+8m4KP+o*ki(Aw~q0}2% z(ZW^L_sJ~l;qqsT&Dl1ewy$#t0bSetT@yG0s=_(1S*v}jVh78LKiszlS9&x z_2TwMxWmilMf+mB~ZW5&a$8eA@#FDU;+cX8CDM{(tFM`f%f9gR6ak>lZ~>2%Yf zjqhJ{%>(Gp$n7}zo+SD_ptBZd2K^%eSZwN;t@%1UrLdeOUMZ2nVgLaY%>=Sz8~jv^ z^ZUZ0>?)ukr$iKulQ~B;P(BeE$SSses%UDwlnqbEr2vaXTZrk9)D&BE0#%Xl4Zfo< z^)Er`xEb(EHov};yPv(i6Q3mWTam;@aGhxc5dzW5#bKW6+`-0Qv5pV}a|T6@tXq-6 z2$AZV%j9mGw|Oh;q+gy<#5h185BQ%?=fw5yJluMDKAr%fZfgED>hU(_E4U(FTG2zZvb#=}3$Sf&Mr$}b#)y$JHoQe{eNcY)HGeV_X$SdxU_x-t!!EALvQiw(VD&UR@Y5|2XSoService Unavailable', { - status: 503, - statusText: 'Service Unavailable', - headers: new Headers({ - 'Content-Type': 'text/html' - }) - }); - } - }) - ); -}); - -/* The activate event fires after a service worker has been successfully installed. - It is most useful when phasing out an older version of a service worker, as at - this point you know that the new worker was installed correctly. In this example, - we delete old caches that don't match the version in the worker we just finished - installing. -*/ -self.addEventListener("activate", function (event) { - /* Just like with the install event, event.waitUntil blocks activate on a promise. - Activation will fail unless the promise is fulfilled. - */ - //console.log('WORKER: activate event in progress.'); - - event.waitUntil( - caches - /* This method returns a promise which will resolve to an array of available - cache keys. - */ - .keys() - .then(function (keys) { - // We return a promise that settles when all outdated caches are deleted. - return Promise.all( - keys - .filter(function (key) { - // Filter by keys that don't start with the latest version prefix. - return !key.startsWith(version); - }) - .map(function (key) { - /* Return a promise that's fulfilled - when each outdated cache is deleted. - */ - return caches.delete(key); - }) - ); - }) - .then(function () { - //console.log('WORKER: activate completed.'); - }) - ); -}); diff --git a/example-projects/pwa/src/main.rs b/example-projects/pwa/src/main.rs deleted file mode 100644 index 581395e3d2..0000000000 --- a/example-projects/pwa/src/main.rs +++ /dev/null @@ -1,21 +0,0 @@ -use dioxus::prelude::*; - -fn main() { - // init debug tool for WebAssembly - wasm_logger::init(wasm_logger::Config::default()); - console_error_panic_hook::set_once(); - - dioxus::launch(app); -} - -fn app() -> Element { - rsx! ( - div { style: "text-align: center;", - h1 { "🌗 Dioxus 🚀" } - h3 { "Frontend that scales." } - p { - "Dioxus is a portable, performant, and ergonomic framework for building cross-platform user interfaces in Rust." - } - } - ) -} diff --git a/example-projects/router/.gitignore b/example-projects/router/.gitignore deleted file mode 100644 index 21fff11dd3..0000000000 --- a/example-projects/router/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -dist -target -static -.dioxus \ No newline at end of file diff --git a/example-projects/router/Cargo.toml b/example-projects/router/Cargo.toml deleted file mode 100644 index 9f92d87aa1..0000000000 --- a/example-projects/router/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "fullstack-router-example" -version = "0.1.0" -edition = "2021" -publish = false - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -dioxus = { workspace = true, features = ["fullstack", "router"] } -axum = { workspace = true, optional = true } -tokio = {workspace = true, features = ["full"], optional = true } -serde = { version = "1.0.159", features = ["derive"] } - -[features] -default = [] -server = ["axum", "dioxus/axum"] -web = ["dioxus/web"] - diff --git a/example-projects/router/src/main.rs b/example-projects/router/src/main.rs deleted file mode 100644 index bfda6d8bed..0000000000 --- a/example-projects/router/src/main.rs +++ /dev/null @@ -1,80 +0,0 @@ -//! Run with: -//! -//! ```sh -//! dx serve --platform fullstack -//! ``` - -use dioxus::prelude::*; - -fn main() { - dioxus::launch(app); -} - -fn app() -> Element { - rsx! { Router:: {} } -} - -#[derive(Clone, Routable, Debug, PartialEq, serde::Serialize, serde::Deserialize)] -enum Route { - #[route("/")] - Home {}, - - #[route("/blog/:id/")] - Blog { id: i32 }, -} - -#[component] -fn Blog(id: i32) -> Element { - rsx! { - Link { to: Route::Home {}, "Go to counter" } - table { - tbody { - for _ in 0..id { - tr { - for _ in 0..id { - td { "hello world!" } - } - } - } - } - } - } -} - -#[component] -fn Home() -> Element { - let mut count = use_signal(|| 0); - let mut text = use_signal(|| "...".to_string()); - - rsx! { - Link { to: Route::Blog { id: count() }, "Go to blog" } - div { - h1 { "High-Five counter: {count}" } - button { onclick: move |_| count += 1, "Up high!" } - button { onclick: move |_| count -= 1, "Down low!" } - button { - onclick: move |_| async move { - if let Ok(data) = get_server_data().await { - println!("Client received: {}", data); - text.set(data.clone()); - post_server_data(data).await.unwrap(); - } - }, - "Run server function!" - } - "Server said: {text}" - } - } -} - -#[server(PostServerData)] -async fn post_server_data(data: String) -> Result<(), ServerFnError> { - println!("Server received: {}", data); - - Ok(()) -} - -#[server(GetServerData)] -async fn get_server_data() -> Result { - Ok("Hello from the server!".to_string()) -} diff --git a/example-projects/streaming/.gitignore b/example-projects/streaming/.gitignore deleted file mode 100644 index 21fff11dd3..0000000000 --- a/example-projects/streaming/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -dist -target -static -.dioxus \ No newline at end of file diff --git a/example-projects/streaming/Cargo.toml b/example-projects/streaming/Cargo.toml deleted file mode 100644 index 29eba049ad..0000000000 --- a/example-projects/streaming/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -[package] -name = "fullstack-streaming-example" -version = "0.1.0" -edition = "2021" -publish = false - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -dioxus = { workspace = true, features = ["fullstack"] } -serde = "1.0.159" -simple_logger = "4.2.0" -tracing-wasm = "0.2.1" -tracing.workspace = true -tracing-subscriber = "0.3.17" -futures = "0.3.30" -tokio = { workspace = true, optional = true } -futures-util.workspace = true -once_cell = "1.19.0" - -[features] -default = [] -server = ["dioxus/axum", "tokio"] -web = ["dioxus/web"] diff --git a/example-projects/streaming/src/main.rs b/example-projects/streaming/src/main.rs deleted file mode 100644 index f090a847a2..0000000000 --- a/example-projects/streaming/src/main.rs +++ /dev/null @@ -1,41 +0,0 @@ -use dioxus::prelude::*; -use futures::StreamExt; -use server_fn::codec::{StreamingText, TextStream}; - -fn app() -> Element { - let mut response = use_signal(String::new); - - rsx! { - button { - onclick: move |_| async move { - response.write().clear(); - if let Ok(stream) = test_stream().await { - response.write().push_str("Stream started\n"); - let mut stream = stream.into_inner(); - while let Some(Ok(text)) = stream.next().await { - response.write().push_str(&text); - } - } - }, - "Start stream" - } - "{response}" - } -} - -#[server(output = StreamingText)] -pub async fn test_stream() -> Result { - let (tx, rx) = futures::channel::mpsc::unbounded(); - tokio::spawn(async move { - loop { - tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; - let _ = tx.unbounded_send(Ok("Hello, world!".to_string())); - } - }); - - Ok(TextStream::new(rx)) -} - -fn main() { - dioxus::launch(app) -} diff --git a/example-projects/tailwind/.gitignore b/example-projects/tailwind/.gitignore deleted file mode 100644 index 1521c8b765..0000000000 --- a/example-projects/tailwind/.gitignore +++ /dev/null @@ -1 +0,0 @@ -dist diff --git a/example-projects/tailwind/Cargo.toml b/example-projects/tailwind/Cargo.toml deleted file mode 100644 index 6660b87397..0000000000 --- a/example-projects/tailwind/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "dioxus-tailwind" -version = "0.0.0" -authors = [] -edition = "2021" -description = "A tailwindcss example using Dioxus" -license = "MIT OR Apache-2.0" -repository = "https://github.com/DioxusLabs/dioxus/" -homepage = "https://dioxuslabs.com" -documentation = "https://dioxuslabs.com" -publish = false - -[dependencies] -manganis = { workspace = true } - -[target.'cfg(not(target_arch = "wasm32"))'.dependencies] -dioxus = { path = "../../packages/dioxus", features = ["desktop"] } - -[target.'cfg(target_arch = "wasm32")'.dependencies] -dioxus = { path = "../../packages/dioxus", features = ["web"] } diff --git a/example-projects/tailwind/Dioxus.toml b/example-projects/tailwind/Dioxus.toml deleted file mode 100644 index af7fcf3b8f..0000000000 --- a/example-projects/tailwind/Dioxus.toml +++ /dev/null @@ -1,27 +0,0 @@ -[application] - -# App (Project) Name -name = "Tailwind CSS + Dioxus" - -# Dioxus App Default Platform -# desktop, web, mobile, ssr -default_platform = "web" - -# `build` & `serve` dist path -out_dir = "dist" - -# resource (public) file folder -asset_dir = "public" - -[web.app] - -# HTML title tag content -title = "dioxus | ⛺" - -[web.watcher] - -# when watcher trigger, regenerate the `index.html` -reload_html = true - -# which files or dirs will be watcher monitoring -watch_path = ["src", "public"] diff --git a/example-projects/tailwind/README.md b/example-projects/tailwind/README.md deleted file mode 100644 index 6dca4b775e..0000000000 --- a/example-projects/tailwind/README.md +++ /dev/null @@ -1,7 +0,0 @@ -Example: Basic Tailwind usage - -This example shows how an app might be styled with TailwindCSS. - -## Running - -Our [Tailwind](https://dioxuslabs.com/learn/0.5/cookbook/tailwind) guide explains how to setup and run Dioxus-Tailwind projects. \ No newline at end of file diff --git a/example-projects/tailwind/input.css b/example-projects/tailwind/input.css deleted file mode 100644 index bd6213e1df..0000000000 --- a/example-projects/tailwind/input.css +++ /dev/null @@ -1,3 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; \ No newline at end of file diff --git a/example-projects/tailwind/public/tailwind.css b/example-projects/tailwind/public/tailwind.css deleted file mode 100644 index 65b95316e1..0000000000 --- a/example-projects/tailwind/public/tailwind.css +++ /dev/null @@ -1,833 +0,0 @@ -/* -! tailwindcss v3.2.7 | MIT License | https://tailwindcss.com -*/ - -/* -1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) -2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) -*/ - -*, -::before, -::after { - box-sizing: border-box; - /* 1 */ - border-width: 0; - /* 2 */ - border-style: solid; - /* 2 */ - border-color: #e5e7eb; - /* 2 */ -} - -::before, -::after { - --tw-content: ''; -} - -/* -1. Use a consistent sensible line-height in all browsers. -2. Prevent adjustments of font size after orientation changes in iOS. -3. Use a more readable tab size. -4. Use the user's configured `sans` font-family by default. -5. Use the user's configured `sans` font-feature-settings by default. -*/ - -html { - line-height: 1.5; - /* 1 */ - -webkit-text-size-adjust: 100%; - /* 2 */ - -moz-tab-size: 4; - /* 3 */ - -o-tab-size: 4; - tab-size: 4; - /* 3 */ - font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; - /* 4 */ - font-feature-settings: normal; - /* 5 */ -} - -/* -1. Remove the margin in all browsers. -2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. -*/ - -body { - margin: 0; - /* 1 */ - line-height: inherit; - /* 2 */ -} - -/* -1. Add the correct height in Firefox. -2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) -3. Ensure horizontal rules are visible by default. -*/ - -hr { - height: 0; - /* 1 */ - color: inherit; - /* 2 */ - border-top-width: 1px; - /* 3 */ -} - -/* -Add the correct text decoration in Chrome, Edge, and Safari. -*/ - -abbr:where([title]) { - -webkit-text-decoration: underline dotted; - text-decoration: underline dotted; -} - -/* -Remove the default font size and weight for headings. -*/ - -h1, -h2, -h3, -h4, -h5, -h6 { - font-size: inherit; - font-weight: inherit; -} - -/* -Reset links to optimize for opt-in styling instead of opt-out. -*/ - -a { - color: inherit; - text-decoration: inherit; -} - -/* -Add the correct font weight in Edge and Safari. -*/ - -b, -strong { - font-weight: bolder; -} - -/* -1. Use the user's configured `mono` font family by default. -2. Correct the odd `em` font sizing in all browsers. -*/ - -code, -kbd, -samp, -pre { - font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; - /* 1 */ - font-size: 1em; - /* 2 */ -} - -/* -Add the correct font size in all browsers. -*/ - -small { - font-size: 80%; -} - -/* -Prevent `sub` and `sup` elements from affecting the line height in all browsers. -*/ - -sub, -sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; -} - -sub { - bottom: -0.25em; -} - -sup { - top: -0.5em; -} - -/* -1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) -2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) -3. Remove gaps between table borders by default. -*/ - -table { - text-indent: 0; - /* 1 */ - border-color: inherit; - /* 2 */ - border-collapse: collapse; - /* 3 */ -} - -/* -1. Change the font styles in all browsers. -2. Remove the margin in Firefox and Safari. -3. Remove default padding in all browsers. -*/ - -button, -input, -optgroup, -select, -textarea { - font-family: inherit; - /* 1 */ - font-size: 100%; - /* 1 */ - font-weight: inherit; - /* 1 */ - line-height: inherit; - /* 1 */ - color: inherit; - /* 1 */ - margin: 0; - /* 2 */ - padding: 0; - /* 3 */ -} - -/* -Remove the inheritance of text transform in Edge and Firefox. -*/ - -button, -select { - text-transform: none; -} - -/* -1. Correct the inability to style clickable types in iOS and Safari. -2. Remove default button styles. -*/ - -button, -[type='button'], -[type='reset'], -[type='submit'] { - -webkit-appearance: button; - /* 1 */ - background-color: transparent; - /* 2 */ - background-image: none; - /* 2 */ -} - -/* -Use the modern Firefox focus style for all focusable elements. -*/ - -:-moz-focusring { - outline: auto; -} - -/* -Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) -*/ - -:-moz-ui-invalid { - box-shadow: none; -} - -/* -Add the correct vertical alignment in Chrome and Firefox. -*/ - -progress { - vertical-align: baseline; -} - -/* -Correct the cursor style of increment and decrement buttons in Safari. -*/ - -::-webkit-inner-spin-button, -::-webkit-outer-spin-button { - height: auto; -} - -/* -1. Correct the odd appearance in Chrome and Safari. -2. Correct the outline style in Safari. -*/ - -[type='search'] { - -webkit-appearance: textfield; - /* 1 */ - outline-offset: -2px; - /* 2 */ -} - -/* -Remove the inner padding in Chrome and Safari on macOS. -*/ - -::-webkit-search-decoration { - -webkit-appearance: none; -} - -/* -1. Correct the inability to style clickable types in iOS and Safari. -2. Change font properties to `inherit` in Safari. -*/ - -::-webkit-file-upload-button { - -webkit-appearance: button; - /* 1 */ - font: inherit; - /* 2 */ -} - -/* -Add the correct display in Chrome and Safari. -*/ - -summary { - display: list-item; -} - -/* -Removes the default spacing and border for appropriate elements. -*/ - -blockquote, -dl, -dd, -h1, -h2, -h3, -h4, -h5, -h6, -hr, -figure, -p, -pre { - margin: 0; -} - -fieldset { - margin: 0; - padding: 0; -} - -legend { - padding: 0; -} - -ol, -ul, -menu { - list-style: none; - margin: 0; - padding: 0; -} - -/* -Prevent resizing textareas horizontally by default. -*/ - -textarea { - resize: vertical; -} - -/* -1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) -2. Set the default placeholder color to the user's configured gray 400 color. -*/ - -input::-moz-placeholder, textarea::-moz-placeholder { - opacity: 1; - /* 1 */ - color: #9ca3af; - /* 2 */ -} - -input::placeholder, -textarea::placeholder { - opacity: 1; - /* 1 */ - color: #9ca3af; - /* 2 */ -} - -/* -Set the default cursor for buttons. -*/ - -button, -[role="button"] { - cursor: pointer; -} - -/* -Make sure disabled buttons don't get the pointer cursor. -*/ - -:disabled { - cursor: default; -} - -/* -1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) -2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) - This can trigger a poorly considered lint error in some tools but is included by design. -*/ - -img, -svg, -video, -canvas, -audio, -iframe, -embed, -object { - display: block; - /* 1 */ - vertical-align: middle; - /* 2 */ -} - -/* -Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) -*/ - -img, -video { - max-width: 100%; - height: auto; -} - -/* Make elements with the HTML hidden attribute stay hidden by default */ - -[hidden] { - display: none; -} - -*, ::before, ::after { - --tw-border-spacing-x: 0; - --tw-border-spacing-y: 0; - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-rotate: 0; - --tw-skew-x: 0; - --tw-skew-y: 0; - --tw-scale-x: 1; - --tw-scale-y: 1; - --tw-pan-x: ; - --tw-pan-y: ; - --tw-pinch-zoom: ; - --tw-scroll-snap-strictness: proximity; - --tw-ordinal: ; - --tw-slashed-zero: ; - --tw-numeric-figure: ; - --tw-numeric-spacing: ; - --tw-numeric-fraction: ; - --tw-ring-inset: ; - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-color: rgb(59 130 246 / 0.5); - --tw-ring-offset-shadow: 0 0 #0000; - --tw-ring-shadow: 0 0 #0000; - --tw-shadow: 0 0 #0000; - --tw-shadow-colored: 0 0 #0000; - --tw-blur: ; - --tw-brightness: ; - --tw-contrast: ; - --tw-grayscale: ; - --tw-hue-rotate: ; - --tw-invert: ; - --tw-saturate: ; - --tw-sepia: ; - --tw-drop-shadow: ; - --tw-backdrop-blur: ; - --tw-backdrop-brightness: ; - --tw-backdrop-contrast: ; - --tw-backdrop-grayscale: ; - --tw-backdrop-hue-rotate: ; - --tw-backdrop-invert: ; - --tw-backdrop-opacity: ; - --tw-backdrop-saturate: ; - --tw-backdrop-sepia: ; -} - -::backdrop { - --tw-border-spacing-x: 0; - --tw-border-spacing-y: 0; - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-rotate: 0; - --tw-skew-x: 0; - --tw-skew-y: 0; - --tw-scale-x: 1; - --tw-scale-y: 1; - --tw-pan-x: ; - --tw-pan-y: ; - --tw-pinch-zoom: ; - --tw-scroll-snap-strictness: proximity; - --tw-ordinal: ; - --tw-slashed-zero: ; - --tw-numeric-figure: ; - --tw-numeric-spacing: ; - --tw-numeric-fraction: ; - --tw-ring-inset: ; - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-color: rgb(59 130 246 / 0.5); - --tw-ring-offset-shadow: 0 0 #0000; - --tw-ring-shadow: 0 0 #0000; - --tw-shadow: 0 0 #0000; - --tw-shadow-colored: 0 0 #0000; - --tw-blur: ; - --tw-brightness: ; - --tw-contrast: ; - --tw-grayscale: ; - --tw-hue-rotate: ; - --tw-invert: ; - --tw-saturate: ; - --tw-sepia: ; - --tw-drop-shadow: ; - --tw-backdrop-blur: ; - --tw-backdrop-brightness: ; - --tw-backdrop-contrast: ; - --tw-backdrop-grayscale: ; - --tw-backdrop-hue-rotate: ; - --tw-backdrop-invert: ; - --tw-backdrop-opacity: ; - --tw-backdrop-saturate: ; - --tw-backdrop-sepia: ; -} - -.container { - width: 100%; -} - -@media (min-width: 640px) { - .container { - max-width: 640px; - } -} - -@media (min-width: 768px) { - .container { - max-width: 768px; - } -} - -@media (min-width: 1024px) { - .container { - max-width: 1024px; - } -} - -@media (min-width: 1280px) { - .container { - max-width: 1280px; - } -} - -@media (min-width: 1536px) { - .container { - max-width: 1536px; - } -} - -.mx-auto { - margin-left: auto; - margin-right: auto; -} - -.mb-16 { - margin-bottom: 4rem; -} - -.mb-4 { - margin-bottom: 1rem; -} - -.mb-8 { - margin-bottom: 2rem; -} - -.ml-1 { - margin-left: 0.25rem; -} - -.ml-3 { - margin-left: 0.75rem; -} - -.ml-4 { - margin-left: 1rem; -} - -.mr-5 { - margin-right: 1.25rem; -} - -.mt-4 { - margin-top: 1rem; -} - -.flex { - display: flex; -} - -.inline-flex { - display: inline-flex; -} - -.hidden { - display: none; -} - -.h-10 { - height: 2.5rem; -} - -.h-4 { - height: 1rem; -} - -.w-10 { - width: 2.5rem; -} - -.w-4 { - width: 1rem; -} - -.w-5\/6 { - width: 83.333333%; -} - -.flex-col { - flex-direction: column; -} - -.flex-wrap { - flex-wrap: wrap; -} - -.items-center { - align-items: center; -} - -.justify-center { - justify-content: center; -} - -.rounded { - border-radius: 0.25rem; -} - -.rounded-full { - border-radius: 9999px; -} - -.border-0 { - border-width: 0px; -} - -.bg-gray-800 { - --tw-bg-opacity: 1; - background-color: rgb(31 41 55 / var(--tw-bg-opacity)); -} - -.bg-gray-900 { - --tw-bg-opacity: 1; - background-color: rgb(17 24 39 / var(--tw-bg-opacity)); -} - -.bg-indigo-500 { - --tw-bg-opacity: 1; - background-color: rgb(99 102 241 / var(--tw-bg-opacity)); -} - -.object-cover { - -o-object-fit: cover; - object-fit: cover; -} - -.object-center { - -o-object-position: center; - object-position: center; -} - -.p-2 { - padding: 0.5rem; -} - -.p-5 { - padding: 1.25rem; -} - -.px-3 { - padding-left: 0.75rem; - padding-right: 0.75rem; -} - -.px-5 { - padding-left: 1.25rem; - padding-right: 1.25rem; -} - -.px-6 { - padding-left: 1.5rem; - padding-right: 1.5rem; -} - -.py-1 { - padding-top: 0.25rem; - padding-bottom: 0.25rem; -} - -.py-2 { - padding-top: 0.5rem; - padding-bottom: 0.5rem; -} - -.py-24 { - padding-top: 6rem; - padding-bottom: 6rem; -} - -.text-center { - text-align: center; -} - -.text-3xl { - font-size: 1.875rem; - line-height: 2.25rem; -} - -.text-base { - font-size: 1rem; - line-height: 1.5rem; -} - -.text-lg { - font-size: 1.125rem; - line-height: 1.75rem; -} - -.text-xl { - font-size: 1.25rem; - line-height: 1.75rem; -} - -.font-medium { - font-weight: 500; -} - -.leading-relaxed { - line-height: 1.625; -} - -.text-gray-400 { - --tw-text-opacity: 1; - color: rgb(156 163 175 / var(--tw-text-opacity)); -} - -.text-white { - --tw-text-opacity: 1; - color: rgb(255 255 255 / var(--tw-text-opacity)); -} - -.hover\:bg-gray-700:hover { - --tw-bg-opacity: 1; - background-color: rgb(55 65 81 / var(--tw-bg-opacity)); -} - -.hover\:bg-indigo-600:hover { - --tw-bg-opacity: 1; - background-color: rgb(79 70 229 / var(--tw-bg-opacity)); -} - -.hover\:text-white:hover { - --tw-text-opacity: 1; - color: rgb(255 255 255 / var(--tw-text-opacity)); -} - -.focus\:outline-none:focus { - outline: 2px solid transparent; - outline-offset: 2px; -} - -@media (min-width: 640px) { - .sm\:text-4xl { - font-size: 2.25rem; - line-height: 2.5rem; - } -} - -@media (min-width: 768px) { - .md\:mb-0 { - margin-bottom: 0px; - } - - .md\:ml-auto { - margin-left: auto; - } - - .md\:mt-0 { - margin-top: 0px; - } - - .md\:w-1\/2 { - width: 50%; - } - - .md\:flex-row { - flex-direction: row; - } - - .md\:items-start { - align-items: flex-start; - } - - .md\:pr-16 { - padding-right: 4rem; - } - - .md\:text-left { - text-align: left; - } -} - -@media (min-width: 1024px) { - .lg\:inline-block { - display: inline-block; - } - - .lg\:w-full { - width: 100%; - } - - .lg\:max-w-lg { - max-width: 32rem; - } - - .lg\:flex-grow { - flex-grow: 1; - } - - .lg\:pr-24 { - padding-right: 6rem; - } -} \ No newline at end of file diff --git a/example-projects/tailwind/src/main.rs b/example-projects/tailwind/src/main.rs deleted file mode 100644 index 84c65b3692..0000000000 --- a/example-projects/tailwind/src/main.rs +++ /dev/null @@ -1,102 +0,0 @@ -#![allow(non_snake_case)] - -use dioxus::prelude::*; - -fn main() { - dioxus::launch(app); -} - -pub fn app() -> Element { - let grey_background = true; - rsx!( - document::Stylesheet { href: asset!("/public/tailwind.css") } - div { - header { - class: "text-gray-400 body-font", - // you can use optional attributes to optionally apply a tailwind class - class: if grey_background { - "bg-gray-900" - }, - div { class: "container mx-auto flex flex-wrap p-5 flex-col md:flex-row items-center", - a { class: "flex title-font font-medium items-center text-white mb-4 md:mb-0", - StacksIcon {} - span { class: "ml-3 text-xl", "Hello Dioxus!" } - } - nav { class: "md:ml-auto flex flex-wrap items-center text-base justify-center", - a { class: "mr-5 hover:text-white", "First Link" } - a { class: "mr-5 hover:text-white", "Second Link" } - a { class: "mr-5 hover:text-white", "Third Link" } - a { class: "mr-5 hover:text-white", "Fourth Link" } - } - button { class: "inline-flex items-center bg-gray-800 border-0 py-1 px-3 focus:outline-none hover:bg-gray-700 rounded text-base mt-4 md:mt-0", - "Button" - RightArrowIcon {} - } - } - } - - section { class: "text-gray-400 bg-gray-900 body-font", - div { class: "container mx-auto flex px-5 py-24 md:flex-row flex-col items-center", - div { class: "lg:flex-grow md:w-1/2 lg:pr-24 md:pr-16 flex flex-col md:items-start md:text-left mb-16 md:mb-0 items-center text-center", - h1 { class: "title-font sm:text-4xl text-3xl mb-4 font-medium text-white", - br { class: "hidden lg:inline-block" } - "Dioxus Sneak Peek" - } - p { class: "mb-8 leading-relaxed", - - "Dioxus is a new UI framework that makes it easy and simple to write cross-platform apps using web - technologies! It is functional, fast, and portable. Dioxus can run on the web, on the desktop, and - on mobile and embedded platforms." - } - div { class: "flex justify-center", - button { class: "inline-flex text-white bg-indigo-500 border-0 py-2 px-6 focus:outline-none hover:bg-indigo-600 rounded text-lg", - "Learn more" - } - button { class: "ml-4 inline-flex text-gray-400 bg-gray-800 border-0 py-2 px-6 focus:outline-none hover:bg-gray-700 hover:text-white rounded text-lg", - "Build an app" - } - } - } - div { class: "lg:max-w-lg lg:w-full md:w-1/2 w-5/6", - img { - class: "object-cover object-center rounded", - src: "https://i.imgur.com/oK6BLtw.png", - referrerpolicy: "no-referrer", - alt: "hero" - } - } - } - } - } - ) -} - -pub fn StacksIcon() -> Element { - rsx!( - svg { - fill: "none", - stroke: "currentColor", - stroke_linecap: "round", - stroke_linejoin: "round", - stroke_width: "2", - class: "w-10 h-10 text-white p-2 bg-indigo-500 rounded-full", - view_box: "0 0 24 24", - path { d: "M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5" } - } - ) -} - -pub fn RightArrowIcon() -> Element { - rsx!( - svg { - fill: "none", - stroke: "currentColor", - stroke_linecap: "round", - stroke_linejoin: "round", - stroke_width: "2", - class: "w-4 h-4 ml-1", - view_box: "0 0 24 24", - path { d: "M5 12h14M12 5l7 7-7 7" } - } - ) -} diff --git a/example-projects/tailwind/tailwind.config.js b/example-projects/tailwind/tailwind.config.js deleted file mode 100644 index 2a69d5803a..0000000000 --- a/example-projects/tailwind/tailwind.config.js +++ /dev/null @@ -1,9 +0,0 @@ -/** @type {import('tailwindcss').Config} */ -module.exports = { - mode: "all", - content: ["./src/**/*.{rs,html,css}", "./dist/**/*.html"], - theme: { - extend: {}, - }, - plugins: [], -}; From a1468c027fc375b665a882eef7c5dc1fcb12b02b Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Sun, 15 Sep 2024 18:06:06 -0700 Subject: [PATCH 107/139] drop changes to examples --- examples/PWA-example/Cargo.toml | 17 + examples/PWA-example/Dioxus.toml | 27 + examples/PWA-example/LICENSE | 21 + examples/PWA-example/README.md | 45 + examples/PWA-example/index.html | 30 + examples/PWA-example/public/favicon.ico | Bin 0 -> 23104 bytes examples/PWA-example/public/logo_192.png | Bin 0 -> 11198 bytes examples/PWA-example/public/logo_512.png | Bin 0 -> 48151 bytes examples/PWA-example/public/manifest.json | 34 + examples/PWA-example/public/sw.js | 198 + examples/PWA-example/src/main.rs | 21 + examples/all_events.rs | 6 +- examples/assets/purecss.css | 12 - examples/backgrounded_futures.rs | 2 +- examples/calculator.rs | 6 +- examples/calculator_mutable.rs | 4 +- examples/clock.rs | 10 +- examples/control_focus.rs | 9 +- examples/counters.rs | 6 +- examples/crm.rs | 11 +- examples/custom_assets.rs | 32 +- examples/custom_html.rs | 2 +- examples/custom_menu.rs | 2 +- examples/disabled.rs | 2 +- examples/dog_app.rs | 2 +- examples/dynamic_asset.rs | 6 +- examples/errors.rs | 36 +- examples/eval.rs | 29 +- examples/file_explorer.rs | 8 +- examples/file_upload.rs | 6 +- examples/flat_router.rs | 6 +- examples/form.rs | 2 +- examples/future.rs | 2 +- examples/generic_component.rs | 2 +- examples/global.rs | 6 +- examples/hash_fragment_state.rs | 4 +- examples/hello_world.rs | 2 +- examples/hydration.rs | 2 +- examples/image_generator_openai.rs | 4 +- examples/link.rs | 6 +- examples/login_form.rs | 2 +- examples/memo_chain.rs | 2 +- examples/meta.rs | 13 +- examples/mobile_demo/.gitignore | 10 + examples/mobile_demo/Cargo.lock | 4007 +++++++++++ examples/mobile_demo/Cargo.toml | 51 + examples/mobile_demo/README.md | 11 + examples/mobile_demo/mobile.toml | 8 + examples/mobile_demo/src/index.html | 12 + examples/mobile_demo/src/lib.rs | 90 + examples/multiwindow.rs | 2 +- examples/nested_listeners.rs | 2 +- .../openid_connect_demo/.cargo/config.toml | 5 + examples/openid_connect_demo/.gitignore | 3 + examples/openid_connect_demo/Cargo.lock | 6372 +++++++++++++++++ examples/openid_connect_demo/Cargo.toml | 36 + examples/openid_connect_demo/Dioxus.toml | 35 + examples/openid_connect_demo/README.md | 12 + examples/openid_connect_demo/src/constants.rs | 2 + examples/openid_connect_demo/src/main.rs | 36 + examples/openid_connect_demo/src/model/mod.rs | 1 + .../openid_connect_demo/src/model/user.rs | 7 + examples/openid_connect_demo/src/oidc.rs | 127 + .../openid_connect_demo/src/props/client.rs | 20 + examples/openid_connect_demo/src/props/mod.rs | 1 + examples/openid_connect_demo/src/router.rs | 16 + examples/openid_connect_demo/src/storage.rs | 35 + .../openid_connect_demo/src/views/header.rs | 221 + .../openid_connect_demo/src/views/home.rs | 5 + .../openid_connect_demo/src/views/login.rs | 86 + examples/openid_connect_demo/src/views/mod.rs | 4 + .../src/views/not_found.rs | 10 + examples/optional_props.rs | 2 +- examples/overlay.rs | 8 +- examples/popup.rs | 2 +- examples/query_segment_search.rs | 6 +- examples/read_size.rs | 4 +- examples/readme.rs | 2 +- examples/reducer.rs | 6 +- examples/resize.rs | 4 +- examples/router.rs | 6 +- examples/router_resource.rs | 2 +- examples/rsx_usage.rs | 5 +- examples/scroll_to_top.rs | 2 +- examples/shortcut.rs | 2 +- examples/shorthand.rs | 2 +- examples/signals.rs | 2 +- examples/simple_list.rs | 2 +- examples/simple_router.rs | 2 +- examples/streams.rs | 2 +- examples/suspense.rs | 2 +- examples/svg.rs | 2 +- examples/tailwind/.gitignore | 1 + examples/tailwind/Cargo.toml | 20 + examples/tailwind/Dioxus.toml | 27 + examples/tailwind/README.md | 7 + examples/tailwind/input.css | 3 + examples/tailwind/public/tailwind.css | 833 +++ examples/tailwind/src/main.rs | 103 + examples/tailwind/tailwind.config.js | 9 + examples/title.rs | 5 +- examples/todomvc.rs | 6 +- examples/weather_app.rs | 4 +- examples/web_component.rs | 2 +- examples/window_event.rs | 4 +- examples/window_focus.rs | 2 +- examples/window_zoom.rs | 2 +- examples/xss_safety.rs | 2 +- 108 files changed, 12795 insertions(+), 164 deletions(-) create mode 100644 examples/PWA-example/Cargo.toml create mode 100644 examples/PWA-example/Dioxus.toml create mode 100644 examples/PWA-example/LICENSE create mode 100644 examples/PWA-example/README.md create mode 100644 examples/PWA-example/index.html create mode 100644 examples/PWA-example/public/favicon.ico create mode 100644 examples/PWA-example/public/logo_192.png create mode 100644 examples/PWA-example/public/logo_512.png create mode 100644 examples/PWA-example/public/manifest.json create mode 100644 examples/PWA-example/public/sw.js create mode 100644 examples/PWA-example/src/main.rs delete mode 100644 examples/assets/purecss.css create mode 100644 examples/mobile_demo/.gitignore create mode 100644 examples/mobile_demo/Cargo.lock create mode 100644 examples/mobile_demo/Cargo.toml create mode 100644 examples/mobile_demo/README.md create mode 100644 examples/mobile_demo/mobile.toml create mode 100644 examples/mobile_demo/src/index.html create mode 100644 examples/mobile_demo/src/lib.rs create mode 100644 examples/openid_connect_demo/.cargo/config.toml create mode 100644 examples/openid_connect_demo/.gitignore create mode 100644 examples/openid_connect_demo/Cargo.lock create mode 100644 examples/openid_connect_demo/Cargo.toml create mode 100644 examples/openid_connect_demo/Dioxus.toml create mode 100644 examples/openid_connect_demo/README.md create mode 100644 examples/openid_connect_demo/src/constants.rs create mode 100644 examples/openid_connect_demo/src/main.rs create mode 100644 examples/openid_connect_demo/src/model/mod.rs create mode 100644 examples/openid_connect_demo/src/model/user.rs create mode 100644 examples/openid_connect_demo/src/oidc.rs create mode 100644 examples/openid_connect_demo/src/props/client.rs create mode 100644 examples/openid_connect_demo/src/props/mod.rs create mode 100644 examples/openid_connect_demo/src/router.rs create mode 100644 examples/openid_connect_demo/src/storage.rs create mode 100644 examples/openid_connect_demo/src/views/header.rs create mode 100644 examples/openid_connect_demo/src/views/home.rs create mode 100644 examples/openid_connect_demo/src/views/login.rs create mode 100644 examples/openid_connect_demo/src/views/mod.rs create mode 100644 examples/openid_connect_demo/src/views/not_found.rs create mode 100644 examples/tailwind/.gitignore create mode 100644 examples/tailwind/Cargo.toml create mode 100644 examples/tailwind/Dioxus.toml create mode 100644 examples/tailwind/README.md create mode 100644 examples/tailwind/input.css create mode 100644 examples/tailwind/public/tailwind.css create mode 100644 examples/tailwind/src/main.rs create mode 100644 examples/tailwind/tailwind.config.js diff --git a/examples/PWA-example/Cargo.toml b/examples/PWA-example/Cargo.toml new file mode 100644 index 0000000000..bee204f20a --- /dev/null +++ b/examples/PWA-example/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "dioxus-pwa-example" +version = "0.1.0" +authors = ["Antonio Curavalea "] +edition = "2021" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +dioxus = { workspace = true, features = ["web"] } + +log = "0.4.6" + +# WebAssembly Debug +wasm-logger = "0.2.0" +console_error_panic_hook = "0.1.7" diff --git a/examples/PWA-example/Dioxus.toml b/examples/PWA-example/Dioxus.toml new file mode 100644 index 0000000000..a831a28ff7 --- /dev/null +++ b/examples/PWA-example/Dioxus.toml @@ -0,0 +1,27 @@ +[application] + +# App (Project) Name +name = "dioxus-pwa-example" + +# Dioxus App Default Platform +# desktop, web, mobile, ssr +default_platform = "web" + +# `build` & `serve` dist path +out_dir = "dist" + +# resource (public) file folder +asset_dir = "public" + +[web.app] + +# HTML title tag content +title = "dioxus | ⛺" + +[web.watcher] + +# when watcher trigger, regenerate the `index.html` +reload_html = true + +# which files or dirs will be watcher monitoring +watch_path = ["src", "public"] diff --git a/examples/PWA-example/LICENSE b/examples/PWA-example/LICENSE new file mode 100644 index 0000000000..bcdd828e9c --- /dev/null +++ b/examples/PWA-example/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Dioxus + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/examples/PWA-example/README.md b/examples/PWA-example/README.md new file mode 100644 index 0000000000..d501df2126 --- /dev/null +++ b/examples/PWA-example/README.md @@ -0,0 +1,45 @@ +# Dioxus PWA example + +This is a basic example of a progressive web app (PWA) using Dioxus and Dioxus CLI. +Currently PWA functionality requires the use of a service worker and manifest file, so this isn't 100% Rust yet. + +It is also very much usable as a template for your projects, if you're aiming to create a PWA. + +## Try the example + +Make sure you have Dioxus CLI installed (if you're unsure, run `cargo install dioxus-cli --locked`). + +You can run `dx serve` in this directory to start the web server locally, or run +`dx build --release` to build the project so you can deploy it on a separate web-server. + +## Project Structure + +``` +├── Cargo.toml +├── Dioxus.toml +├── index.html // Custom HTML is needed for this, to load the SW and manifest. +├── LICENSE +├── public +│ ├── favicon.ico +│ ├── logo_192.png +│ ├── logo_512.png +│ ├── manifest.json // The manifest file - edit this as you need to. +│ └── sw.js // The service worker - you must edit this for actual projects. +├── README.md +└── src + └── main.rs +``` + +## Resources + +If you're just getting started with PWAs, here are some useful resources: + +- [PWABuilder docs](https://docs.pwabuilder.com/#/) +- [MDN article on PWAs](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps) + +For service worker scripting (in JavaScript): + +- [Service worker guide from PWABuilder](https://docs.pwabuilder.com/#/home/sw-intro) +- [Service worker examples, also from PWABuilder](https://github.com/pwa-builder/pwabuilder-serviceworkers) + +If you want to stay as close to 100% Rust as possible, you can try using [wasi-worker](https://github.com/dunnock/wasi-worker) to replace the JS service worker file. The JSON manifest will still be required though. diff --git a/examples/PWA-example/index.html b/examples/PWA-example/index.html new file mode 100644 index 0000000000..44bfa59cc4 --- /dev/null +++ b/examples/PWA-example/index.html @@ -0,0 +1,30 @@ + + + + {app_title} + + + + + + {style_include} + + +
      + + {script_include} + + \ No newline at end of file diff --git a/examples/PWA-example/public/favicon.ico b/examples/PWA-example/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..b11015bdbacdb3b99e03555edb85d78467b37c7d GIT binary patch literal 23104 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 W+`s-{ME)24jHW23DqAIO8vH-~t)grI literal 0 HcmV?d00001 diff --git a/examples/PWA-example/public/logo_192.png b/examples/PWA-example/public/logo_192.png new file mode 100644 index 0000000000000000000000000000000000000000..112eb5ee49ca4e69d742a99269d1ae08a970dd7a GIT binary patch literal 11198 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 literal 0 HcmV?d00001 diff --git a/examples/PWA-example/public/logo_512.png b/examples/PWA-example/public/logo_512.png new file mode 100644 index 0000000000000000000000000000000000000000..43129772a892a5ca82dfdc8bb4d9027cc830ddf5 GIT binary patch literal 48151 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 Unavailable', { + status: 503, + statusText: 'Service Unavailable', + headers: new Headers({ + 'Content-Type': 'text/html' + }) + }); + } + }) + ); +}); + +/* The activate event fires after a service worker has been successfully installed. + It is most useful when phasing out an older version of a service worker, as at + this point you know that the new worker was installed correctly. In this example, + we delete old caches that don't match the version in the worker we just finished + installing. +*/ +self.addEventListener("activate", function (event) { + /* Just like with the install event, event.waitUntil blocks activate on a promise. + Activation will fail unless the promise is fulfilled. + */ + //console.log('WORKER: activate event in progress.'); + + event.waitUntil( + caches + /* This method returns a promise which will resolve to an array of available + cache keys. + */ + .keys() + .then(function (keys) { + // We return a promise that settles when all outdated caches are deleted. + return Promise.all( + keys + .filter(function (key) { + // Filter by keys that don't start with the latest version prefix. + return !key.startsWith(version); + }) + .map(function (key) { + /* Return a promise that's fulfilled + when each outdated cache is deleted. + */ + return caches.delete(key); + }) + ); + }) + .then(function () { + //console.log('WORKER: activate completed.'); + }) + ); +}); diff --git a/examples/PWA-example/src/main.rs b/examples/PWA-example/src/main.rs new file mode 100644 index 0000000000..3e4d5b2361 --- /dev/null +++ b/examples/PWA-example/src/main.rs @@ -0,0 +1,21 @@ +use dioxus::prelude::*; + +fn main() { + // init debug tool for WebAssembly + wasm_logger::init(wasm_logger::Config::default()); + console_error_panic_hook::set_once(); + + launch(app); +} + +fn app() -> Element { + rsx! ( + div { style: "text-align: center;", + h1 { "🌗 Dioxus 🚀" } + h3 { "Frontend that scales." } + p { + "Dioxus is a portable, performant, and ergonomic framework for building cross-platform user interfaces in Rust." + } + } + ) +} diff --git a/examples/all_events.rs b/examples/all_events.rs index 4e4ca3d6d5..2a04ee2bb3 100644 --- a/examples/all_events.rs +++ b/examples/all_events.rs @@ -6,8 +6,10 @@ use dioxus::prelude::*; use std::{collections::VecDeque, fmt::Debug, rc::Rc}; +const STYLE: &str = asset!("./examples/assets/events.css"); + fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { @@ -24,7 +26,7 @@ fn app() -> Element { }; rsx! { - document::Stylesheet { href: asset!("/examples/assets/events.css") } + head::Link { rel: "stylesheet", href: STYLE } div { id: "container", // focusing is necessary to catch keyboard events div { id: "receiver", tabindex: 0, diff --git a/examples/assets/purecss.css b/examples/assets/purecss.css deleted file mode 100644 index dd301cc364..0000000000 --- a/examples/assets/purecss.css +++ /dev/null @@ -1,12 +0,0 @@ -/*! -Pure v2.0.6 -Copyright 2013 Yahoo! -Licensed under the BSD License. -https://github.com/pure-css/pure/blob/master/LICENSE -*/ -/*! -normalize.css v | MIT License | git.io/normalize -Copyright (c) Nicolas Gallagher and Jonathan Neal -*/ -/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ -html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{-webkit-box-sizing:content-box;box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{-webkit-box-sizing:border-box;box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{-webkit-box-sizing:border-box;box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}html{font-family:sans-serif}.hidden,[hidden]{display:none!important}.pure-img{max-width:100%;height:auto;display:block}.pure-g{letter-spacing:-.31em;text-rendering:optimizespeed;font-family:FreeSans,Arimo,"Droid Sans",Helvetica,Arial,sans-serif;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-line-pack:start;align-content:flex-start}@media all and (-ms-high-contrast:none),(-ms-high-contrast:active){table .pure-g{display:block}}.opera-only :-o-prefocus,.pure-g{word-spacing:-0.43em}.pure-u{display:inline-block;letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-g [class*=pure-u]{font-family:sans-serif}.pure-u-1,.pure-u-1-1,.pure-u-1-12,.pure-u-1-2,.pure-u-1-24,.pure-u-1-3,.pure-u-1-4,.pure-u-1-5,.pure-u-1-6,.pure-u-1-8,.pure-u-10-24,.pure-u-11-12,.pure-u-11-24,.pure-u-12-24,.pure-u-13-24,.pure-u-14-24,.pure-u-15-24,.pure-u-16-24,.pure-u-17-24,.pure-u-18-24,.pure-u-19-24,.pure-u-2-24,.pure-u-2-3,.pure-u-2-5,.pure-u-20-24,.pure-u-21-24,.pure-u-22-24,.pure-u-23-24,.pure-u-24-24,.pure-u-3-24,.pure-u-3-4,.pure-u-3-5,.pure-u-3-8,.pure-u-4-24,.pure-u-4-5,.pure-u-5-12,.pure-u-5-24,.pure-u-5-5,.pure-u-5-6,.pure-u-5-8,.pure-u-6-24,.pure-u-7-12,.pure-u-7-24,.pure-u-7-8,.pure-u-8-24,.pure-u-9-24{display:inline-block;letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-u-1-24{width:4.1667%}.pure-u-1-12,.pure-u-2-24{width:8.3333%}.pure-u-1-8,.pure-u-3-24{width:12.5%}.pure-u-1-6,.pure-u-4-24{width:16.6667%}.pure-u-1-5{width:20%}.pure-u-5-24{width:20.8333%}.pure-u-1-4,.pure-u-6-24{width:25%}.pure-u-7-24{width:29.1667%}.pure-u-1-3,.pure-u-8-24{width:33.3333%}.pure-u-3-8,.pure-u-9-24{width:37.5%}.pure-u-2-5{width:40%}.pure-u-10-24,.pure-u-5-12{width:41.6667%}.pure-u-11-24{width:45.8333%}.pure-u-1-2,.pure-u-12-24{width:50%}.pure-u-13-24{width:54.1667%}.pure-u-14-24,.pure-u-7-12{width:58.3333%}.pure-u-3-5{width:60%}.pure-u-15-24,.pure-u-5-8{width:62.5%}.pure-u-16-24,.pure-u-2-3{width:66.6667%}.pure-u-17-24{width:70.8333%}.pure-u-18-24,.pure-u-3-4{width:75%}.pure-u-19-24{width:79.1667%}.pure-u-4-5{width:80%}.pure-u-20-24,.pure-u-5-6{width:83.3333%}.pure-u-21-24,.pure-u-7-8{width:87.5%}.pure-u-11-12,.pure-u-22-24{width:91.6667%}.pure-u-23-24{width:95.8333%}.pure-u-1,.pure-u-1-1,.pure-u-24-24,.pure-u-5-5{width:100%}.pure-button{display:inline-block;line-height:normal;white-space:nowrap;vertical-align:middle;text-align:center;cursor:pointer;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-box-sizing:border-box;box-sizing:border-box}.pure-button::-moz-focus-inner{padding:0;border:0}.pure-button-group{letter-spacing:-.31em;text-rendering:optimizespeed}.opera-only :-o-prefocus,.pure-button-group{word-spacing:-0.43em}.pure-button-group .pure-button{letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-button{font-family:inherit;font-size:100%;padding:.5em 1em;color:rgba(0,0,0,.8);border:none transparent;background-color:#e6e6e6;text-decoration:none;border-radius:2px}.pure-button-hover,.pure-button:focus,.pure-button:hover{background-image:-webkit-gradient(linear,left top,left bottom,from(transparent),color-stop(40%,rgba(0,0,0,.05)),to(rgba(0,0,0,.1)));background-image:linear-gradient(transparent,rgba(0,0,0,.05) 40%,rgba(0,0,0,.1))}.pure-button:focus{outline:0}.pure-button-active,.pure-button:active{-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.15) inset,0 0 6px rgba(0,0,0,.2) inset;box-shadow:0 0 0 1px rgba(0,0,0,.15) inset,0 0 6px rgba(0,0,0,.2) inset;border-color:#000}.pure-button-disabled,.pure-button-disabled:active,.pure-button-disabled:focus,.pure-button-disabled:hover,.pure-button[disabled]{border:none;background-image:none;opacity:.4;cursor:not-allowed;-webkit-box-shadow:none;box-shadow:none;pointer-events:none}.pure-button-hidden{display:none}.pure-button-primary,.pure-button-selected,a.pure-button-primary,a.pure-button-selected{background-color:#0078e7;color:#fff}.pure-button-group .pure-button{margin:0;border-radius:0;border-right:1px solid rgba(0,0,0,.2)}.pure-button-group .pure-button:first-child{border-top-left-radius:2px;border-bottom-left-radius:2px}.pure-button-group .pure-button:last-child{border-top-right-radius:2px;border-bottom-right-radius:2px;border-right:none}.pure-form input[type=color],.pure-form input[type=date],.pure-form input[type=datetime-local],.pure-form input[type=datetime],.pure-form input[type=email],.pure-form input[type=month],.pure-form input[type=number],.pure-form input[type=password],.pure-form input[type=search],.pure-form input[type=tel],.pure-form input[type=text],.pure-form input[type=time],.pure-form input[type=url],.pure-form input[type=week],.pure-form select,.pure-form textarea{padding:.5em .6em;display:inline-block;border:1px solid #ccc;-webkit-box-shadow:inset 0 1px 3px #ddd;box-shadow:inset 0 1px 3px #ddd;border-radius:4px;vertical-align:middle;-webkit-box-sizing:border-box;box-sizing:border-box}.pure-form input:not([type]){padding:.5em .6em;display:inline-block;border:1px solid #ccc;-webkit-box-shadow:inset 0 1px 3px #ddd;box-shadow:inset 0 1px 3px #ddd;border-radius:4px;-webkit-box-sizing:border-box;box-sizing:border-box}.pure-form input[type=color]{padding:.2em .5em}.pure-form input[type=color]:focus,.pure-form input[type=date]:focus,.pure-form input[type=datetime-local]:focus,.pure-form input[type=datetime]:focus,.pure-form input[type=email]:focus,.pure-form input[type=month]:focus,.pure-form input[type=number]:focus,.pure-form input[type=password]:focus,.pure-form input[type=search]:focus,.pure-form input[type=tel]:focus,.pure-form input[type=text]:focus,.pure-form input[type=time]:focus,.pure-form input[type=url]:focus,.pure-form input[type=week]:focus,.pure-form select:focus,.pure-form textarea:focus{outline:0;border-color:#129fea}.pure-form input:not([type]):focus{outline:0;border-color:#129fea}.pure-form input[type=checkbox]:focus,.pure-form input[type=file]:focus,.pure-form input[type=radio]:focus{outline:thin solid #129FEA;outline:1px auto #129FEA}.pure-form .pure-checkbox,.pure-form .pure-radio{margin:.5em 0;display:block}.pure-form input[type=color][disabled],.pure-form input[type=date][disabled],.pure-form input[type=datetime-local][disabled],.pure-form input[type=datetime][disabled],.pure-form input[type=email][disabled],.pure-form input[type=month][disabled],.pure-form input[type=number][disabled],.pure-form input[type=password][disabled],.pure-form input[type=search][disabled],.pure-form input[type=tel][disabled],.pure-form input[type=text][disabled],.pure-form input[type=time][disabled],.pure-form input[type=url][disabled],.pure-form input[type=week][disabled],.pure-form select[disabled],.pure-form textarea[disabled]{cursor:not-allowed;background-color:#eaeded;color:#cad2d3}.pure-form input:not([type])[disabled]{cursor:not-allowed;background-color:#eaeded;color:#cad2d3}.pure-form input[readonly],.pure-form select[readonly],.pure-form textarea[readonly]{background-color:#eee;color:#777;border-color:#ccc}.pure-form input:focus:invalid,.pure-form select:focus:invalid,.pure-form textarea:focus:invalid{color:#b94a48;border-color:#e9322d}.pure-form input[type=checkbox]:focus:invalid:focus,.pure-form input[type=file]:focus:invalid:focus,.pure-form input[type=radio]:focus:invalid:focus{outline-color:#e9322d}.pure-form select{height:2.25em;border:1px solid #ccc;background-color:#fff}.pure-form select[multiple]{height:auto}.pure-form label{margin:.5em 0 .2em}.pure-form fieldset{margin:0;padding:.35em 0 .75em;border:0}.pure-form legend{display:block;width:100%;padding:.3em 0;margin-bottom:.3em;color:#333;border-bottom:1px solid #e5e5e5}.pure-form-stacked input[type=color],.pure-form-stacked input[type=date],.pure-form-stacked input[type=datetime-local],.pure-form-stacked input[type=datetime],.pure-form-stacked input[type=email],.pure-form-stacked input[type=file],.pure-form-stacked input[type=month],.pure-form-stacked input[type=number],.pure-form-stacked input[type=password],.pure-form-stacked input[type=search],.pure-form-stacked input[type=tel],.pure-form-stacked input[type=text],.pure-form-stacked input[type=time],.pure-form-stacked input[type=url],.pure-form-stacked input[type=week],.pure-form-stacked label,.pure-form-stacked select,.pure-form-stacked textarea{display:block;margin:.25em 0}.pure-form-stacked input:not([type]){display:block;margin:.25em 0}.pure-form-aligned input,.pure-form-aligned select,.pure-form-aligned textarea,.pure-form-message-inline{display:inline-block;vertical-align:middle}.pure-form-aligned textarea{vertical-align:top}.pure-form-aligned .pure-control-group{margin-bottom:.5em}.pure-form-aligned .pure-control-group label{text-align:right;display:inline-block;vertical-align:middle;width:10em;margin:0 1em 0 0}.pure-form-aligned .pure-controls{margin:1.5em 0 0 11em}.pure-form .pure-input-rounded,.pure-form input.pure-input-rounded{border-radius:2em;padding:.5em 1em}.pure-form .pure-group fieldset{margin-bottom:10px}.pure-form .pure-group input,.pure-form .pure-group textarea{display:block;padding:10px;margin:0 0 -1px;border-radius:0;position:relative;top:-1px}.pure-form .pure-group input:focus,.pure-form .pure-group textarea:focus{z-index:3}.pure-form .pure-group input:first-child,.pure-form .pure-group textarea:first-child{top:1px;border-radius:4px 4px 0 0;margin:0}.pure-form .pure-group input:first-child:last-child,.pure-form .pure-group textarea:first-child:last-child{top:1px;border-radius:4px;margin:0}.pure-form .pure-group input:last-child,.pure-form .pure-group textarea:last-child{top:-2px;border-radius:0 0 4px 4px;margin:0}.pure-form .pure-group button{margin:.35em 0}.pure-form .pure-input-1{width:100%}.pure-form .pure-input-3-4{width:75%}.pure-form .pure-input-2-3{width:66%}.pure-form .pure-input-1-2{width:50%}.pure-form .pure-input-1-3{width:33%}.pure-form .pure-input-1-4{width:25%}.pure-form-message-inline{display:inline-block;padding-left:.3em;color:#666;vertical-align:middle;font-size:.875em}.pure-form-message{display:block;color:#666;font-size:.875em}@media only screen and (max-width :480px){.pure-form button[type=submit]{margin:.7em 0 0}.pure-form input:not([type]),.pure-form input[type=color],.pure-form input[type=date],.pure-form input[type=datetime-local],.pure-form input[type=datetime],.pure-form input[type=email],.pure-form input[type=month],.pure-form input[type=number],.pure-form input[type=password],.pure-form input[type=search],.pure-form input[type=tel],.pure-form input[type=text],.pure-form input[type=time],.pure-form input[type=url],.pure-form input[type=week],.pure-form label{margin-bottom:.3em;display:block}.pure-group input:not([type]),.pure-group input[type=color],.pure-group input[type=date],.pure-group input[type=datetime-local],.pure-group input[type=datetime],.pure-group input[type=email],.pure-group input[type=month],.pure-group input[type=number],.pure-group input[type=password],.pure-group input[type=search],.pure-group input[type=tel],.pure-group input[type=text],.pure-group input[type=time],.pure-group input[type=url],.pure-group input[type=week]{margin-bottom:0}.pure-form-aligned .pure-control-group label{margin-bottom:.3em;text-align:left;display:block;width:100%}.pure-form-aligned .pure-controls{margin:1.5em 0 0 0}.pure-form-message,.pure-form-message-inline{display:block;font-size:.75em;padding:.2em 0 .8em}}.pure-menu{-webkit-box-sizing:border-box;box-sizing:border-box}.pure-menu-fixed{position:fixed;left:0;top:0;z-index:3}.pure-menu-item,.pure-menu-list{position:relative}.pure-menu-list{list-style:none;margin:0;padding:0}.pure-menu-item{padding:0;margin:0;height:100%}.pure-menu-heading,.pure-menu-link{display:block;text-decoration:none;white-space:nowrap}.pure-menu-horizontal{width:100%;white-space:nowrap}.pure-menu-horizontal .pure-menu-list{display:inline-block}.pure-menu-horizontal .pure-menu-heading,.pure-menu-horizontal .pure-menu-item,.pure-menu-horizontal .pure-menu-separator{display:inline-block;vertical-align:middle}.pure-menu-item .pure-menu-item{display:block}.pure-menu-children{display:none;position:absolute;left:100%;top:0;margin:0;padding:0;z-index:3}.pure-menu-horizontal .pure-menu-children{left:0;top:auto;width:inherit}.pure-menu-active>.pure-menu-children,.pure-menu-allow-hover:hover>.pure-menu-children{display:block;position:absolute}.pure-menu-has-children>.pure-menu-link:after{padding-left:.5em;content:"\25B8";font-size:small}.pure-menu-horizontal .pure-menu-has-children>.pure-menu-link:after{content:"\25BE"}.pure-menu-scrollable{overflow-y:scroll;overflow-x:hidden}.pure-menu-scrollable .pure-menu-list{display:block}.pure-menu-horizontal.pure-menu-scrollable .pure-menu-list{display:inline-block}.pure-menu-horizontal.pure-menu-scrollable{white-space:nowrap;overflow-y:hidden;overflow-x:auto;padding:.5em 0}.pure-menu-horizontal .pure-menu-children .pure-menu-separator,.pure-menu-separator{background-color:#ccc;height:1px;margin:.3em 0}.pure-menu-horizontal .pure-menu-separator{width:1px;height:1.3em;margin:0 .3em}.pure-menu-horizontal .pure-menu-children .pure-menu-separator{display:block;width:auto}.pure-menu-heading{text-transform:uppercase;color:#565d64}.pure-menu-link{color:#777}.pure-menu-children{background-color:#fff}.pure-menu-heading,.pure-menu-link{padding:.5em 1em}.pure-menu-disabled{opacity:.5}.pure-menu-disabled .pure-menu-link:hover{background-color:transparent;cursor:default}.pure-menu-active>.pure-menu-link,.pure-menu-link:focus,.pure-menu-link:hover{background-color:#eee}.pure-menu-selected>.pure-menu-link,.pure-menu-selected>.pure-menu-link:visited{color:#000}.pure-table{border-collapse:collapse;border-spacing:0;empty-cells:show;border:1px solid #cbcbcb}.pure-table caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.pure-table td,.pure-table th{border-left:1px solid #cbcbcb;border-width:0 0 0 1px;font-size:inherit;margin:0;overflow:visible;padding:.5em 1em}.pure-table thead{background-color:#e0e0e0;color:#000;text-align:left;vertical-align:bottom}.pure-table td{background-color:transparent}.pure-table-odd td{background-color:#f2f2f2}.pure-table-striped tr:nth-child(2n-1) td{background-color:#f2f2f2}.pure-table-bordered td{border-bottom:1px solid #cbcbcb}.pure-table-bordered tbody>tr:last-child>td{border-bottom-width:0}.pure-table-horizontal td,.pure-table-horizontal th{border-width:0 0 1px 0;border-bottom:1px solid #cbcbcb}.pure-table-horizontal tbody>tr:last-child>td{border-bottom-width:0} diff --git a/examples/backgrounded_futures.rs b/examples/backgrounded_futures.rs index 4fabef09f7..4cafad439e 100644 --- a/examples/backgrounded_futures.rs +++ b/examples/backgrounded_futures.rs @@ -11,7 +11,7 @@ use async_std::task::sleep; use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/calculator.rs b/examples/calculator.rs index b373923f6b..9bf2a04618 100644 --- a/examples/calculator.rs +++ b/examples/calculator.rs @@ -12,10 +12,10 @@ use dioxus::events::*; use dioxus::html::input_data::keyboard_types::Key; use dioxus::prelude::*; -const STYLE: Asset = asset!("/examples/assets/calculator.css"); +const STYLE: &str = asset!("./examples/assets/calculator.css"); fn main() { - dioxus::launch::builder() + LaunchBuilder::desktop() .with_cfg(desktop!({ use dioxus::desktop::{Config, LogicalSize, WindowBuilder}; Config::new().with_window( @@ -54,7 +54,7 @@ fn app() -> Element { }; rsx! { - document::Stylesheet { href: STYLE } + head::Link { rel: "stylesheet", href: STYLE } div { id: "wrapper", div { class: "app", div { class: "calculator", tabindex: "0", onkeydown: handle_key_down_event, diff --git a/examples/calculator_mutable.rs b/examples/calculator_mutable.rs index 9d1ec05fbc..2f933ebbea 100644 --- a/examples/calculator_mutable.rs +++ b/examples/calculator_mutable.rs @@ -13,7 +13,7 @@ use dioxus::html::MouseEvent; use dioxus::prelude::*; fn main() { - dioxus::launch::builder() + LaunchBuilder::desktop() .with_cfg( Config::new().with_window( WindowBuilder::new() @@ -29,7 +29,7 @@ fn app() -> Element { let mut state = use_signal(Calculator::new); rsx! { - document::Stylesheet { href: asset!("/examples/assets/calculator.css") } + head::Link { rel: "stylesheet", href: asset!("./examples/assets/calculator.css") } div { id: "wrapper", div { class: "app", div { diff --git a/examples/clock.rs b/examples/clock.rs index d344311fae..dc168a5ec2 100644 --- a/examples/clock.rs +++ b/examples/clock.rs @@ -5,18 +5,18 @@ use async_std::task::sleep; use dioxus::prelude::*; use web_time::Instant; -const STYLE: Asset = asset!("/examples/assets/clock.css"); +const STYLE: &str = asset!("./examples/assets/clock.css"); fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { let mut millis = use_signal(|| 0); use_future(move || async move { - // Save our initial time - let start = std::time::Instant::now(); + // Save our initial timea + let start = Instant::now(); loop { sleep(std::time::Duration::from_millis(27)).await; @@ -36,7 +36,7 @@ fn app() -> Element { ); rsx! { - document::Stylesheet { href: STYLE } + head::Link { rel: "stylesheet", href: STYLE } div { id: "app", div { id: "title", "Carpe diem 🎉" } div { id: "clock-display", "{time}" } diff --git a/examples/control_focus.rs b/examples/control_focus.rs index 6a383fa662..c69293c56d 100644 --- a/examples/control_focus.rs +++ b/examples/control_focus.rs @@ -5,8 +5,13 @@ use std::rc::Rc; +use async_std::task::sleep; +use dioxus::prelude::*; + +const STYLE: &str = asset!("./examples/assets/roulette.css"); + fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { @@ -35,7 +40,7 @@ fn app() -> Element { }); rsx! { - document::Stylesheet { href: asset!("/examples/assets/roulette.css") } + head::Link { rel: "stylesheet", href: STYLE } h1 { "Input Roulette" } button { onclick: move |_| running.toggle(), "Toggle roulette" } div { id: "roulette-grid", diff --git a/examples/counters.rs b/examples/counters.rs index 63013a1cc5..f2d8c4203c 100644 --- a/examples/counters.rs +++ b/examples/counters.rs @@ -2,8 +2,10 @@ use dioxus::prelude::*; +const STYLE: &str = asset!("./examples/assets/counter.css"); + fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { @@ -14,7 +16,7 @@ fn app() -> Element { let sum = use_memo(move || counters.read().iter().copied().sum::()); rsx! { - document::Stylesheet { href: asset!("/examples/assets/counter.css") } + head::Link { rel: "stylesheet", href: STYLE } div { id: "controls", button { onclick: move |_| counters.write().push(0), "Add counter" } diff --git a/examples/crm.rs b/examples/crm.rs index ca83f58d40..21f07466cd 100644 --- a/examples/crm.rs +++ b/examples/crm.rs @@ -12,7 +12,7 @@ use dioxus::prelude::*; fn main() { - dioxus::builder() + LaunchBuilder::new() .with_cfg(desktop!({ use dioxus::desktop::{LogicalSize, WindowBuilder}; dioxus::desktop::Config::default() @@ -20,8 +20,13 @@ fn main() { })) .launch(|| { rsx! { - document::Stylesheet { href: asset!("/examples/assets/crm.css") } - document::Link { href: asset!("/examples/assets/purecss.css") } + head::Link { + rel: "stylesheet", + href: asset!("https://unpkg.com/purecss@2.0.6/build/pure-min.css"), + integrity: "sha384-Uu6IeWbM+gzNVXJcM9XV3SohHtmWE+3VGi496jvgX1jyvDTXfdK+rfZc8C1Aehk5", + crossorigin: "anonymous" + } + head::Link { rel: "stylesheet", href: asset!("./examples/assets/crm.css") } h1 { "Dioxus CRM Example" } Router:: {} } diff --git a/examples/custom_assets.rs b/examples/custom_assets.rs index 8a56530110..6c927d21a4 100644 --- a/examples/custom_assets.rs +++ b/examples/custom_assets.rs @@ -1,37 +1,27 @@ //! A simple example on how to use assets loading from the filesystem. //! -//! Dioxus provides an asset!() macro which properly handles asset loading and bundling for you. -//! For bundling, asset!() must be paired with a tool that handles mangansis-link sections. The dioxus-cli -//! handles this for you, but this means you can't just simply `cargo build --release` to build and -//! distribute your app. +//! If the feature "collect-assets" is enabled, the assets will be collected via the dioxus CLI and embedded into the +//! final bundle. This lets you do various useful things like minify, compress, and optimize your assets. //! -//! You can run this example with `cargo run --example assets` or `dx serve --example assets`. -//! When manganis is not active, the asset!() macro will fallback to the path of the asset on -//! your filesystem. +//! We can still use assets without the CLI middleware, but generally larger apps will benefit from it. + use dioxus::prelude::*; -/// asset!() will mark this asset as a dependency of the app without actually including it in the -/// generated code. This is better than include_str!() or include_bytes!() since it works -/// for web apps as well as native and mobile apps. -/// -/// When used with web apps, manganis will detect the import of the image, optimize it, and put it -/// in the output dist folder in the right location, ensuring no two images have the same name. -static IMAGE: ImageAsset = asset!("/examples/assets/logo.png".image().format(ImageType::Avif)); +#[cfg(not(feature = "collect-assets"))] +static ASSET_PATH: &str = "examples/assets/logo.png"; + +#[cfg(feature = "collect-assets")] +static ASSET_PATH: &str = asset!("examples/assets/logo.png".format(ImageType::Avif)); fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { rsx! { div { h1 { "This should show an image:" } - img { src: IMAGE } - - // temporarily keep support for these too - img { src: "/Users/jonkelley/Development/dioxus/examples/assets/logo.png" } - img { src: "/examples/assets/logo.png" } - img { src: "examples/assets/logo.png" } + img { src: ASSET_PATH.to_string() } } } } diff --git a/examples/custom_html.rs b/examples/custom_html.rs index f1ea4b91ad..dbbe334f05 100644 --- a/examples/custom_html.rs +++ b/examples/custom_html.rs @@ -4,7 +4,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch::builder() + LaunchBuilder::new() .with_cfg( dioxus::desktop::Config::new().with_custom_index( r#" diff --git a/examples/custom_menu.rs b/examples/custom_menu.rs index da1085bfd5..f78f33c884 100644 --- a/examples/custom_menu.rs +++ b/examples/custom_menu.rs @@ -28,7 +28,7 @@ fn main() { let config = dioxus::desktop::Config::new().with_menu(menu); // Launch the app with the custom menu - dioxus::launch::builder().with_cfg(config).launch(app) + LaunchBuilder::new().with_cfg(config).launch(app) } fn app() -> Element { diff --git a/examples/disabled.rs b/examples/disabled.rs index b48b1a912e..6aaa70b9d4 100644 --- a/examples/disabled.rs +++ b/examples/disabled.rs @@ -5,7 +5,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/dog_app.rs b/examples/dog_app.rs index 09bf5f0a96..1c53c8731f 100644 --- a/examples/dog_app.rs +++ b/examples/dog_app.rs @@ -11,7 +11,7 @@ use dioxus::prelude::*; use std::collections::HashMap; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/dynamic_asset.rs b/examples/dynamic_asset.rs index b741ba57af..14a33c4809 100644 --- a/examples/dynamic_asset.rs +++ b/examples/dynamic_asset.rs @@ -7,8 +7,10 @@ use dioxus::desktop::{use_asset_handler, wry::http::Response}; use dioxus::prelude::*; +const STYLE: &str = asset!("./examples/assets/custom_assets.css"); + fn main() { - dioxus::launch(app); + launch_desktop(app); } fn app() -> Element { @@ -22,7 +24,7 @@ fn app() -> Element { }); rsx! { - document::Stylesheet { href: asset!("/examples/assets/custom_assets.css") } + head::Link { rel: "stylesheet", href: STYLE } h1 { "Dynamic Assets" } img { src: "/logos/logo.png" } } diff --git a/examples/errors.rs b/examples/errors.rs index fe16598e95..4727cb5e03 100644 --- a/examples/errors.rs +++ b/examples/errors.rs @@ -11,11 +11,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(|| { - rsx! { - Router:: {} - } - }); + launch(|| rsx! { Router:: {} }); } /// You can use an ErrorBoundary to catch errors in children and display a warning @@ -38,8 +34,11 @@ fn ParseNumber() -> Element { h1 { "Error handler demo" } button { onclick: move |_| { + // You can return a result from an event handler which lets you easily quit rendering early if something fails let data: i32 = "0.5".parse()?; + println!("parsed {data}"); + Ok(()) }, "Click to throw an error" @@ -59,7 +58,10 @@ fn Show() -> Element { if let Some(error) = error.show() { {error} } else { - pre { color: "red", "{error}" } + pre { + color: "red", + "{error}" + } } } } @@ -86,10 +88,15 @@ fn ParseNumberWithShow() -> Element { border_width: "2px", border_radius: "5px", p { "Failed to parse data" } - Link { to: Route::Home {}, "Go back to the homepage" } + Link { + to: Route::Home {}, + "Go back to the homepage" + } } })?; + println!("parsed {data}"); + Ok(()) }, "Click to throw an error" @@ -132,13 +139,22 @@ fn Home() -> Element { rsx! { ul { li { - Link { to: Route::Simple {}, "Simple errors" } + Link { + to: Route::Simple {}, + "Simple errors" + } } li { - Link { to: Route::Panic {}, "Capture panics" } + Link { + to: Route::Panic {}, + "Capture panics" + } } li { - Link { to: Route::Show {}, "Show errors" } + Link { + to: Route::Show {}, + "Show errors" + } } } } diff --git a/examples/eval.rs b/examples/eval.rs index 68d26c8b09..9cfd15e941 100644 --- a/examples/eval.rs +++ b/examples/eval.rs @@ -7,7 +7,7 @@ use async_std::task::sleep; use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { @@ -19,24 +19,29 @@ fn app() -> Element { // The `eval` is available in the prelude - and simply takes a block of JS. // Dioxus' eval is interesting since it allows sending messages to and from the JS code using the `await dioxus.recv()` // builtin function. This allows you to create a two-way communication channel between Rust and JS. - let mut eval = document::eval( + let mut eval = eval( r#" - return "hi from JS!"; + dioxus.send("Hi from JS!"); + let msg = await dioxus.recv(); + console.log(msg); + return "hi from JS!"; "#, ); - // This will print "Hi from JS!" and "Hi from Rust!". - let res = eval.await; + // Send a message to the JS code. + eval.send("Hi from Rust!".into()).unwrap(); + + // Our line on the JS side will log the message and then return "hello world". + let res = eval.recv().await.unwrap(); - println!("hello from js! {:?}", res); + // This will print "Hi from JS!" and "Hi from Rust!". + println!("{:?}", eval.await); res }); - todo!() - // future.read_unchecked().as_ref().map(|f| match f { - // Some(Ok(v)) => rsx!( p { "{v:?}" } ), - // Some(Err(e)) => rsx!( p { "{v:?}" } ), - // None => rsx!( p { "waiting.." } ), - // }) + match future.value().as_ref() { + Some(v) => rsx!( p { "{v}" } ), + _ => rsx!( p { "waiting.." } ), + } } diff --git a/examples/file_explorer.rs b/examples/file_explorer.rs index c5d5f31756..1ac571d2e8 100644 --- a/examples/file_explorer.rs +++ b/examples/file_explorer.rs @@ -9,7 +9,7 @@ use dioxus::desktop::{Config, WindowBuilder}; use dioxus::prelude::*; fn main() { - dioxus::launch::builder() + LaunchBuilder::desktop() .with_cfg(Config::new().with_window(WindowBuilder::new().with_resizable(true))) .launch(app) } @@ -18,12 +18,12 @@ fn app() -> Element { let mut files = use_signal(Files::new); rsx! { - document::Link { + head::Link { rel: "stylesheet", - href: asset!("/examples/assets/fileexplorer.css") + href: asset!("./examples/assets/fileexplorer.css") } div { - document::Link { href: "https://fonts.googleapis.com/icon?family=Material+Icons", rel: "stylesheet" } + head::Link { href: "https://fonts.googleapis.com/icon?family=Material+Icons", rel: "stylesheet" } header { i { class: "material-icons icon-menu", "menu" } h1 { "Files: " {files.read().current()} } diff --git a/examples/file_upload.rs b/examples/file_upload.rs index ffc1858238..9fb74072bb 100644 --- a/examples/file_upload.rs +++ b/examples/file_upload.rs @@ -8,10 +8,10 @@ use std::sync::Arc; use dioxus::prelude::*; use dioxus::{html::HasFileData, prelude::dioxus_elements::FileEngine}; -const STYLE: Asset = asset!("/examples/assets/file_upload.css"); +const STYLE: &str = asset!("./examples/assets/file_upload.css"); fn main() { - dioxus::launch(app); + launch(app); } struct UploadedFile { @@ -43,7 +43,7 @@ fn app() -> Element { }; rsx! { - document::Stylesheet { href: STYLE } + head::Link { rel: "stylesheet", href: STYLE } h1 { "File Upload Example" } p { "Drop a .txt, .rs, or .js file here to read it" } diff --git a/examples/flat_router.rs b/examples/flat_router.rs index d537ee4c1b..79a9d2a3f2 100644 --- a/examples/flat_router.rs +++ b/examples/flat_router.rs @@ -9,12 +9,12 @@ use dioxus::prelude::*; -const STYLE: Asset = asset!("/examples/assets/flat_router.css"); +const STYLE: &str = asset!("./examples/assets/flat_router.css"); fn main() { - dioxus::launch(|| { + launch(|| { rsx! { - document::Stylesheet { href: STYLE } + head::Link { rel: "stylesheet", href: STYLE } Router:: {} } }) diff --git a/examples/form.rs b/examples/form.rs index ff6154b9d4..91b09ecd34 100644 --- a/examples/form.rs +++ b/examples/form.rs @@ -7,7 +7,7 @@ use dioxus::prelude::*; use std::collections::HashMap; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/future.rs b/examples/future.rs index e32386fe83..f4ddc10468 100644 --- a/examples/future.rs +++ b/examples/future.rs @@ -7,7 +7,7 @@ use async_std::task::sleep; use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/generic_component.rs b/examples/generic_component.rs index 520f257787..6af34e542b 100644 --- a/examples/generic_component.rs +++ b/examples/generic_component.rs @@ -7,7 +7,7 @@ use dioxus::prelude::*; use std::fmt::Display; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/global.rs b/examples/global.rs index 693b912e93..0a08485b02 100644 --- a/examples/global.rs +++ b/examples/global.rs @@ -7,18 +7,18 @@ use dioxus::prelude::*; -const STYLE: Asset = asset!("/examples/assets/counter.css"); +const STYLE: &str = asset!("./examples/assets/counter.css"); static COUNT: GlobalSignal = Signal::global(|| 0); static DOUBLED_COUNT: GlobalMemo = Memo::global(|| COUNT() * 2); fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { rsx! { - document::Stylesheet { href: STYLE } + head::Link { rel: "stylesheet", href: STYLE } Increment {} Decrement {} Reset {} diff --git a/examples/hash_fragment_state.rs b/examples/hash_fragment_state.rs index bc0ec2cc88..ed77c8c46e 100644 --- a/examples/hash_fragment_state.rs +++ b/examples/hash_fragment_state.rs @@ -2,7 +2,7 @@ //! //! You can set up two way data binding between the url hash and signals. //! -//! Run this example on desktop with +//! Run this example on desktop with //! ```sh //! dx serve --example hash_fragment_state --features=ciborium,base64 //! ``` @@ -19,7 +19,7 @@ use dioxus::prelude::*; use serde::{Deserialize, Serialize}; fn main() { - dioxus::launch(|| { + launch(|| { rsx! { Router:: {} } diff --git a/examples/hello_world.rs b/examples/hello_world.rs index 20c9af934a..ee33c22309 100644 --- a/examples/hello_world.rs +++ b/examples/hello_world.rs @@ -12,7 +12,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/hydration.rs b/examples/hydration.rs index cffacc86aa..1e7de2706f 100644 --- a/examples/hydration.rs +++ b/examples/hydration.rs @@ -13,7 +13,7 @@ use dioxus::desktop::Config; use dioxus::prelude::*; fn main() { - dioxus::launch::builder() + LaunchBuilder::desktop() .with_cfg(Config::new().with_prerendered({ // We build the dom a first time, then pre-render it to HTML let pre_rendered_dom = VirtualDom::prebuilt(app); diff --git a/examples/image_generator_openai.rs b/examples/image_generator_openai.rs index 67c743c427..474c46de90 100644 --- a/examples/image_generator_openai.rs +++ b/examples/image_generator_openai.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; use serde_json::{json, Error}; fn main() { - dioxus::launch(app) + launch(app) } fn app() -> Element { @@ -36,7 +36,7 @@ fn app() -> Element { }); rsx! { - document::Stylesheet { href: "https://unpkg.com/bulma@0.9.0/css/bulma.min.css" } + head::Link { rel: "stylesheet", href: "https://unpkg.com/bulma@0.9.0/css/bulma.min.css" } div { class: "container", div { class: "columns", div { class: "column", diff --git a/examples/link.rs b/examples/link.rs index 7d1f4be609..ff29b65626 100644 --- a/examples/link.rs +++ b/examples/link.rs @@ -8,15 +8,15 @@ use dioxus::prelude::*; -const STYLE: Asset = asset!("/examples/assets/links.css"); +const STYLE: &str = asset!("./examples/assets/links.css"); fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { rsx! ( - document::Stylesheet { href: STYLE } + head::Link { rel: "stylesheet", href: STYLE } Router:: {} ) } diff --git a/examples/login_form.rs b/examples/login_form.rs index 617723466c..39e874cc99 100644 --- a/examples/login_form.rs +++ b/examples/login_form.rs @@ -9,7 +9,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/memo_chain.rs b/examples/memo_chain.rs index ab57089397..205fe87693 100644 --- a/examples/memo_chain.rs +++ b/examples/memo_chain.rs @@ -6,7 +6,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/meta.rs b/examples/meta.rs index 9d8e1a4ae0..fae916258f 100644 --- a/examples/meta.rs +++ b/examples/meta.rs @@ -3,7 +3,8 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + tracing_subscriber::fmt::init(); + launch(app); } fn app() -> Element { @@ -11,23 +12,23 @@ fn app() -> Element { // You can use the Meta component to render a meta tag into the head of the page // Meta tags are useful to provide information about the page to search engines and social media sites // This example sets up meta tags for the open graph protocol for social media previews - document::Meta { + Meta { property: "og:title", content: "My Site", } - document::Meta { + Meta { property: "og:type", content: "website", } - document::Meta { + Meta { property: "og:url", content: "https://www.example.com", } - document::Meta { + Meta { property: "og:image", content: "https://example.com/image.jpg", } - document::Meta { + Meta { name: "description", content: "My Site is a site", } diff --git a/examples/mobile_demo/.gitignore b/examples/mobile_demo/.gitignore new file mode 100644 index 0000000000..e1e084c4bb --- /dev/null +++ b/examples/mobile_demo/.gitignore @@ -0,0 +1,10 @@ +# Rust +target/ +**/*.rs.bk + +# cargo-mobile2 +.cargo/ +/gen + +# macOS +.DS_Store diff --git a/examples/mobile_demo/Cargo.lock b/examples/mobile_demo/Cargo.lock new file mode 100644 index 0000000000..7f50292263 --- /dev/null +++ b/examples/mobile_demo/Cargo.lock @@ -0,0 +1,4007 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + +[[package]] +name = "android_log-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85965b6739a430150bdd138e2374a98af0c3ee0d030b3bb7fc3bddff58d0102e" + +[[package]] +name = "android_logger" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ec2333c185d826313162cee39d3fcc6a84ba08114a839bebf53b961e7e75773" +dependencies = [ + "android_log-sys", + "env_logger 0.7.1", + "lazy_static", + "log", +] + +[[package]] +name = "anyhow" +version = "1.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" + +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + +[[package]] +name = "async-lock" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa24f727524730b077666307f2734b4a1a1c57acb79193127dcc8914d5242dd7" +dependencies = [ + "event-listener", +] + +[[package]] +name = "async-task" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae" + +[[package]] +name = "async-trait" +version = "0.1.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "atk" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4af014b17dd80e8af9fa689b2d4a211ddba6eb583c1622f35d0cb543f6b17e4" +dependencies = [ + "atk-sys", + "glib", + "libc", +] + +[[package]] +name = "atk-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "251e0b7d90e33e0ba930891a505a9a35ece37b2dd37a14f3ffc306c13b980009" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "atomic-waker" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +dependencies = [ + "serde", +] + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "blocking" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77231a1c8f801696fc0123ec6150ce92cffb8e164a02afb9c8ddee0e9b65ad65" +dependencies = [ + "async-channel", + "async-lock", + "async-task", + "atomic-waker", + "fastrand", + "futures-lite", + "log", +] + +[[package]] +name = "bumpalo" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" + +[[package]] +name = "bytemuck" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cairo-rs" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" +dependencies = [ + "bitflags 2.4.2", + "cairo-sys-rs", + "glib", + "libc", + "once_cell", + "thiserror", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfb" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" +dependencies = [ + "byteorder", + "fnv", + "uuid", +] + +[[package]] +name = "cfg-expr" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b40ccee03b5175c18cde8f37e7d2a33bcef6f8ec8f7cc0d81090d1bb380949c9" +dependencies = [ + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "cocoa" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" +dependencies = [ + "bitflags 1.3.2", + "block", + "cocoa-foundation", + "core-foundation", + "core-graphics", + "foreign-types 0.5.0", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "931d3837c286f56e3c58423ce4eba12d08db2374461a785c86f672b08b5650d6" +dependencies = [ + "bitflags 1.3.2", + "block", + "core-foundation", + "core-graphics-types", + "foreign-types 0.3.2", + "libc", + "objc", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "combine" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "concurrent-queue" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "const_format" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a214c7af3d04997541b18d432afaff4c455e79e2029079647e72fc2bd27673" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "constcat" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd7e35aee659887cbfb97aaf227ac12cad1a9d7c71e55ff3376839ed4e282d08" + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + +[[package]] +name = "core-graphics" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "970a29baf4110c26fedbc7f82107d42c23f7e88e404c4577ed73fe99ff85a212" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types 0.5.0", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bb142d41022986c1d8ff29103a1411c8a3dfad3552f87a4f8dc50d61d4f4e33" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[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 = "cssparser" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa 0.4.8", + "matches", + "phf 0.8.0", + "proc-macro2", + "quote", + "smallvec", + "syn 1.0.109", +] + +[[package]] +name = "cssparser-macros" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" +dependencies = [ + "quote", + "syn 2.0.52", +] + +[[package]] +name = "darling" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "darling_macro" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown 0.14.0", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case 0.4.0", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dioxus" +version = "0.5.0-alpha.0" +dependencies = [ + "dioxus-config-macro", + "dioxus-core", + "dioxus-core-macro", + "dioxus-desktop", + "dioxus-fullstack", + "dioxus-hooks", + "dioxus-hot-reload", + "dioxus-html", + "dioxus-mobile", + "dioxus-signals", +] + +[[package]] +name = "dioxus-cli-config" +version = "0.5.0-alpha.0" +dependencies = [ + "once_cell", + "serde", + "serde_json", + "tracing", +] + +[[package]] +name = "dioxus-config-macro" +version = "0.5.0-alpha.0" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "dioxus-core" +version = "0.5.0-alpha.0" +dependencies = [ + "futures-channel", + "futures-util", + "longest-increasing-subsequence", + "rustc-hash", + "serde", + "slab", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "dioxus-core-macro" +version = "0.5.0-alpha.0" +dependencies = [ + "constcat", + "convert_case 0.6.0", + "dioxus-rsx", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "dioxus-debug-cell" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ea539174bb236e0e7dc9c12b19b88eae3cb574dedbd0252a2d43ea7e6de13e2" + +[[package]] +name = "dioxus-desktop" +version = "0.5.0-alpha.0" +dependencies = [ + "async-trait", + "core-foundation", + "dioxus-cli-config", + "dioxus-core", + "dioxus-hooks", + "dioxus-hot-reload", + "dioxus-html", + "dioxus-interpreter-js", + "dunce", + "futures-channel", + "futures-util", + "generational-box", + "global-hotkey", + "infer", + "muda", + "objc", + "objc_id", + "rfd", + "rustc-hash", + "serde", + "serde_json", + "slab", + "tao", + "thiserror", + "tokio", + "tracing", + "urlencoding", + "webbrowser", + "wry 0.37.0", +] + +[[package]] +name = "dioxus-fullstack" +version = "0.5.0-alpha.0" +dependencies = [ + "async-trait", + "base64", + "bytes", + "ciborium", + "dioxus-hot-reload", + "dioxus-lib", + "dioxus-mobile", + "dioxus_server_macro", + "futures-util", + "once_cell", + "serde", + "serde_json", + "server_fn", + "tracing", +] + +[[package]] +name = "dioxus-hooks" +version = "0.5.0-alpha.0" +dependencies = [ + "dioxus-core", + "dioxus-debug-cell", + "dioxus-signals", + "futures-channel", + "futures-util", + "generational-box", + "slab", + "thiserror", + "tracing", +] + +[[package]] +name = "dioxus-hot-reload" +version = "0.5.0-alpha.0" +dependencies = [ + "dioxus-core", + "dioxus-html", + "dioxus-rsx", + "interprocess", + "serde", + "serde_json", +] + +[[package]] +name = "dioxus-html" +version = "0.5.0-alpha.0" +dependencies = [ + "async-trait", + "dioxus-core", + "dioxus-html-internal-macro", + "enumset", + "euclid", + "futures-channel", + "generational-box", + "keyboard-types", + "serde", + "serde-value", + "serde_json", + "serde_repr", + "tokio", + "web-sys", +] + +[[package]] +name = "dioxus-html-internal-macro" +version = "0.5.0-alpha.0" +dependencies = [ + "convert_case 0.6.0", + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "dioxus-interpreter-js" +version = "0.5.0-alpha.0" +dependencies = [ + "dioxus-core", + "dioxus-html", + "md5", + "sledgehammer_bindgen", + "sledgehammer_utils", +] + +[[package]] +name = "dioxus-lib" +version = "0.5.0-alpha.0" +dependencies = [ + "dioxus-core", + "dioxus-core-macro", + "dioxus-hooks", + "dioxus-html", + "dioxus-rsx", + "dioxus-signals", +] + +[[package]] +name = "dioxus-mobile" +version = "0.5.0-alpha.0" +dependencies = [ + "dioxus-desktop", +] + +[[package]] +name = "dioxus-rsx" +version = "0.5.0-alpha.0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", + "tracing", +] + +[[package]] +name = "dioxus-signals" +version = "0.5.0-alpha.0" +dependencies = [ + "dioxus-core", + "futures-channel", + "futures-util", + "generational-box", + "once_cell", + "parking_lot", + "rustc-hash", + "tracing", +] + +[[package]] +name = "dioxus_server_macro" +version = "0.5.0-alpha.0" +dependencies = [ + "convert_case 0.6.0", + "proc-macro2", + "quote", + "server_fn_macro", + "syn 2.0.52", +] + +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + +[[package]] +name = "dlopen2" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1297103d2bbaea85724fcee6294c2d50b1081f9ad47d0f6f6f61eda65315a6" +dependencies = [ + "dlopen2_derive", + "libc", + "once_cell", + "winapi", +] + +[[package]] +name = "dlopen2_derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "dtoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" + +[[package]] +name = "dtoa-short" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbaceec3c6e4211c79e7b1800fb9680527106beb2f9c51904a3210c03a448c74" +dependencies = [ + "dtoa", +] + +[[package]] +name = "dunce" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" + +[[package]] +name = "enumset" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e875f1719c16de097dee81ed675e2d9bb63096823ed3f0ca827b7dea3028bbbb" +dependencies = [ + "enumset_derive", +] + +[[package]] +name = "enumset_derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08b6c6ab82d70f08844964ba10c7babb716de2ecaeab9be5717918a5177d3af" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "env_logger" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "euclid" +version = "0.22.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f253bc5c813ca05792837a0ff4b3a580336b224512d48f7eda1d7dd9210787" +dependencies = [ + "num-traits", + "serde", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "fdeflate" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "field-offset" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" +dependencies = [ + "memoffset", + "rustc_version", +] + +[[package]] +name = "flate2" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "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.52", +] + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" +dependencies = [ + "mac", + "new_debug_unreachable", +] + +[[package]] +name = "futures" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-executor" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" + +[[package]] +name = "futures-lite" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + +[[package]] +name = "futures-macro" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" + +[[package]] +name = "futures-util" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "gdk" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5ba081bdef3b75ebcdbfc953699ed2d7417d6bd853347a42a37d76406a33646" +dependencies = [ + "cairo-rs", + "gdk-pixbuf", + "gdk-sys", + "gio", + "glib", + "libc", + "pango", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" +dependencies = [ + "gdk-pixbuf-sys", + "gio", + "glib", + "libc", + "once_cell", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gdk-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31ff856cb3386dae1703a920f803abafcc580e9b5f711ca62ed1620c25b51ff2" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkwayland-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a90fbf5c033c65d93792192a49a8efb5bb1e640c419682a58bb96f5ae77f3d4a" +dependencies = [ + "gdk-sys", + "glib-sys", + "gobject-sys", + "libc", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkx11" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2ea8a4909d530f79921290389cbd7c34cb9d623bfe970eaae65ca5f9cd9cce" +dependencies = [ + "gdk", + "gdkx11-sys", + "gio", + "glib", + "libc", + "x11", +] + +[[package]] +name = "gdkx11-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fee8f00f4ee46cad2939b8990f5c70c94ff882c3028f3cc5abf950fa4ab53043" +dependencies = [ + "gdk-sys", + "glib-sys", + "libc", + "system-deps", + "x11", +] + +[[package]] +name = "generational-box" +version = "0.5.0-alpha.0" +dependencies = [ + "parking_lot", +] + +[[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 = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "gimli" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" + +[[package]] +name = "gio" +version = "0.18.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys", + "glib", + "libc", + "once_cell", + "pin-project-lite", + "smallvec", + "thiserror", +] + +[[package]] +name = "gio-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", + "winapi", +] + +[[package]] +name = "glib" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" +dependencies = [ + "bitflags 2.4.2", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "memchr", + "once_cell", + "smallvec", + "thiserror", +] + +[[package]] +name = "glib-macros" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" +dependencies = [ + "heck", + "proc-macro-crate 2.0.2", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "glib-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" +dependencies = [ + "libc", + "system-deps", +] + +[[package]] +name = "global-hotkey" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0d37e95d3937251ee2019709389bb793c1237f16d45fc0fe7b2464b5f97c68" +dependencies = [ + "crossbeam-channel", + "keyboard-types", + "once_cell", + "thiserror", + "windows-sys 0.52.0", + "x11-dl", +] + +[[package]] +name = "gloo-net" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43aaa242d1239a8822c15c645f02166398da4f8b5c4bae795c1f5b44e9eee173" +dependencies = [ + "futures-channel", + "futures-core", + "futures-sink", + "gloo-utils", + "http 0.2.9", + "js-sys", + "pin-project", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-utils" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gobject-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gtk" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93c4f5e0e20b60e10631a5f06da7fe3dda744b05ad0ea71fee2f47adf865890c" +dependencies = [ + "atk", + "cairo-rs", + "field-offset", + "futures-channel", + "gdk", + "gdk-pixbuf", + "gio", + "glib", + "gtk-sys", + "gtk3-macros", + "libc", + "pango", + "pkg-config", +] + +[[package]] +name = "gtk-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771437bf1de2c1c0b496c11505bdf748e26066bbe942dfc8f614c9460f6d7722" +dependencies = [ + "atk-sys", + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "system-deps", +] + +[[package]] +name = "gtk3-macros" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6063efb63db582968fb7df72e1ae68aa6360dcfb0a75143f34fc7d616bad75e" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "half" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5eceaaeec696539ddaf7b333340f1af35a5aa87ae3e4f3ead0532f72affab2e" +dependencies = [ + "cfg-if", + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" + +[[package]] +name = "home" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "html5ever" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" +dependencies = [ + "log", + "mac", + "markup5ever", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "http" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes", + "fnv", + "itoa 1.0.9", +] + +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa 1.0.9", +] + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "image" +version = "0.24.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527909aa81e20ac3a44803521443a765550f09b5130c2c2fa1ea59c2f8f50a3a" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "num-rational", + "num-traits", +] + +[[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", +] + +[[package]] +name = "indexmap" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +dependencies = [ + "equivalent", + "hashbrown 0.14.0", +] + +[[package]] +name = "infer" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6c16b11a665b26aeeb9b1d7f954cdeb034be38dd00adab4f2ae921a8fee804" +dependencies = [ + "cfb", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "interprocess" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81f2533f3be42fffe3b5e63b71aeca416c1c3bc33e4e27be018521e76b1f38fb" +dependencies = [ + "blocking", + "cfg-if", + "futures-core", + "futures-io", + "intmap", + "libc", + "once_cell", + "rustc_version", + "spinning", + "thiserror", + "to_method", + "winapi", +] + +[[package]] +name = "intmap" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae52f28f45ac2bc96edb7714de995cffc174a395fb0abf5bff453587c980d7b9" + +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "javascriptcore-rs" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca5671e9ffce8ffba57afc24070e906da7fc4b1ba66f2cabebf61bf2ea257fcc" +dependencies = [ + "bitflags 1.3.2", + "glib", + "javascriptcore-rs-sys", +] + +[[package]] +name = "javascriptcore-rs-sys" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1be78d14ffa4b75b66df31840478fef72b51f8c2465d4ca7c194da9f7a5124" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "jni" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec" +dependencies = [ + "cesu8", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", +] + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror", + "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 = "js-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "keyboard-types" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" +dependencies = [ + "bitflags 2.4.2", + "serde", + "unicode-segmentation", +] + +[[package]] +name = "kuchikiki" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e4755b7b995046f510a7520c42b2fed58b77bd94d5a87a8eb43d2fd126da8" +dependencies = [ + "cssparser", + "html5ever", + "indexmap 1.9.3", + "matches", + "selectors", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "libxdo" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00333b8756a3d28e78def82067a377de7fa61b24909000aeaa2b446a948d14db" +dependencies = [ + "libxdo-sys", +] + +[[package]] +name = "libxdo-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db23b9e7e2b7831bbd8aac0bbeeeb7b68cbebc162b227e7052e8e55829a09212" +dependencies = [ + "libc", + "x11", +] + +[[package]] +name = "lock_api" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" + +[[package]] +name = "longest-increasing-subsequence" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3bd0dd2cd90571056fdb71f6275fada10131182f84899f4b2a916e565d81d86" + +[[package]] +name = "lru" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" +dependencies = [ + "hashbrown 0.14.0", +] + +[[package]] +name = "mac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "markup5ever" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" +dependencies = [ + "log", + "phf 0.10.1", + "phf_codegen 0.10.0", + "string_cache", + "string_cache_codegen", + "tendril", +] + +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + +[[package]] +name = "md5" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", + "simd-adler32", +] + +[[package]] +name = "mobile-demo" +version = "0.1.0" +dependencies = [ + "android_logger", + "anyhow", + "core-foundation", + "dioxus", + "env_logger 0.9.3", + "jni 0.19.0", + "log", + "paste", + "wry 0.35.2", +] + +[[package]] +name = "muda" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c47e7625990fc1af2226ea4f34fb2412b03c12639fcb91868581eb3a6893453" +dependencies = [ + "cocoa", + "crossbeam-channel", + "gtk", + "keyboard-types", + "libxdo", + "objc", + "once_cell", + "png", + "thiserror", + "windows-sys 0.52.0", +] + +[[package]] +name = "ndk" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" +dependencies = [ + "bitflags 1.3.2", + "jni-sys", + "ndk-sys", + "num_enum", + "raw-window-handle 0.5.2", + "thiserror", +] + +[[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.4.1+23.1.7779620" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cf2aae958bd232cac5069850591667ad422d263686d75b52a065f9badeee5a3" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi 0.3.2", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", + "objc_exception", +] + +[[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_exception" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" +dependencies = [ + "cc", +] + +[[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.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "ordered-float" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7940cf2ca942593318d07fcf2596cdca60a85c9e7fab408a5e21a4f9dcd40d87" +dependencies = [ + "num-traits", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "pango" +version = "0.18.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" +dependencies = [ + "gio", + "glib", + "libc", + "once_cell", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "parking" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.48.1", +] + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "percent-encoding" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" + +[[package]] +name = "phf" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" +dependencies = [ + "phf_macros", + "phf_shared 0.8.0", + "proc-macro-hack", +] + +[[package]] +name = "phf" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" +dependencies = [ + "phf_shared 0.10.0", +] + +[[package]] +name = "phf_codegen" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" +dependencies = [ + "phf_generator 0.8.0", + "phf_shared 0.8.0", +] + +[[package]] +name = "phf_codegen" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", +] + +[[package]] +name = "phf_generator" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" +dependencies = [ + "phf_shared 0.8.0", + "rand 0.7.3", +] + +[[package]] +name = "phf_generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +dependencies = [ + "phf_shared 0.10.0", + "rand 0.8.5", +] + +[[package]] +name = "phf_macros" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" +dependencies = [ + "phf_generator 0.8.0", + "phf_shared 0.8.0", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "phf_shared" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" +dependencies = [ + "siphasher", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + +[[package]] +name = "png" +version = "0.17.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59871cc5b6cce7eaccca5a802b4173377a1c2ba90654246789a8fa2334426d11" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "prettyplease" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" +dependencies = [ + "proc-macro2", + "syn 2.0.52", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.14", +] + +[[package]] +name = "proc-macro-crate" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" +dependencies = [ + "toml_datetime", + "toml_edit 0.20.2", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + +[[package]] +name = "proc-macro2" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", + "rand_pcg", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[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 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.10", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "raw-window-handle" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" + +[[package]] +name = "raw-window-handle" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42a9830a0e1b9fb145ebb365b8bc4ccd75f290f98c0247deafbbe2c75cefb544" + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "regex" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7b6d6190b7594385f61bd3911cd1be99dfddcfc365a4160cc2ab5bff4aed294" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" + +[[package]] +name = "rfd" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c9e7b57df6e8472152674607f6cc68aa14a748a3157a857a94f516e11aeacc2" +dependencies = [ + "block", + "dispatch", + "glib-sys", + "gobject-sys", + "gtk-sys", + "js-sys", + "log", + "objc", + "objc-foundation", + "objc_id", + "raw-window-handle 0.5.2", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[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 = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "selectors" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe" +dependencies = [ + "bitflags 1.3.2", + "cssparser", + "derive_more", + "fxhash", + "log", + "matches", + "phf 0.8.0", + "phf_codegen 0.8.0", + "precomputed-hash", + "servo_arc", + "smallvec", + "thin-slice", +] + +[[package]] +name = "semver" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" + +[[package]] +name = "send_wrapper" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" +dependencies = [ + "futures-core", +] + +[[package]] +name = "serde" +version = "1.0.180" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea67f183f058fe88a4e3ec6e2788e003840893b91bac4559cabedd00863b3ed" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float", + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.180" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24e744d7782b686ab3b73267ef05697159cc0e5abbed3f47f9933165e5219036" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "serde_json" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" +dependencies = [ + "itoa 1.0.9", + "ryu", + "serde", +] + +[[package]] +name = "serde_qs" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0431a35568651e363364210c91983c1da5eb29404d9f0928b67d4ebcfa7d330c" +dependencies = [ + "percent-encoding", + "serde", + "thiserror", +] + +[[package]] +name = "serde_repr" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "serde_spanned" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" +dependencies = [ + "serde", +] + +[[package]] +name = "server_fn" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2955da1dc5fcd970c182ebf1089af6c5f19051e1f286a21f7b96490a49b7a531" +dependencies = [ + "bytes", + "const_format", + "dashmap", + "futures", + "gloo-net", + "http 1.1.0", + "js-sys", + "once_cell", + "send_wrapper", + "serde", + "serde_json", + "serde_qs", + "server_fn_macro_default", + "thiserror", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "xxhash-rust", +] + +[[package]] +name = "server_fn_macro" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adfdd051ef905fdb3da20942b0c52d536158d7489a724e14cc2fd47323e7ca91" +dependencies = [ + "const_format", + "convert_case 0.6.0", + "proc-macro2", + "quote", + "syn 2.0.52", + "xxhash-rust", +] + +[[package]] +name = "server_fn_macro_default" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "060af1def72353a779fcc184c53e1965d3055a38b9e827f2259b2bff2d9c371e" +dependencies = [ + "server_fn_macro", + "syn 2.0.52", +] + +[[package]] +name = "servo_arc" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432" +dependencies = [ + "nodrop", + "stable_deref_trait", +] + +[[package]] +name = "sha2" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "siphasher" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" + +[[package]] +name = "slab" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +dependencies = [ + "autocfg", +] + +[[package]] +name = "sledgehammer_bindgen" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcfaf791ff02f48f3518ce825d32cf419c13a43c1d8b1232f74ac89f339c46d2" +dependencies = [ + "sledgehammer_bindgen_macro", +] + +[[package]] +name = "sledgehammer_bindgen_macro" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdd941cc539bd3dc694edaf9d0c4e1221d02baa67c6b45ec04fad1024d9e8139" +dependencies = [ + "quote", + "syn 2.0.52", +] + +[[package]] +name = "sledgehammer_utils" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f20798defa0e9d4eff9ca451c7f84774c7378a9c3b5a40112cfa2b3eadb97ae2" +dependencies = [ + "lru", + "once_cell", + "rustc-hash", +] + +[[package]] +name = "smallvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" + +[[package]] +name = "soup3" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "471f924a40f31251afc77450e781cb26d55c0b650842efafc9c6cbd2f7cc4f9f" +dependencies = [ + "futures-channel", + "gio", + "glib", + "libc", + "soup3-sys", +] + +[[package]] +name = "soup3-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebe8950a680a12f24f15ebe1bf70db7af98ad242d9db43596ad3108aab86c27" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "spinning" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d4f0e86297cad2658d92a707320d87bf4e6ae1050287f51d19b67ef3f153a7b" +dependencies = [ + "lock_api", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "string_cache" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" +dependencies = [ + "new_debug_unreachable", + "once_cell", + "parking_lot", + "phf_shared 0.10.0", + "precomputed-hash", + "serde", +] + +[[package]] +name = "string_cache_codegen" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", + "proc-macro2", + "quote", +] + +[[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.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "system-deps" +version = "6.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30c2de8a4d8f4b823d634affc9cd2a74ec98c53a756f317e529a48046cbf71f3" +dependencies = [ + "cfg-expr", + "heck", + "pkg-config", + "toml", + "version-compare", +] + +[[package]] +name = "tao" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccba570365293ca309d60f30fdac2c5271b732dc762e6154e59c85d2c762a0a1" +dependencies = [ + "bitflags 1.3.2", + "cocoa", + "core-foundation", + "core-graphics", + "crossbeam-channel", + "dispatch", + "dlopen2", + "gdkwayland-sys", + "gdkx11-sys", + "gtk", + "image", + "instant", + "jni 0.21.1", + "lazy_static", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "objc", + "once_cell", + "parking_lot", + "png", + "raw-window-handle 0.5.2", + "raw-window-handle 0.6.0", + "scopeguard", + "tao-macros", + "unicode-segmentation", + "url", + "windows", + "windows-implement", + "windows-version", + "x11-dl", +] + +[[package]] +name = "tao-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b27a4bcc5eb524658234589bdffc7e7bfb996dbae6ce9393bfd39cb4159b445" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "target-lexicon" +version = "0.12.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a" + +[[package]] +name = "tendril" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" +dependencies = [ + "futf", + "mac", + "utf-8", +] + +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thin-slice" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" + +[[package]] +name = "thiserror" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "to_method" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c4ceeeca15c8384bbc3e011dbd8fccb7f068a440b752b7d9b32ceb0ca0e2e8" + +[[package]] +name = "tokio" +version = "1.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" +dependencies = [ + "autocfg", + "backtrace", + "bytes", + "num_cpus", + "pin-project-lite", + "tokio-macros", +] + +[[package]] +name = "tokio-macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "toml" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.19.14", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" +dependencies = [ + "indexmap 2.0.0", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "toml_edit" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +dependencies = [ + "indexmap 2.0.0", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "nu-ansi-term", + "sharded-slab", + "smallvec", + "thread_local", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unicode-bidi" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + +[[package]] +name = "unicode-ident" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "url" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[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 = "uuid" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version-compare" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "waker-fn" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" + +[[package]] +name = "walkdir" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[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.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.52", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" + +[[package]] +name = "wasm-streams" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webbrowser" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd222aa310eb7532e3fd427a5d7db7e44bc0b0cf1c1e21139c345325511a85b6" +dependencies = [ + "core-foundation", + "home", + "jni 0.21.1", + "log", + "ndk-context", + "objc", + "raw-window-handle 0.5.2", + "url", + "web-sys", +] + +[[package]] +name = "webkit2gtk" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76b1bc1e54c581da1e9f179d0b38512ba358fb1af2d634a1affe42e37172361a" +dependencies = [ + "bitflags 1.3.2", + "cairo-rs", + "gdk", + "gdk-sys", + "gio", + "gio-sys", + "glib", + "glib-sys", + "gobject-sys", + "gtk", + "gtk-sys", + "javascriptcore-rs", + "libc", + "once_cell", + "soup3", + "webkit2gtk-sys", +] + +[[package]] +name = "webkit2gtk-sys" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62daa38afc514d1f8f12b8693d30d5993ff77ced33ce30cd04deebc267a6d57c" +dependencies = [ + "bitflags 1.3.2", + "cairo-sys-rs", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "gtk-sys", + "javascriptcore-rs-sys", + "libc", + "pkg-config", + "soup3-sys", + "system-deps", +] + +[[package]] +name = "webview2-com" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0ae9c7e420783826cf769d2c06ac9ba462f450eca5893bb8c6c6529a4e5dd33" +dependencies = [ + "webview2-com-macros", + "webview2-com-sys", + "windows", + "windows-core", + "windows-implement", + "windows-interface", +] + +[[package]] +name = "webview2-com-macros" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac1345798ecd8122468840bcdf1b95e5dc6d2206c5e4b0eafa078d061f59c9bc" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "webview2-com-sys" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6ad85fceee6c42fa3d61239eba5a11401bf38407a849ed5ea1b407df08cca72" +dependencies = [ + "thiserror", + "windows", + "windows-core", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[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.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core", + "windows-implement", + "windows-interface", + "windows-targets 0.52.4", +] + +[[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.4", +] + +[[package]] +name = "windows-implement" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12168c33176773b86799be25e2a2ba07c7aab9968b37541f1094dbd7a60c8946" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "windows-interface" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d8dc32e0095a7eeccebd0e3f09e9509365ecb3fc6ac4d6f5f14a3f6392942d1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[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.1", +] + +[[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.4", +] + +[[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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + +[[package]] +name = "windows-targets" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +dependencies = [ + "windows_aarch64_gnullvm 0.52.4", + "windows_aarch64_msvc 0.52.4", + "windows_i686_gnu 0.52.4", + "windows_i686_msvc 0.52.4", + "windows_x86_64_gnu 0.52.4", + "windows_x86_64_gnullvm 0.52.4", + "windows_x86_64_msvc 0.52.4", +] + +[[package]] +name = "windows-version" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75aa004c988e080ad34aff5739c39d0312f4684699d6d71fc8a198d057b8b9b4" +dependencies = [ + "windows-targets 0.52.4", +] + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" + +[[package]] +name = "winnow" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bd122eb777186e60c3fdf765a58ac76e41c582f1f535fbf3314434c6b58f3f7" +dependencies = [ + "memchr", +] + +[[package]] +name = "wry" +version = "0.35.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3016c47c9b6f7029a9da7cd48af8352327226bba0e955f3c92e2966651365a9" +dependencies = [ + "base64", + "block", + "cfg_aliases", + "cocoa", + "core-graphics", + "crossbeam-channel", + "dunce", + "gdkx11", + "gtk", + "html5ever", + "http 0.2.9", + "javascriptcore-rs", + "jni 0.21.1", + "kuchikiki", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "objc", + "objc_id", + "once_cell", + "raw-window-handle 0.5.2", + "serde", + "serde_json", + "sha2", + "soup3", + "tao-macros", + "thiserror", + "url", + "webkit2gtk", + "webkit2gtk-sys", + "webview2-com", + "windows", + "windows-implement", + "windows-version", + "x11-dl", +] + +[[package]] +name = "wry" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b717040ba9771fd88eb428c6ea6b555f8e734ff8534f02c13e8f10d97f5935e" +dependencies = [ + "base64", + "block", + "cfg_aliases", + "cocoa", + "core-graphics", + "crossbeam-channel", + "dunce", + "gdkx11", + "gtk", + "html5ever", + "http 0.2.9", + "javascriptcore-rs", + "jni 0.21.1", + "kuchikiki", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "objc", + "objc_id", + "once_cell", + "percent-encoding", + "raw-window-handle 0.6.0", + "serde", + "serde_json", + "sha2", + "soup3", + "tao-macros", + "thiserror", + "webkit2gtk", + "webkit2gtk-sys", + "webview2-com", + "windows", + "windows-implement", + "windows-version", + "x11-dl", +] + +[[package]] +name = "x11" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "xxhash-rust" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927da81e25be1e1a2901d59b81b37dd2efd1fc9c9345a55007f09bf5a2d3ee03" + +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] diff --git a/examples/mobile_demo/Cargo.toml b/examples/mobile_demo/Cargo.toml new file mode 100644 index 0000000000..4d4155750b --- /dev/null +++ b/examples/mobile_demo/Cargo.toml @@ -0,0 +1,51 @@ +[package] +name = "mobile-demo" +version = "0.1.0" +authors = ["Jonathan Kelley "] +edition = "2021" + +[lib] +crate-type = ["staticlib", "cdylib", "rlib"] + +[[bin]] +name = "mobile-demo-desktop" +path = "gen/bin/desktop.rs" + +[package.metadata.cargo-android] +app-activity-name = "com.example.mobile_demo.MainActivity" +app-dependencies = [ + "androidx.webkit:webkit:1.6.1", + "androidx.appcompat:appcompat:1.6.1", + "com.google.android.material:material:1.8.0", +] +project-dependencies = ["org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21"] +app-plugins = ["org.jetbrains.kotlin.android"] +app-permissions = ["android.permission.INTERNET"] +app-theme-parent = "Theme.MaterialComponents.DayNight.DarkActionBar" +vulkan-validation = false + +[package.metadata.cargo-android.env-vars] +WRY_ANDROID_PACKAGE = "com.example.mobile_demo" +WRY_ANDROID_LIBRARY = "mobile_demo" +WRY_ANDROID_KOTLIN_FILES_OUT_DIR = "/app/src/main/kotlin/com/example/mobile_demo" + +[package.metadata.cargo-apple.ios] +frameworks = ["WebKit"] + +[dependencies] +anyhow = "1.0.56" +log = "0.4.11" +wry = "0.35.0" +dioxus = { path = "../../packages/dioxus", features = ["mobile"]} + + +[target.'cfg(target_os = "android")'.dependencies] +android_logger = "0.9.0" +jni = "0.19.0" +paste = "1.0" + +[target.'cfg(not(target_os = "android"))'.dependencies] +env_logger = "0.9.0" + +[target.'cfg(target_os = "ios")'.dependencies] +core-foundation = "0.9.3" diff --git a/examples/mobile_demo/README.md b/examples/mobile_demo/README.md new file mode 100644 index 0000000000..4d5ea46ed0 --- /dev/null +++ b/examples/mobile_demo/README.md @@ -0,0 +1,11 @@ +# Dioxus Mobile demo + +## How this project was generated + +Right now, Dioxus supports mobile targets including iOS and Android. However, our tooling is not mature enough to include the build commands directly. + +This project was generated using [cargo-mobile2](https://github.com/tauri-apps/cargo-mobile2). We have yet to integrate this generation into the Dioxus-CLI. The open issue for this is [#1157](https://github.com/DioxusLabs/dioxus/issues/1157). + +## Running this project + +Because the tooling and ecosystem is still young, Dioxus mobile can be difficult to setup and run. We have [detailed guides](https://dioxuslabs.com/learn/0.5/getting_started) for creating, building, and running on both iOS and Android. diff --git a/examples/mobile_demo/mobile.toml b/examples/mobile_demo/mobile.toml new file mode 100644 index 0000000000..3b87772508 --- /dev/null +++ b/examples/mobile_demo/mobile.toml @@ -0,0 +1,8 @@ +[app] +name = "mobile-demo" +stylized-name = "Mobile Demo" +domain = "example.com" +template-pack = "wry" + +[apple] +development-team = "34U4FG9TJ8" diff --git a/examples/mobile_demo/src/index.html b/examples/mobile_demo/src/index.html new file mode 100644 index 0000000000..b0e6290e12 --- /dev/null +++ b/examples/mobile_demo/src/index.html @@ -0,0 +1,12 @@ + + + + Dioxus app + + + + +
      + + + diff --git a/examples/mobile_demo/src/lib.rs b/examples/mobile_demo/src/lib.rs new file mode 100644 index 0000000000..6fc142d9a3 --- /dev/null +++ b/examples/mobile_demo/src/lib.rs @@ -0,0 +1,90 @@ +use anyhow::Result; +use dioxus::mobile::Config; +use dioxus::prelude::*; + +#[cfg(target_os = "android")] +use dioxus::mobile::wry::android_binding; + +#[cfg(target_os = "android")] +fn init_logging() { + android_logger::init_once( + android_logger::Config::default() + .with_min_level(log::Level::Trace) + .with_tag("mobile-demo"), + ); +} + +#[cfg(not(target_os = "android"))] +fn init_logging() { + env_logger::init(); +} + +#[cfg(any(target_os = "android", target_os = "ios"))] +fn stop_unwind T, T>(f: F) -> T { + match std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)) { + Ok(t) => t, + Err(err) => { + eprintln!("attempt to unwind out of `rust` with err: {:?}", err); + std::process::abort() + } + } +} + +#[cfg(any(target_os = "android", target_os = "ios"))] +fn _start_app() { + stop_unwind(|| main().unwrap()); +} + +#[no_mangle] +#[inline(never)] +#[cfg(any(target_os = "android", target_os = "ios"))] +pub extern "C" fn start_app() { + #[cfg(target_os = "android")] + android_binding!(com_example, mobile_demo, _start_app); + #[cfg(target_os = "ios")] + _start_app() +} + +pub fn main() -> Result<()> { + init_logging(); + + // Right now we're going through dioxus-desktop but we'd like to go through dioxus-mobile + // That will seed the index.html with some fixes that prevent the page from scrolling/zooming etc + LaunchBuilder::mobile() + .with_cfg( + // Note that we have to disable the viewport goofiness of the browser. + // Dioxus_mobile should do this for us + Config::default().with_custom_index(include_str!("index.html").to_string()), + ) + .launch(app); + + Ok(()) +} + +fn app() -> Element { + let mut items = use_signal(|| vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); + + log::debug!("Hello from the app"); + + rsx! { + div { + h1 { "Hello, Mobile" } + div { + margin_left: "auto", + margin_right: "auto", + width: "200px", + padding: "10px", + border: "1px solid black", + button { + onclick: move |_| { + items.push(items.len()); + }, + "Add item" + } + for item in items.iter() { + div { "- {item}" } + } + } + } + } +} diff --git a/examples/multiwindow.rs b/examples/multiwindow.rs index d89a78ee6d..6858f65896 100644 --- a/examples/multiwindow.rs +++ b/examples/multiwindow.rs @@ -7,7 +7,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch_desktop(app); } fn app() -> Element { diff --git a/examples/nested_listeners.rs b/examples/nested_listeners.rs index c2316aed37..33cf3d4043 100644 --- a/examples/nested_listeners.rs +++ b/examples/nested_listeners.rs @@ -7,7 +7,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/openid_connect_demo/.cargo/config.toml b/examples/openid_connect_demo/.cargo/config.toml new file mode 100644 index 0000000000..fcb494816a --- /dev/null +++ b/examples/openid_connect_demo/.cargo/config.toml @@ -0,0 +1,5 @@ +[env] +DIOXUS_FRONT_ISSUER_URL = "TODO" +DIOXUS_FRONT_CLIENT_ID = "TODO" +DIOXUS_FRONT_CLIENT_SECRET = "TODO" +DIOXUS_FRONT_URL = "http://localhost:8080" diff --git a/examples/openid_connect_demo/.gitignore b/examples/openid_connect_demo/.gitignore new file mode 100644 index 0000000000..919c8ee550 --- /dev/null +++ b/examples/openid_connect_demo/.gitignore @@ -0,0 +1,3 @@ +/target +/dist +.env diff --git a/examples/openid_connect_demo/Cargo.lock b/examples/openid_connect_demo/Cargo.lock new file mode 100644 index 0000000000..b7d513c11e --- /dev/null +++ b/examples/openid_connect_demo/Cargo.lock @@ -0,0 +1,6372 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + +[[package]] +name = "anymap" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33954243bd79057c2de7338850b85983a44588021f8a5fee574a8888c6de4344" + +[[package]] +name = "anymap2" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c" + +[[package]] +name = "ashpd" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd884d7c72877a94102c3715f3b1cd09ff4fac28221add3e57cfbe25c236d093" +dependencies = [ + "async-fs", + "async-net", + "enumflags2", + "futures-channel", + "futures-util", + "rand 0.8.5", + "serde", + "serde_repr", + "url", + "zbus", +] + +[[package]] +name = "askama_escape" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341" + +[[package]] +name = "async-broadcast" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cd0e2e25ea8e5f7e9df04578dc6cf5c83577fd09b1a46aaf5c85e1c33f2a7e" +dependencies = [ + "event-listener", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-channel" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8828ec6e544c02b0d6691d21ed9f9218d0384a82542855073c2a3f58304aaf0" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "slab", +] + +[[package]] +name = "async-fs" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" +dependencies = [ + "async-lock", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-io" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d6baa8f0178795da0e71bc42c9e5d13261aac7ee549853162e66a241ba17964" +dependencies = [ + "async-lock", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "async-lock" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-net" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" +dependencies = [ + "async-io", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-process" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7eda79bbd84e29c2b308d1dc099d7de8dcc7035e48f4bf5dc4a531a44ff5e2a" +dependencies = [ + "async-channel", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "async-recursion" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "async-signal" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "329972aa325176e89114919f2a80fdae4f4c040f66a370b1a1159c6c0f94e7aa" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix", + "signal-hook-registry", + "slab", + "windows-sys 0.52.0", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "async-trait" +version = "0.1.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "atk" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4af014b17dd80e8af9fa689b2d4a211ddba6eb583c1622f35d0cb543f6b17e4" +dependencies = [ + "atk-sys", + "glib", + "libc", +] + +[[package]] +name = "atk-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "251e0b7d90e33e0ba930891a505a9a35ece37b2dd37a14f3ffc306c13b980009" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "atomic-polyfill" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" +dependencies = [ + "critical-section", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "axum" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" +dependencies = [ + "async-trait", + "axum-core", + "axum-macros", + "base64 0.21.7", + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", + "hyper 1.3.1", + "hyper-util", + "itoa 1.0.11", + "matchit", + "memchr", + "mime", + "multer", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sha1", + "sync_wrapper 1.0.1", + "tokio", + "tokio-tungstenite", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper 0.1.2", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00c055ee2d014ae5981ce1016374e8213682aa14d9bf40e48ab48b5f3ef20eaa" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "backtrace" +version = "0.3.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17c6a35df3749d2e8bb1b7b21a976d82b15548788d2735b9d82f329268f71a11" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +dependencies = [ + "serde", +] + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "blocking" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + +[[package]] +name = "bstr" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" + +[[package]] +name = "cairo-rs" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" +dependencies = [ + "bitflags 2.5.0", + "cairo-sys-rs", + "glib", + "libc", + "once_cell", + "thiserror", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "camino" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "cc" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfb" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" +dependencies = [ + "byteorder", + "fnv", + "uuid", +] + +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-targets 0.52.5", +] + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "cobs" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" + +[[package]] +name = "cocoa" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" +dependencies = [ + "bitflags 1.3.2", + "block", + "cocoa-foundation", + "core-foundation", + "core-graphics", + "foreign-types", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" +dependencies = [ + "bitflags 1.3.2", + "block", + "core-foundation", + "core-graphics-types", + "libc", + "objc", +] + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "const_format" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a214c7af3d04997541b18d432afaff4c455e79e2029079647e72fc2bd27673" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[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-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "core-graphics" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "critical-section" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" + +[[package]] +name = "crossbeam-channel" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +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.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array 0.14.7", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array 0.14.7", + "typenum", +] + +[[package]] +name = "cssparser" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa 0.4.8", + "matches", + "phf 0.8.0", + "proc-macro2", + "quote", + "smallvec", + "syn 1.0.109", +] + +[[package]] +name = "cssparser-macros" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" +dependencies = [ + "quote", + "syn 2.0.66", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "platforms", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "darling" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.66", +] + +[[package]] +name = "darling_macro" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "data-encoding" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" + +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", + "serde", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case 0.4.0", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "dioxus" +version = "0.5.2" +dependencies = [ + "dioxus-config-macro", + "dioxus-core", + "dioxus-core-macro", + "dioxus-desktop", + "dioxus-fullstack", + "dioxus-hooks", + "dioxus-hot-reload", + "dioxus-html", + "dioxus-liveview", + "dioxus-router", + "dioxus-signals", + "dioxus-ssr", + "dioxus-static-site-generation", + "dioxus-web", + "serde", +] + +[[package]] +name = "dioxus-cli-config" +version = "0.5.2" +dependencies = [ + "once_cell", + "serde", + "serde_json", + "tracing", +] + +[[package]] +name = "dioxus-config-macro" +version = "0.5.2" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "dioxus-core" +version = "0.5.2" +dependencies = [ + "futures-channel", + "futures-util", + "generational-box", + "longest-increasing-subsequence", + "rustc-hash", + "serde", + "slab", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "dioxus-core-macro" +version = "0.5.2" +dependencies = [ + "convert_case 0.6.0", + "dioxus-rsx", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "dioxus-debug-cell" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ea539174bb236e0e7dc9c12b19b88eae3cb574dedbd0252a2d43ea7e6de13e2" + +[[package]] +name = "dioxus-desktop" +version = "0.5.2" +dependencies = [ + "async-trait", + "cocoa", + "core-foundation", + "dioxus-cli-config", + "dioxus-core", + "dioxus-hooks", + "dioxus-hot-reload", + "dioxus-html", + "dioxus-interpreter-js", + "dunce", + "futures-channel", + "futures-util", + "generational-box", + "global-hotkey", + "infer", + "muda", + "objc", + "objc_id", + "rfd", + "rustc-hash", + "serde", + "serde_json", + "signal-hook", + "slab", + "tao", + "thiserror", + "tokio", + "tracing", + "urlencoding", + "webbrowser", + "wry", +] + +[[package]] +name = "dioxus-fullstack" +version = "0.5.2" +dependencies = [ + "anymap", + "async-trait", + "axum", + "base64 0.21.7", + "bytes", + "ciborium", + "dioxus-cli-config", + "dioxus-desktop", + "dioxus-hot-reload", + "dioxus-lib", + "dioxus-ssr", + "dioxus-web", + "dioxus_server_macro", + "futures-util", + "http 1.1.0", + "hyper 1.3.1", + "once_cell", + "pin-project", + "serde", + "serde_json", + "server_fn", + "thiserror", + "tokio", + "tokio-stream", + "tokio-util", + "tower", + "tower-http", + "tower-layer", + "tracing", + "tracing-futures", + "web-sys", +] + +[[package]] +name = "dioxus-hooks" +version = "0.5.2" +dependencies = [ + "dioxus-core", + "dioxus-debug-cell", + "dioxus-signals", + "futures-channel", + "futures-util", + "generational-box", + "slab", + "thiserror", + "tracing", +] + +[[package]] +name = "dioxus-hot-reload" +version = "0.5.2" +dependencies = [ + "axum", + "chrono", + "dioxus-core", + "dioxus-html", + "dioxus-rsx", + "execute", + "futures-util", + "ignore", + "interprocess-docfix", + "notify", + "once_cell", + "serde", + "serde_json", + "tokio", + "tokio-stream", + "tracing", +] + +[[package]] +name = "dioxus-html" +version = "0.5.2" +dependencies = [ + "async-trait", + "dioxus-core", + "dioxus-html-internal-macro", + "dioxus-rsx", + "enumset", + "euclid", + "futures-channel", + "generational-box", + "keyboard-types", + "serde", + "serde-value", + "serde_json", + "serde_repr", + "tokio", + "tracing", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "dioxus-html-internal-macro" +version = "0.5.2" +dependencies = [ + "convert_case 0.6.0", + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "dioxus-interpreter-js" +version = "0.5.2" +dependencies = [ + "dioxus-core", + "dioxus-html", + "js-sys", + "md5", + "sledgehammer_bindgen", + "sledgehammer_utils", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "dioxus-lib" +version = "0.5.2" +dependencies = [ + "dioxus-config-macro", + "dioxus-core", + "dioxus-core-macro", + "dioxus-hooks", + "dioxus-html", + "dioxus-rsx", + "dioxus-signals", +] + +[[package]] +name = "dioxus-liveview" +version = "0.5.2" +dependencies = [ + "axum", + "dioxus-cli-config", + "dioxus-core", + "dioxus-hot-reload", + "dioxus-html", + "dioxus-interpreter-js", + "futures-channel", + "futures-util", + "generational-box", + "rustc-hash", + "serde", + "serde_json", + "slab", + "thiserror", + "tokio", + "tokio-stream", + "tokio-util", + "tracing", +] + +[[package]] +name = "dioxus-logger" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe09dc9773dc1f1bb0d38529203d6555d08f67aadca5cf955ac3d1a9e69880" +dependencies = [ + "console_error_panic_hook", + "tracing", + "tracing-subscriber", + "tracing-wasm", +] + +[[package]] +name = "dioxus-router" +version = "0.5.2" +dependencies = [ + "dioxus-cli-config", + "dioxus-fullstack", + "dioxus-lib", + "dioxus-router-macro", + "dioxus-ssr", + "gloo", + "gloo-utils 0.1.7", + "http 1.1.0", + "js-sys", + "tokio", + "tracing", + "url", + "urlencoding", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "dioxus-router-macro" +version = "0.5.2" +dependencies = [ + "proc-macro2", + "quote", + "slab", + "syn 2.0.66", +] + +[[package]] +name = "dioxus-rsx" +version = "0.5.2" +dependencies = [ + "dioxus-core", + "internment", + "krates", + "proc-macro2", + "quote", + "syn 2.0.66", + "tracing", +] + +[[package]] +name = "dioxus-sdk" +version = "0.5.0" +source = "git+https://github.com/Dioxuslabs/sdk#d49812fbc9d4506bd3b1ec994f45ef4447f34c79" +dependencies = [ + "cfg-if", + "dioxus", + "dioxus-signals", + "directories", + "futures-util", + "js-sys", + "once_cell", + "postcard", + "rustc-hash", + "serde", + "tokio", + "tracing", + "uuid", + "wasm-bindgen", + "web-sys", + "yazi", +] + +[[package]] +name = "dioxus-signals" +version = "0.5.2" +dependencies = [ + "dioxus-core", + "futures-channel", + "futures-util", + "generational-box", + "once_cell", + "parking_lot", + "rustc-hash", + "serde", + "tracing", +] + +[[package]] +name = "dioxus-ssr" +version = "0.5.2" +dependencies = [ + "askama_escape", + "async-trait", + "chrono", + "dioxus-cli-config", + "dioxus-core", + "dioxus-html", + "generational-box", + "http 1.1.0", + "lru", + "rustc-hash", + "serde_json", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "dioxus-static-site-generation" +version = "0.5.2" +dependencies = [ + "axum", + "dioxus-cli-config", + "dioxus-fullstack", + "dioxus-hot-reload", + "dioxus-lib", + "dioxus-router", + "dioxus-ssr", + "dioxus-web", + "http 1.1.0", + "tokio", + "tower", + "tower-http", + "tracing", +] + +[[package]] +name = "dioxus-web" +version = "0.5.2" +dependencies = [ + "async-trait", + "console_error_panic_hook", + "dioxus-core", + "dioxus-html", + "dioxus-interpreter-js", + "futures-channel", + "futures-util", + "generational-box", + "js-sys", + "rustc-hash", + "serde", + "serde-wasm-bindgen", + "serde_json", + "tracing", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "dioxus_server_macro" +version = "0.5.2" +dependencies = [ + "convert_case 0.6.0", + "proc-macro2", + "quote", + "server_fn_macro", + "syn 2.0.66", +] + +[[package]] +name = "directories" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + +[[package]] +name = "dlopen2" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1297103d2bbaea85724fcee6294c2d50b1081f9ad47d0f6f6f61eda65315a6" +dependencies = [ + "dlopen2_derive", + "libc", + "once_cell", + "winapi", +] + +[[package]] +name = "dlopen2_derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "dtoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" + +[[package]] +name = "dtoa-short" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbaceec3c6e4211c79e7b1800fb9680527106beb2f9c51904a3210c03a448c74" +dependencies = [ + "dtoa", +] + +[[package]] +name = "dunce" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" + +[[package]] +name = "dyn-clone" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +dependencies = [ + "curve25519-dalek", + "ed25519", + "serde", + "sha2", + "subtle", + "zeroize", +] + +[[package]] +name = "either" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array 0.14.7", + "group", + "hkdf", + "pem-rfc7468", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" + +[[package]] +name = "encoding_rs" +version = "0.8.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +dependencies = [ + "cfg-if", +] + +[[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.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3278c9d5fb675e0a51dabcf4c0d355f692b064171535ba72361be1528a9d8e8d" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c785274071b1b420972453b306eeca06acf4633829db4223b58a2a8c5953bc4" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "enumset" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "226c0da7462c13fb57e5cc9e0dc8f0635e7d27f276a3a7fd30054647f669007d" +dependencies = [ + "enumset_derive", +] + +[[package]] +name = "enumset_derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08b6c6ab82d70f08844964ba10c7babb716de2ecaeab9be5717918a5177d3af" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[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.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "euclid" +version = "0.22.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0f0eb73b934648cd7a4a61f1b15391cd95dab0b4da6e2e66c2a072c144b4a20" +dependencies = [ + "num-traits", + "serde", +] + +[[package]] +name = "event-listener" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +dependencies = [ + "event-listener", + "pin-project-lite", +] + +[[package]] +name = "execute" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a82608ee96ce76aeab659e9b8d3c2b787bffd223199af88c674923d861ada10" +dependencies = [ + "execute-command-macro", + "execute-command-tokens", + "generic-array 1.0.0", +] + +[[package]] +name = "execute-command-macro" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90dec53d547564e911dc4ff3ecb726a64cf41a6fa01a2370ebc0d95175dd08bd" +dependencies = [ + "execute-command-macro-impl", +] + +[[package]] +name = "execute-command-macro-impl" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce8cd46a041ad005ab9c71263f9a0ff5b529eac0fe4cc9b4a20f4f0765d8cf4b" +dependencies = [ + "execute-command-tokens", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "execute-command-tokens" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69dc321eb6be977f44674620ca3aa21703cb20ffbe560e1ae97da08401ffbcad" + +[[package]] +name = "fastrand" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" + +[[package]] +name = "fdeflate" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "field-offset" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" +dependencies = [ + "memoffset", + "rustc_version", +] + +[[package]] +name = "filetime" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.4.1", + "windows-sys 0.52.0", +] + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "flate2" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[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 = "fsevent-sys" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" +dependencies = [ + "libc", +] + +[[package]] +name = "futf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" +dependencies = [ + "mac", + "new_debug_unreachable", +] + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-lite" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "gdk" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5ba081bdef3b75ebcdbfc953699ed2d7417d6bd853347a42a37d76406a33646" +dependencies = [ + "cairo-rs", + "gdk-pixbuf", + "gdk-sys", + "gio", + "glib", + "libc", + "pango", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" +dependencies = [ + "gdk-pixbuf-sys", + "gio", + "glib", + "libc", + "once_cell", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gdk-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31ff856cb3386dae1703a920f803abafcc580e9b5f711ca62ed1620c25b51ff2" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkwayland-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a90fbf5c033c65d93792192a49a8efb5bb1e640c419682a58bb96f5ae77f3d4a" +dependencies = [ + "gdk-sys", + "glib-sys", + "gobject-sys", + "libc", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkx11" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2ea8a4909d530f79921290389cbd7c34cb9d623bfe970eaae65ca5f9cd9cce" +dependencies = [ + "gdk", + "gdkx11-sys", + "gio", + "glib", + "libc", + "x11", +] + +[[package]] +name = "gdkx11-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fee8f00f4ee46cad2939b8990f5c70c94ff882c3028f3cc5abf950fa4ab53043" +dependencies = [ + "gdk-sys", + "glib-sys", + "libc", + "system-deps", + "x11", +] + +[[package]] +name = "generational-box" +version = "0.5.2" +dependencies = [ + "parking_lot", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "generic-array" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe739944a5406424e080edccb6add95685130b9f160d5407c639c7df0c5836b0" +dependencies = [ + "typenum", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "gimli" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" + +[[package]] +name = "gio" +version = "0.18.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys", + "glib", + "libc", + "once_cell", + "pin-project-lite", + "smallvec", + "thiserror", +] + +[[package]] +name = "gio-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", + "winapi", +] + +[[package]] +name = "glib" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" +dependencies = [ + "bitflags 2.5.0", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "memchr", + "once_cell", + "smallvec", + "thiserror", +] + +[[package]] +name = "glib-macros" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" +dependencies = [ + "heck 0.4.1", + "proc-macro-crate 2.0.2", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "glib-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" +dependencies = [ + "libc", + "system-deps", +] + +[[package]] +name = "global-hotkey" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89cb13e8c52c87e28a46eae3e5e65b8f0cd465c4c9e67b13d56c70412e792bc3" +dependencies = [ + "bitflags 2.5.0", + "cocoa", + "crossbeam-channel", + "keyboard-types", + "objc", + "once_cell", + "thiserror", + "windows-sys 0.52.0", + "x11-dl", +] + +[[package]] +name = "globset" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "gloo" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28999cda5ef6916ffd33fb4a7b87e1de633c47c0dc6d97905fee1cdaa142b94d" +dependencies = [ + "gloo-console", + "gloo-dialogs", + "gloo-events", + "gloo-file", + "gloo-history", + "gloo-net 0.3.1", + "gloo-render", + "gloo-storage", + "gloo-timers", + "gloo-utils 0.1.7", + "gloo-worker", +] + +[[package]] +name = "gloo-console" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b7ce3c05debe147233596904981848862b068862e9ec3e34be446077190d3f" +dependencies = [ + "gloo-utils 0.1.7", + "js-sys", + "serde", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-dialogs" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67062364ac72d27f08445a46cab428188e2e224ec9e37efdba48ae8c289002e6" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-events" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b107f8abed8105e4182de63845afcc7b69c098b7852a813ea7462a320992fc" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-file" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d5564e570a38b43d78bdc063374a0c3098c4f0d64005b12f9bbe87e869b6d7" +dependencies = [ + "gloo-events", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-history" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85725d90bf0ed47063b3930ef28e863658a7905989e9929a8708aab74a1d5e7f" +dependencies = [ + "gloo-events", + "gloo-utils 0.1.7", + "serde", + "serde-wasm-bindgen", + "serde_urlencoded", + "thiserror", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-net" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a66b4e3c7d9ed8d315fd6b97c8b1f74a7c6ecbbc2320e65ae7ed38b7068cc620" +dependencies = [ + "futures-channel", + "futures-core", + "futures-sink", + "gloo-utils 0.1.7", + "http 0.2.12", + "js-sys", + "pin-project", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-net" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43aaa242d1239a8822c15c645f02166398da4f8b5c4bae795c1f5b44e9eee173" +dependencies = [ + "futures-channel", + "futures-core", + "futures-sink", + "gloo-utils 0.2.0", + "http 0.2.12", + "js-sys", + "pin-project", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-render" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd9306aef67cfd4449823aadcd14e3958e0800aa2183955a309112a84ec7764" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-storage" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6ab60bf5dbfd6f0ed1f7843da31b41010515c745735c970e821945ca91e480" +dependencies = [ + "gloo-utils 0.1.7", + "js-sys", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-timers" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "gloo-utils" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037fcb07216cb3a30f7292bd0176b050b7b9a052ba830ef7d5d65f6dc64ba58e" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-utils" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-worker" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13471584da78061a28306d1359dd0178d8d6fc1c7c80e5e35d27260346e0516a" +dependencies = [ + "anymap2", + "bincode", + "gloo-console", + "gloo-utils 0.1.7", + "js-sys", + "serde", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gobject-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "gtk" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93c4f5e0e20b60e10631a5f06da7fe3dda744b05ad0ea71fee2f47adf865890c" +dependencies = [ + "atk", + "cairo-rs", + "field-offset", + "futures-channel", + "gdk", + "gdk-pixbuf", + "gio", + "glib", + "gtk-sys", + "gtk3-macros", + "libc", + "pango", + "pkg-config", +] + +[[package]] +name = "gtk-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771437bf1de2c1c0b496c11505bdf748e26066bbe942dfc8f614c9460f6d7722" +dependencies = [ + "atk-sys", + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "system-deps", +] + +[[package]] +name = "gtk3-macros" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6063efb63db582968fb7df72e1ae68aa6360dcfb0a75143f34fc7d616bad75e" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap 2.2.6", + "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", + "crunchy", +] + +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "heapless" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" +dependencies = [ + "atomic-polyfill", + "hash32", + "rustc_version", + "serde", + "spin 0.9.8", + "stable_deref_trait", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "html5ever" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" +dependencies = [ + "log", + "mac", + "markup5ever", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa 1.0.11", +] + +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa 1.0.11", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + +[[package]] +name = "http-body" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +dependencies = [ + "bytes", + "http 1.1.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" +dependencies = [ + "bytes", + "futures-core", + "http 1.1.0", + "http-body 1.0.0", + "pin-project-lite", +] + +[[package]] +name = "http-range-header" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08a397c49fec283e3d6211adbe480be95aae5f304cfb923e9970e08956d5168a" + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "0.14.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http 0.2.12", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa 1.0.11", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "httparse", + "httpdate", + "itoa 1.0.11", + "pin-project-lite", + "smallvec", + "tokio", +] + +[[package]] +name = "hyper-rustls" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +dependencies = [ + "futures-util", + "http 0.2.12", + "hyper 0.14.29", + "rustls", + "tokio", + "tokio-rustls", +] + +[[package]] +name = "hyper-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b875924a60b96e5d7b9ae7b066540b1dd1cbd90d1828f54c92e02a283351c56" +dependencies = [ + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "hyper 1.3.1", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +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 = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "ignore" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata", + "same-file", + "walkdir", + "winapi-util", +] + +[[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.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown 0.14.5", + "serde", +] + +[[package]] +name = "infer" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6c16b11a665b26aeeb9b1d7f954cdeb034be38dd00adab4f2ae921a8fee804" +dependencies = [ + "cfb", +] + +[[package]] +name = "inotify" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" +dependencies = [ + "bitflags 1.3.2", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "internment" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04e8e537b529b8674e97e9fb82c10ff168a290ac3867a0295f112061ffbca1ef" +dependencies = [ + "hashbrown 0.14.5", + "parking_lot", +] + +[[package]] +name = "interprocess-docfix" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b84ee245c606aeb0841649a9288e3eae8c61b853a8cd5c0e14450e96d53d28f" +dependencies = [ + "blocking", + "cfg-if", + "futures-core", + "futures-io", + "intmap", + "libc", + "once_cell", + "rustc_version", + "spinning", + "thiserror", + "to_method", + "winapi", +] + +[[package]] +name = "intmap" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae52f28f45ac2bc96edb7714de995cffc174a395fb0abf5bff453587c980d7b9" + +[[package]] +name = "inventory" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f958d3d68f4167080a18141e10381e7634563984a537f2a49a30fd8e53ac5767" + +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "javascriptcore-rs" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca5671e9ffce8ffba57afc24070e906da7fc4b1ba66f2cabebf61bf2ea257fcc" +dependencies = [ + "bitflags 1.3.2", + "glib", + "javascriptcore-rs-sys", +] + +[[package]] +name = "javascriptcore-rs-sys" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1be78d14ffa4b75b66df31840478fef72b51f8c2465d4ca7c194da9f7a5124" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror", + "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 = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "keyboard-types" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" +dependencies = [ + "bitflags 2.5.0", + "serde", + "unicode-segmentation", +] + +[[package]] +name = "kqueue" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +dependencies = [ + "bitflags 1.3.2", + "libc", +] + +[[package]] +name = "krates" +version = "0.16.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcb3baf2360eb25ad31f0ada3add63927ada6db457791979b82ac199f835cb9" +dependencies = [ + "cargo-platform", + "cargo_metadata", + "cfg-expr", + "petgraph", + "semver", +] + +[[package]] +name = "kuchikiki" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e4755b7b995046f510a7520c42b2fed58b77bd94d5a87a8eb43d2fd126da8" +dependencies = [ + "cssparser", + "html5ever", + "indexmap 1.9.3", + "matches", + "selectors", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin 0.5.2", +] + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.5.0", + "libc", +] + +[[package]] +name = "libxdo" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00333b8756a3d28e78def82067a377de7fa61b24909000aeaa2b446a948d14db" +dependencies = [ + "libxdo-sys", +] + +[[package]] +name = "libxdo-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db23b9e7e2b7831bbd8aac0bbeeeb7b68cbebc162b227e7052e8e55829a09212" +dependencies = [ + "libc", + "x11", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[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 = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "longest-increasing-subsequence" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3bd0dd2cd90571056fdb71f6275fada10131182f84899f4b2a916e565d81d86" + +[[package]] +name = "lru" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" +dependencies = [ + "hashbrown 0.14.5", +] + +[[package]] +name = "mac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "markup5ever" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" +dependencies = [ + "log", + "phf 0.10.1", + "phf_codegen 0.10.0", + "string_cache", + "string_cache_codegen", + "tendril", +] + +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + +[[package]] +name = "md5" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" + +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[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 = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "miniz_oxide" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" +dependencies = [ + "adler", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.48.0", +] + +[[package]] +name = "muda" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c47e7625990fc1af2226ea4f34fb2412b03c12639fcb91868581eb3a6893453" +dependencies = [ + "cocoa", + "crossbeam-channel", + "gtk", + "keyboard-types", + "libxdo", + "objc", + "once_cell", + "png", + "thiserror", + "windows-sys 0.52.0", +] + +[[package]] +name = "multer" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b" +dependencies = [ + "bytes", + "encoding_rs", + "futures-util", + "http 1.1.0", + "httparse", + "memchr", + "mime", + "spin 0.9.8", + "version_check", +] + +[[package]] +name = "ndk" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" +dependencies = [ + "bitflags 1.3.2", + "jni-sys", + "ndk-sys", + "num_enum", + "raw-window-handle 0.5.2", + "thiserror", +] + +[[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.4.1+23.1.7779620" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cf2aae958bd232cac5069850591667ad422d263686d75b52a065f9badeee5a3" +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.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags 2.5.0", + "cfg-if", + "libc", + "memoffset", +] + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + +[[package]] +name = "notify" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "729f63e1ca555a43fe3efa4f3efdf4801c479da85b432242a7b726f353c88486" +dependencies = [ + "bitflags 1.3.2", + "crossbeam-channel", + "filetime", + "fsevent-sys", + "inotify", + "kqueue", + "libc", + "mio", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand 0.8.5", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[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-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "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", + "libm", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "oauth2" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c38841cdd844847e3e7c8d29cef9dcfed8877f8f56f9071f77843ecf3baf937f" +dependencies = [ + "base64 0.13.1", + "chrono", + "getrandom 0.2.15", + "http 0.2.12", + "rand 0.8.5", + "reqwest", + "serde", + "serde_json", + "serde_path_to_error", + "sha2", + "thiserror", + "url", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", + "objc_exception", +] + +[[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_exception" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" +dependencies = [ + "cc", +] + +[[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.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "openid_auth_demo" +version = "0.1.0" +dependencies = [ + "anyhow", + "console_error_panic_hook", + "dioxus", + "dioxus-logger", + "dioxus-sdk", + "form_urlencoded", + "log", + "openidconnect", + "serde", + "uuid", +] + +[[package]] +name = "openidconnect" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f47e80a9cfae4462dd29c41e987edd228971d6565553fbc14b8a11e666d91590" +dependencies = [ + "base64 0.13.1", + "chrono", + "dyn-clone", + "ed25519-dalek", + "hmac", + "http 0.2.12", + "itertools", + "log", + "oauth2", + "p256", + "p384", + "rand 0.8.5", + "rsa", + "serde", + "serde-value", + "serde_derive", + "serde_json", + "serde_path_to_error", + "serde_plain", + "serde_with", + "sha2", + "subtle", + "thiserror", + "url", +] + +[[package]] +name = "ordered-float" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +dependencies = [ + "num-traits", +] + +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "p384" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "pango" +version = "0.18.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" +dependencies = [ + "gio", + "glib", + "libc", + "once_cell", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "parking" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" + +[[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", + "libc", + "redox_syscall 0.5.1", + "smallvec", + "windows-targets 0.52.5", +] + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "petgraph" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset", + "indexmap 2.2.6", +] + +[[package]] +name = "phf" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" +dependencies = [ + "phf_macros", + "phf_shared 0.8.0", + "proc-macro-hack", +] + +[[package]] +name = "phf" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" +dependencies = [ + "phf_shared 0.10.0", +] + +[[package]] +name = "phf_codegen" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" +dependencies = [ + "phf_generator 0.8.0", + "phf_shared 0.8.0", +] + +[[package]] +name = "phf_codegen" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", +] + +[[package]] +name = "phf_generator" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" +dependencies = [ + "phf_shared 0.8.0", + "rand 0.7.3", +] + +[[package]] +name = "phf_generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +dependencies = [ + "phf_shared 0.10.0", + "rand 0.8.5", +] + +[[package]] +name = "phf_macros" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" +dependencies = [ + "phf_generator 0.8.0", + "phf_shared 0.8.0", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "phf_shared" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" +dependencies = [ + "siphasher", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[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.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1d5c74c9876f070d3e8fd503d748c7d974c3e48da8f41350fa5222ef9b4391" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "platforms" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7" + +[[package]] +name = "png" +version = "0.17.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "polling" +version = "3.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6a007746f34ed64099e88783b0ae369eaa3da6392868ba262e2af9b8fbaea1" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "pollster" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2" + +[[package]] +name = "postcard" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a55c51ee6c0db07e68448e336cf8ea4131a620edefebf9893e759b2d793420f8" +dependencies = [ + "cobs", + "embedded-io", + "heapless", + "serde", +] + +[[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.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "prettyplease" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" +dependencies = [ + "proc-macro2", + "syn 2.0.66", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" +dependencies = [ + "toml_datetime", + "toml_edit 0.20.2", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + +[[package]] +name = "proc-macro2" +version = "1.0.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", + "rand_pcg", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[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 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.15", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "raw-window-handle" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" + +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +dependencies = [ + "bitflags 2.5.0", +] + +[[package]] +name = "redox_users" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +dependencies = [ + "getrandom 0.2.15", + "libredox", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" + +[[package]] +name = "reqwest" +version = "0.11.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +dependencies = [ + "base64 0.21.7", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.29", + "hyper-rustls", + "ipnet", + "js-sys", + "log", + "mime", + "mime_guess", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 0.1.2", + "system-configuration", + "tokio", + "tokio-rustls", + "tokio-util", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "webpki-roots", + "winreg", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "rfd" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a73a7337fc24366edfca76ec521f51877b114e42dab584008209cca6719251" +dependencies = [ + "ashpd", + "block", + "dispatch", + "js-sys", + "log", + "objc", + "objc-foundation", + "objc_id", + "pollster", + "raw-window-handle 0.6.2", + "urlencoding", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-sys 0.48.0", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.15", + "libc", + "spin 0.9.8", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rsa" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" +dependencies = [ + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core 0.6.4", + "signature", + "spki", + "subtle", + "zeroize", +] + +[[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_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags 2.5.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.21.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + +[[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 = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array 0.14.7", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "selectors" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe" +dependencies = [ + "bitflags 1.3.2", + "cssparser", + "derive_more", + "fxhash", + "log", + "matches", + "phf 0.8.0", + "phf_codegen 0.8.0", + "precomputed-hash", + "servo_arc", + "smallvec", + "thin-slice", +] + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +dependencies = [ + "serde", +] + +[[package]] +name = "send_wrapper" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" +dependencies = [ + "futures-core", +] + +[[package]] +name = "serde" +version = "1.0.203" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float", + "serde", +] + +[[package]] +name = "serde-wasm-bindgen" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3b143e2833c57ab9ad3ea280d21fd34e285a42837aeb0ee301f4f41890fa00e" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "serde_derive" +version = "1.0.203" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "serde_json" +version = "1.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +dependencies = [ + "itoa 1.0.11", + "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 1.0.11", + "serde", +] + +[[package]] +name = "serde_plain" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1fc6db65a611022b23a0dec6975d63fb80a302cb3388835ff02c097258d50" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_qs" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0431a35568651e363364210c91983c1da5eb29404d9f0928b67d4ebcfa7d330c" +dependencies = [ + "percent-encoding", + "serde", + "thiserror", +] + +[[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.66", +] + +[[package]] +name = "serde_spanned" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" +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 1.0.11", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "3.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ad483d2ab0149d5a5ebcd9972a3852711e0153d863bf5a5d0391d28883c4a20" +dependencies = [ + "base64 0.22.1", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.2.6", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65569b702f41443e8bc8bbb1c5779bd0450bbe723b56198980e80ec45780bce2" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "server_fn" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b06e6e5467a2cd93ce1accfdfd8b859404f0b3b2041131ffd774fabf666b8219" +dependencies = [ + "axum", + "bytes", + "const_format", + "dashmap", + "futures", + "gloo-net 0.5.0", + "http 1.1.0", + "http-body-util", + "hyper 1.3.1", + "inventory", + "js-sys", + "once_cell", + "reqwest", + "send_wrapper", + "serde", + "serde_json", + "serde_qs", + "server_fn_macro_default", + "thiserror", + "tower", + "tower-layer", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "xxhash-rust", +] + +[[package]] +name = "server_fn_macro" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09c216bb1c1ac890151397643c663c875a1836adf0b269be4e389cb1b48c173c" +dependencies = [ + "const_format", + "convert_case 0.6.0", + "proc-macro2", + "quote", + "syn 2.0.66", + "xxhash-rust", +] + +[[package]] +name = "server_fn_macro_default" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00783df297ec85ea605779f2fef9cbec98981dffe2e01e1a9845c102ee1f1ae6" +dependencies = [ + "server_fn_macro", + "syn 2.0.66", +] + +[[package]] +name = "servo_arc" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432" +dependencies = [ + "nodrop", + "stable_deref_trait", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[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 = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core 0.6.4", +] + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "sledgehammer_bindgen" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcfaf791ff02f48f3518ce825d32cf419c13a43c1d8b1232f74ac89f339c46d2" +dependencies = [ + "sledgehammer_bindgen_macro", + "wasm-bindgen", +] + +[[package]] +name = "sledgehammer_bindgen_macro" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdd941cc539bd3dc694edaf9d0c4e1221d02baa67c6b45ec04fad1024d9e8139" +dependencies = [ + "quote", + "syn 2.0.66", +] + +[[package]] +name = "sledgehammer_utils" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f20798defa0e9d4eff9ca451c7f84774c7378a9c3b5a40112cfa2b3eadb97ae2" +dependencies = [ + "lru", + "once_cell", + "rustc-hash", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "soup3" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "471f924a40f31251afc77450e781cb26d55c0b650842efafc9c6cbd2f7cc4f9f" +dependencies = [ + "futures-channel", + "gio", + "glib", + "libc", + "soup3-sys", +] + +[[package]] +name = "soup3-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebe8950a680a12f24f15ebe1bf70db7af98ad242d9db43596ad3108aab86c27" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spinning" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d4f0e86297cad2658d92a707320d87bf4e6ae1050287f51d19b67ef3f153a7b" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[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 = "string_cache" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" +dependencies = [ + "new_debug_unreachable", + "once_cell", + "parking_lot", + "phf_shared 0.10.0", + "precomputed-hash", + "serde", +] + +[[package]] +name = "string_cache_codegen" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", + "proc-macro2", + "quote", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[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.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "sync_wrapper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +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 0.5.0", + "pkg-config", + "toml", + "version-compare", +] + +[[package]] +name = "tao" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69ebbccb78deb5a36744c079eea2981b4a48ecbbe6b1b2ffbaa528bea3f5e5db" +dependencies = [ + "bitflags 1.3.2", + "cocoa", + "core-foundation", + "core-graphics", + "crossbeam-channel", + "dispatch", + "dlopen2", + "gdkwayland-sys", + "gdkx11-sys", + "gtk", + "instant", + "jni", + "lazy_static", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "objc", + "once_cell", + "parking_lot", + "raw-window-handle 0.5.2", + "raw-window-handle 0.6.2", + "scopeguard", + "tao-macros", + "unicode-segmentation", + "url", + "windows 0.54.0", + "windows-version", + "x11-dl", +] + +[[package]] +name = "tao-macros" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec114582505d158b669b136e6851f85840c109819d77c42bb7c0709f727d18c2" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "target-lexicon" +version = "0.12.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" + +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys 0.52.0", +] + +[[package]] +name = "tendril" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" +dependencies = [ + "futf", + "mac", + "utf-8", +] + +[[package]] +name = "thin-slice" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" + +[[package]] +name = "thiserror" +version = "1.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "itoa 1.0.11", + "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.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "to_method" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c4ceeeca15c8384bbc3e011dbd8fccb7f068a440b752b7d9b32ceb0ca0e2e8" + +[[package]] +name = "tokio" +version = "1.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", + "tokio-util", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite", +] + +[[package]] +name = "tokio-util" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "futures-util", + "hashbrown 0.14.5", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.20.2", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.2.6", + "toml_datetime", + "winnow", +] + +[[package]] +name = "toml_edit" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +dependencies = [ + "indexmap 2.2.6", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" +dependencies = [ + "bitflags 2.5.0", + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", + "http-range-header", + "httpdate", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project", + "tracing", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "nu-ansi-term", + "sharded-slab", + "smallvec", + "thread_local", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "tracing-wasm" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4575c663a174420fa2d78f4108ff68f65bf2fbb7dd89f33749b6e826b3626e07" +dependencies = [ + "tracing", + "tracing-subscriber", + "wasm-bindgen", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "tungstenite" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http 1.1.0", + "httparse", + "log", + "rand 0.8.5", + "sha1", + "thiserror", + "url", + "utf-8", +] + +[[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", +] + +[[package]] +name = "unicase" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +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 = "uuid" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" +dependencies = [ + "getrandom 0.2.15", + "wasm-bindgen", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[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.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[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 = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[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.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.66", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "wasm-streams" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webbrowser" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db67ae75a9405634f5882791678772c94ff5f16a66535aae186e26aa0841fc8b" +dependencies = [ + "core-foundation", + "home", + "jni", + "log", + "ndk-context", + "objc", + "raw-window-handle 0.5.2", + "url", + "web-sys", +] + +[[package]] +name = "webkit2gtk" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76b1bc1e54c581da1e9f179d0b38512ba358fb1af2d634a1affe42e37172361a" +dependencies = [ + "bitflags 1.3.2", + "cairo-rs", + "gdk", + "gdk-sys", + "gio", + "gio-sys", + "glib", + "glib-sys", + "gobject-sys", + "gtk", + "gtk-sys", + "javascriptcore-rs", + "libc", + "once_cell", + "soup3", + "webkit2gtk-sys", +] + +[[package]] +name = "webkit2gtk-sys" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62daa38afc514d1f8f12b8693d30d5993ff77ced33ce30cd04deebc267a6d57c" +dependencies = [ + "bitflags 1.3.2", + "cairo-sys-rs", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "gtk-sys", + "javascriptcore-rs-sys", + "libc", + "pkg-config", + "soup3-sys", + "system-deps", +] + +[[package]] +name = "webpki-roots" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + +[[package]] +name = "webview2-com" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0ae9c7e420783826cf769d2c06ac9ba462f450eca5893bb8c6c6529a4e5dd33" +dependencies = [ + "webview2-com-macros", + "webview2-com-sys", + "windows 0.52.0", + "windows-core 0.52.0", + "windows-implement 0.52.0", + "windows-interface 0.52.0", +] + +[[package]] +name = "webview2-com-macros" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac1345798ecd8122468840bcdf1b95e5dc6d2206c5e4b0eafa078d061f59c9bc" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "webview2-com-sys" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6ad85fceee6c42fa3d61239eba5a11401bf38407a849ed5ea1b407df08cca72" +dependencies = [ + "thiserror", + "windows 0.52.0", + "windows-core 0.52.0", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +dependencies = [ + "windows-sys 0.52.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.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core 0.52.0", + "windows-implement 0.52.0", + "windows-interface 0.52.0", + "windows-targets 0.52.5", +] + +[[package]] +name = "windows" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49" +dependencies = [ + "windows-core 0.54.0", + "windows-implement 0.53.0", + "windows-interface 0.53.0", + "windows-targets 0.52.5", +] + +[[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.5", +] + +[[package]] +name = "windows-core" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65" +dependencies = [ + "windows-result", + "windows-targets 0.52.5", +] + +[[package]] +name = "windows-implement" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12168c33176773b86799be25e2a2ba07c7aab9968b37541f1094dbd7a60c8946" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "windows-implement" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942ac266be9249c84ca862f0a164a39533dc2f6f33dc98ec89c8da99b82ea0bd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "windows-interface" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d8dc32e0095a7eeccebd0e3f09e9509365ecb3fc6ac4d6f5f14a3f6392942d1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "windows-interface" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da33557140a288fae4e1d5f8873aaf9eb6613a9cf82c3e070223ff177f598b60" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "windows-result" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "749f0da9cc72d82e600d8d2e44cadd0b9eedb9038f71a1c58556ac1c5791813b" +dependencies = [ + "windows-targets 0.52.5", +] + +[[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.5", +] + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", +] + +[[package]] +name = "windows-version" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6998aa457c9ba8ff2fb9f13e9d2a930dabcea28f1d0ab94d687d8b3654844515" +dependencies = [ + "windows-targets 0.52.5", +] + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "wry" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b717040ba9771fd88eb428c6ea6b555f8e734ff8534f02c13e8f10d97f5935e" +dependencies = [ + "base64 0.21.7", + "block", + "cfg_aliases", + "cocoa", + "core-graphics", + "crossbeam-channel", + "dunce", + "gdkx11", + "gtk", + "html5ever", + "http 0.2.12", + "javascriptcore-rs", + "jni", + "kuchikiki", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "objc", + "objc_id", + "once_cell", + "percent-encoding", + "raw-window-handle 0.6.2", + "serde", + "serde_json", + "sha2", + "soup3", + "tao-macros", + "thiserror", + "webkit2gtk", + "webkit2gtk-sys", + "webview2-com", + "windows 0.52.0", + "windows-implement 0.52.0", + "windows-version", + "x11-dl", +] + +[[package]] +name = "x11" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "xdg-home" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21e5a325c3cb8398ad6cf859c1135b25dd29e186679cf2da7581d9679f63b38e" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "xxhash-rust" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927da81e25be1e1a2901d59b81b37dd2efd1fc9c9345a55007f09bf5a2d3ee03" + +[[package]] +name = "yazi" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c94451ac9513335b5e23d7a8a2b61a7102398b8cca5160829d313e84c9d98be1" + +[[package]] +name = "zbus" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b8e3d6ae3342792a6cc2340e4394334c7402f3d793b390d2c5494a4032b3030" +dependencies = [ + "async-broadcast", + "async-executor", + "async-fs", + "async-io", + "async-lock", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "derivative", + "enumflags2", + "event-listener", + "futures-core", + "futures-sink", + "futures-util", + "hex", + "nix", + "ordered-stream", + "rand 0.8.5", + "serde", + "serde_repr", + "sha1", + "static_assertions", + "tracing", + "uds_windows", + "windows-sys 0.52.0", + "xdg-home", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7a3e850ff1e7217a3b7a07eba90d37fe9bb9e89a310f718afcde5885ca9b6d7" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "regex", + "syn 1.0.109", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" +dependencies = [ + "serde", + "static_assertions", + "zvariant", +] + +[[package]] +name = "zerocopy" +version = "0.7.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zvariant" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e09e8be97d44eeab994d752f341e67b3b0d80512a8b315a0671d47232ef1b65" +dependencies = [ + "endi", + "enumflags2", + "serde", + "static_assertions", + "url", + "zvariant_derive", +] + +[[package]] +name = "zvariant_derive" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72a5857e2856435331636a9fbb415b09243df4521a267c5bedcd5289b4d5799e" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00bedb16a193cc12451873fee2a1bc6550225acece0e36f333e68326c73c8172" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] diff --git a/examples/openid_connect_demo/Cargo.toml b/examples/openid_connect_demo/Cargo.toml new file mode 100644 index 0000000000..72ae74dc98 --- /dev/null +++ b/examples/openid_connect_demo/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "openid_auth_demo" +version = "0.1.0" +edition = "2021" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1.0.86" +console_error_panic_hook = "0.1" +dioxus = { path = "../../packages/dioxus", default_features = true, features = [ + "router", + "signals", +], version = "*" } +dioxus-logger = "0.5.1" +dioxus-sdk = { git = "https://github.com/Dioxuslabs/sdk", features = [ + "storage", +] } +form_urlencoded = "1.2.1" +log = "0.4" +openidconnect = "3.5.0" +serde = { version = "1.0.203", features = ["derive"] } +uuid = "1.8" + +[features] +default = ["web"] +server = ["dioxus/axum"] +web = ["dioxus/web"] +desktop = ["dioxus/desktop"] +fullstack = ["dioxus/fullstack"] + +# since we're using dioxus from local path, inform dioxus-sdk to use it as well +[patch.crates-io] +dioxus = { path = "../../packages/dioxus" } +dioxus-signals = { path = "../../packages/signals" } diff --git a/examples/openid_connect_demo/Dioxus.toml b/examples/openid_connect_demo/Dioxus.toml new file mode 100644 index 0000000000..3a130e2ea3 --- /dev/null +++ b/examples/openid_connect_demo/Dioxus.toml @@ -0,0 +1,35 @@ +[application] + +# dioxus project name +name = "OpenID Connect authentication demo" + +# default platform +# you can also use `dioxus serve/build --platform XXX` to use other platform +# value: web | desktop +default_platform = "web" + +# Web `build` & `serve` dist path +out_dir = "dist" + +# resource (static) file folder +asset_dir = "public" + +# hot reload by default +hot_reload = true + +[web.app] + +# HTML title tag content +title = "OpenID Connect authentication demo" + +[web.watcher] + +index_on_404 = true + +watch_path = ["src"] + +[application.plugins] + +available = true + +required = [] diff --git a/examples/openid_connect_demo/README.md b/examples/openid_connect_demo/README.md new file mode 100644 index 0000000000..7b0d100fd9 --- /dev/null +++ b/examples/openid_connect_demo/README.md @@ -0,0 +1,12 @@ +# OpenID Connect example to show how to authenticate an user + +The environment variables in [`.cargo/config.toml`](./.cargo/config.toml) must be set in order for this example to work. + +Once they are set, you can run `dx serve --platform web` or `dx serve --platform desktop`. + +### Environment variables summary + +- `DIOXUS_FRONT_ISSUER_URL`: The openid-connect's issuer url +- `DIOXUS_FRONT_CLIENT_ID`: The openid-connect's client id +- `DIOXUS_FRONT_CLIENT_SECRET`: The openid-connect's client secret +- `DIOXUS_FRONT_URL`: The url the frontend is supposed to be running on, it could be for example `http://localhost:8080` diff --git a/examples/openid_connect_demo/src/constants.rs b/examples/openid_connect_demo/src/constants.rs new file mode 100644 index 0000000000..0a0b4950a6 --- /dev/null +++ b/examples/openid_connect_demo/src/constants.rs @@ -0,0 +1,2 @@ +pub const DIOXUS_FRONT_AUTH_TOKEN: &str = "auth_token"; +pub const DIOXUS_FRONT_AUTH_REQUEST: &str = "auth_request"; diff --git a/examples/openid_connect_demo/src/main.rs b/examples/openid_connect_demo/src/main.rs new file mode 100644 index 0000000000..8487758595 --- /dev/null +++ b/examples/openid_connect_demo/src/main.rs @@ -0,0 +1,36 @@ +#![allow(non_snake_case)] +use dioxus::prelude::*; +use dioxus_logger::tracing::Level; +use router::Route; + +use crate::oidc::ClientState; +use crate::storage::{use_auth_request_provider, use_auth_token_provider}; + +pub(crate) mod constants; +pub(crate) mod model; +pub(crate) mod oidc; +pub(crate) mod props; +pub(crate) mod router; +pub(crate) mod storage; +pub(crate) mod views; + +pub static CLIENT: GlobalSignal = Signal::global(ClientState::default); + +pub static DIOXUS_FRONT_ISSUER_URL: &str = env!("DIOXUS_FRONT_ISSUER_URL"); +pub static DIOXUS_FRONT_CLIENT_ID: &str = env!("DIOXUS_FRONT_CLIENT_ID"); +pub static DIOXUS_FRONT_CLIENT_SECRET: &str = env!("DIOXUS_FRONT_CLIENT_SECRET"); +pub static DIOXUS_FRONT_URL: &str = env!("DIOXUS_FRONT_URL"); + +fn App() -> Element { + use_auth_request_provider(); + use_auth_token_provider(); + rsx! { Router:: {} } +} + +fn main() { + dioxus_logger::init(Level::DEBUG).expect("failed to init logger"); + dioxus_sdk::set_dir!(); + console_error_panic_hook::set_once(); + log::info!("starting app"); + launch(App); +} diff --git a/examples/openid_connect_demo/src/model/mod.rs b/examples/openid_connect_demo/src/model/mod.rs new file mode 100644 index 0000000000..68a79f5c0c --- /dev/null +++ b/examples/openid_connect_demo/src/model/mod.rs @@ -0,0 +1 @@ +pub(crate) mod user; diff --git a/examples/openid_connect_demo/src/model/user.rs b/examples/openid_connect_demo/src/model/user.rs new file mode 100644 index 0000000000..b1005fdd24 --- /dev/null +++ b/examples/openid_connect_demo/src/model/user.rs @@ -0,0 +1,7 @@ +use uuid::Uuid; + +#[derive(PartialEq)] +pub struct User { + pub id: Uuid, + pub name: String, +} diff --git a/examples/openid_connect_demo/src/oidc.rs b/examples/openid_connect_demo/src/oidc.rs new file mode 100644 index 0000000000..2c20c4e49b --- /dev/null +++ b/examples/openid_connect_demo/src/oidc.rs @@ -0,0 +1,127 @@ +use anyhow::Result; +use openidconnect::{ + core::{CoreClient, CoreIdToken, CoreResponseType, CoreTokenResponse}, + reqwest::async_http_client, + url::Url, + AuthenticationFlow, AuthorizationCode, ClaimsVerificationError, ClientId, ClientSecret, + CsrfToken, IssuerUrl, LogoutRequest, Nonce, ProviderMetadataWithLogout, RedirectUrl, + RefreshToken, +}; +use serde::{Deserialize, Serialize}; + +use crate::{props::client::ClientProps, DIOXUS_FRONT_CLIENT_ID}; + +#[derive(Clone, Debug, Default)] +pub struct ClientState { + pub oidc_client: Option, +} + +/// State that holds the nonce and authorization url and the nonce generated to log in an user +#[derive(Clone, PartialEq, Deserialize, Serialize, Default)] +pub struct AuthRequestState { + pub auth_request: Option, +} + +#[derive(Clone, PartialEq, Deserialize, Serialize)] +pub struct AuthRequest { + pub nonce: Nonce, + pub authorize_url: String, +} + +/// State the tokens returned once the user is authenticated +#[derive(Debug, Deserialize, Serialize, Default, Clone)] +pub struct AuthTokenState { + /// Token used to identify the user + pub id_token: Option, + /// Token used to refresh the tokens if they expire + pub refresh_token: Option, +} + +impl PartialEq for AuthTokenState { + fn eq(&self, other: &Self) -> bool { + self.id_token == other.id_token + && self.refresh_token.as_ref().map(|t| t.secret().clone()) + == other.refresh_token.as_ref().map(|t| t.secret().clone()) + } +} + +pub fn email( + client: CoreClient, + id_token: CoreIdToken, + nonce: Nonce, +) -> Result { + match id_token.claims(&client.id_token_verifier(), &nonce) { + Ok(claims) => Ok(claims.clone().email().unwrap().to_string()), + Err(error) => Err(error), + } +} + +pub fn authorize_url(client: CoreClient) -> AuthRequest { + let (authorize_url, _csrf_state, nonce) = client + .authorize_url( + AuthenticationFlow::::AuthorizationCode, + CsrfToken::new_random, + Nonce::new_random, + ) + .add_scope(openidconnect::Scope::new("email".to_string())) + .add_scope(openidconnect::Scope::new("profile".to_string())) + .url(); + AuthRequest { + authorize_url: authorize_url.to_string(), + nonce, + } +} + +pub async fn init_provider_metadata() -> Result { + let issuer_url = IssuerUrl::new(crate::DIOXUS_FRONT_ISSUER_URL.to_string())?; + Ok(ProviderMetadataWithLogout::discover_async(issuer_url, async_http_client).await?) +} + +pub async fn init_oidc_client() -> Result<(ClientId, CoreClient)> { + let client_id = ClientId::new(crate::DIOXUS_FRONT_CLIENT_ID.to_string()); + let provider_metadata = init_provider_metadata().await?; + let client_secret = Some(ClientSecret::new( + crate::DIOXUS_FRONT_CLIENT_SECRET.to_string(), + )); + let redirect_url = RedirectUrl::new(format!("{}/login", crate::DIOXUS_FRONT_URL))?; + + Ok(( + client_id.clone(), + CoreClient::from_provider_metadata(provider_metadata, client_id, client_secret) + .set_redirect_uri(redirect_url), + )) +} + +///TODO: Add pkce_pacifier +pub async fn token_response(oidc_client: CoreClient, code: String) -> Result { + // let (pkce_challenge, pkce_verifier) = PkceCodeChallenge::new_random_sha256(); + Ok(oidc_client + .exchange_code(AuthorizationCode::new(code.clone())) + // .set_pkce_verifier(pkce_verifier) + .request_async(async_http_client) + .await?) +} + +pub async fn exchange_refresh_token( + oidc_client: CoreClient, + refresh_token: RefreshToken, +) -> Result { + Ok(oidc_client + .exchange_refresh_token(&refresh_token) + .request_async(async_http_client) + .await?) +} + +pub async fn log_out_url(id_token_hint: CoreIdToken) -> Result { + let provider_metadata = init_provider_metadata().await?; + let end_session_url = provider_metadata + .additional_metadata() + .clone() + .end_session_endpoint + .unwrap(); + let logout_request: LogoutRequest = LogoutRequest::from(end_session_url); + Ok(logout_request + .set_client_id(ClientId::new(DIOXUS_FRONT_CLIENT_ID.to_string())) + .set_id_token_hint(&id_token_hint) + .http_get_url()) +} diff --git a/examples/openid_connect_demo/src/props/client.rs b/examples/openid_connect_demo/src/props/client.rs new file mode 100644 index 0000000000..2e02111e16 --- /dev/null +++ b/examples/openid_connect_demo/src/props/client.rs @@ -0,0 +1,20 @@ +use dioxus::prelude::*; +use openidconnect::{core::CoreClient, ClientId}; + +#[derive(Props, Clone, Debug)] +pub struct ClientProps { + pub client: CoreClient, + pub client_id: ClientId, +} + +impl PartialEq for ClientProps { + fn eq(&self, other: &Self) -> bool { + self.client_id == other.client_id + } +} + +impl ClientProps { + pub fn new(client_id: ClientId, client: CoreClient) -> Self { + ClientProps { client_id, client } + } +} diff --git a/examples/openid_connect_demo/src/props/mod.rs b/examples/openid_connect_demo/src/props/mod.rs new file mode 100644 index 0000000000..1d33131531 --- /dev/null +++ b/examples/openid_connect_demo/src/props/mod.rs @@ -0,0 +1 @@ +pub(crate) mod client; diff --git a/examples/openid_connect_demo/src/router.rs b/examples/openid_connect_demo/src/router.rs new file mode 100644 index 0000000000..8f4b1b36a6 --- /dev/null +++ b/examples/openid_connect_demo/src/router.rs @@ -0,0 +1,16 @@ +use crate::views::{header::AuthHeader, home::Home, login::Login, not_found::NotFound}; +use dioxus::prelude::*; + +#[derive(Routable, Clone)] +pub enum Route { + #[layout(AuthHeader)] + #[route("/")] + Home {}, + + // https://dioxuslabs.com/learn/0.4/router/reference/routes#query-segments + #[route("/login?:query_string")] + Login { query_string: String }, + #[end_layout] + #[route("/:..route")] + NotFound { route: Vec }, +} diff --git a/examples/openid_connect_demo/src/storage.rs b/examples/openid_connect_demo/src/storage.rs new file mode 100644 index 0000000000..c34819b965 --- /dev/null +++ b/examples/openid_connect_demo/src/storage.rs @@ -0,0 +1,35 @@ +use dioxus::prelude::*; +use dioxus_sdk::storage::*; + +use crate::{ + constants::{DIOXUS_FRONT_AUTH_REQUEST, DIOXUS_FRONT_AUTH_TOKEN}, + oidc::{AuthRequestState, AuthTokenState}, +}; + +pub fn use_auth_token_provider() { + let stored_token = + use_storage::(DIOXUS_FRONT_AUTH_TOKEN.to_owned(), AuthTokenState::default); + + use_context_provider(move || stored_token); +} + +pub fn use_auth_token() -> Signal { + use_context() +} + +pub fn use_auth_request_provider() { + let stored_req = use_storage::( + DIOXUS_FRONT_AUTH_REQUEST.to_owned(), + AuthRequestState::default, + ); + + use_context_provider(move || stored_req); +} + +pub fn use_auth_request() -> Signal { + use_context() +} + +pub fn auth_request() -> Signal { + consume_context() +} diff --git a/examples/openid_connect_demo/src/views/header.rs b/examples/openid_connect_demo/src/views/header.rs new file mode 100644 index 0000000000..274a0d22f0 --- /dev/null +++ b/examples/openid_connect_demo/src/views/header.rs @@ -0,0 +1,221 @@ +use crate::storage::{auth_request, use_auth_request, use_auth_token}; +use crate::{ + oidc::{ + authorize_url, email, exchange_refresh_token, init_oidc_client, log_out_url, + AuthRequestState, AuthTokenState, ClientState, + }, + props::client::ClientProps, + router::Route, + CLIENT, +}; +use anyhow::Result; +use dioxus::prelude::*; +use dioxus::router::prelude::{Link, Outlet}; +use openidconnect::{url::Url, OAuth2TokenResponse, TokenResponse}; + +#[component] +pub fn LogOut() -> Element { + let mut auth_token = use_auth_token(); + let log_out_url_state = use_signal(|| None::>>); + match auth_token().id_token { + Some(id_token) => match &*log_out_url_state.read() { + Some(log_out_url_result) => match log_out_url_result { + Some(uri) => match uri { + Ok(uri) => { + rsx! { + Link { + onclick: move |_| { + auth_token.take(); + }, + to: uri.to_string(), + "Log out" + } + } + } + Err(error) => { + rsx! { div { "Failed to load disconnection url: {error:?}" } } + } + }, + None => { + rsx! { div { "Loading... Please wait" } } + } + }, + None => { + let logout_url_task = move || { + spawn({ + let mut log_out_url_state = log_out_url_state.to_owned(); + async move { + let logout_url = log_out_url(id_token).await; + let logout_url_option = Some(logout_url); + log_out_url_state.set(Some(logout_url_option)); + } + }) + }; + logout_url_task(); + rsx! { div { "Loading log out url... Please wait" } } + } + }, + None => { + rsx! {{}} + } + } +} + +#[component] +pub fn RefreshToken(props: ClientProps) -> Element { + let mut auth_token = use_auth_token(); + match auth_token().refresh_token { + Some(refresh_token) => { + rsx! { div { + onmounted: { + move |_| { + let client = props.client.clone(); + let refresh_token = refresh_token.clone(); + async move { + let exchange_refresh_token = + exchange_refresh_token(client, refresh_token).await; + match exchange_refresh_token { + Ok(response_token) => { + auth_token.set(AuthTokenState { + id_token: response_token.id_token().cloned(), + refresh_token: response_token.refresh_token().cloned(), + }); + } + Err(_error) => { + auth_token.take(); + auth_request().take(); + } + } + } + } + }, + "Refreshing session, please wait" + } } + } + None => { + rsx! { div { "Id token expired and no refresh token found" } } + } + } +} + +#[component] +pub fn LoadClient() -> Element { + let init_client_future = use_resource(move || async move { init_oidc_client().await }); + match &*init_client_future.read_unchecked() { + Some(Ok((client_id, client))) => rsx! { + div { + onmounted: { + let client_id = client_id.clone(); + let client = client.clone(); + move |_| { + *CLIENT.write() = ClientState { + oidc_client: Some(ClientProps::new(client_id.clone(), client.clone())), + }; + } + }, + "Client successfully loaded" + } + Outlet:: {} + }, + Some(Err(error)) => { + log::info! {"Failed to load client: {:?}", error}; + rsx! { + div { "Failed to load client: {error:?}" } + Outlet:: {} + } + } + None => { + rsx! { + div { + div { "Loading client, please wait" } + Outlet:: {} + } + } + } + } +} + +#[component] +pub fn AuthHeader() -> Element { + let client = CLIENT.read().oidc_client.clone(); + let mut auth_request = use_auth_request(); + let auth_token = use_auth_token(); + match (client, auth_request(), auth_token()) { + // We have everything we need to attempt to authenticate the user + (Some(client_props), current_auth_request, current_auth_token) => { + match current_auth_request.auth_request { + Some(new_auth_request) => { + match current_auth_token.id_token { + Some(id_token) => { + match email( + client_props.client.clone(), + id_token.clone(), + new_auth_request.nonce.clone(), + ) { + Ok(email) => { + rsx! { + div { + div { {email} } + LogOut {} + Outlet:: {} + } + } + } + // Id token failed to be decoded + Err(error) => match error { + // Id token failed to be decoded because it expired, we refresh it + openidconnect::ClaimsVerificationError::Expired(_message) => { + log::info!("Token expired"); + rsx! { + div { + RefreshToken { client_id: client_props.client_id, client: client_props.client } + Outlet:: {} + } + } + } + // Other issue with token decoding + _ => { + log::info!("Other issue with token"); + rsx! { + div { + div { "{error}" } + Outlet:: {} + } + } + } + }, + } + } + // User is not logged in + None => { + rsx! { + div { + Link { to: new_auth_request.authorize_url.clone(), "Log in" } + Outlet:: {} + } + } + } + } + } + None => { + rsx! { div { + onmounted: { + let client = client_props.client; + move |_| { + let new_auth_request = authorize_url(client.clone()); + auth_request.set(AuthRequestState { + auth_request: Some(new_auth_request), + }); + } + }, + "Loading nonce" + } } + } + } + } + // Client is not initialized yet, we need it for everything + (None, _, _) => { + rsx! { LoadClient {} } + } + } +} diff --git a/examples/openid_connect_demo/src/views/home.rs b/examples/openid_connect_demo/src/views/home.rs new file mode 100644 index 0000000000..d91b5c2037 --- /dev/null +++ b/examples/openid_connect_demo/src/views/home.rs @@ -0,0 +1,5 @@ +use dioxus::prelude::*; + +pub fn Home() -> Element { + rsx! { div { "Hello world" } } +} diff --git a/examples/openid_connect_demo/src/views/login.rs b/examples/openid_connect_demo/src/views/login.rs new file mode 100644 index 0000000000..e3a4e0025b --- /dev/null +++ b/examples/openid_connect_demo/src/views/login.rs @@ -0,0 +1,86 @@ +use crate::{ + oidc::{token_response, AuthTokenState}, + router::Route, + storage::{auth_request, use_auth_token}, + CLIENT, +}; +use dioxus::prelude::*; +use dioxus::router::prelude::Link; +use openidconnect::{OAuth2TokenResponse, TokenResponse}; + +#[component] +pub fn Login(query_string: String) -> Element { + let client = CLIENT.read().oidc_client.clone(); + let mut auth_token = use_auth_token(); + let current_auth_token = auth_token(); + match client { + Some(client_props) => { + match ( + current_auth_token.id_token, + current_auth_token.refresh_token, + ) { + (Some(_id_token), Some(_refresh_token)) => { + rsx! { + div { "Sign in successful" } + Link { to: Route::Home {}, "Go back home" } + } + } + // If the refresh token is set but not the id_token, there was an error, we just go back home and reset their value + (None, Some(_)) | (Some(_), None) => { + rsx! { + div { "Error while attempting to log in" } + Link { + to: Route::Home {}, + onclick: move |_| { + auth_token.take(); + auth_request().take(); + }, + "Go back home" + } + } + } + (None, None) => { + let mut query_pairs = form_urlencoded::parse(query_string.as_bytes()); + let code_pair = query_pairs.find(|(key, _value)| key == "code"); + match code_pair { + Some((_key, code)) => { + let code = code.to_string(); + rsx! { div { + onmounted: { + move |_| { + let auth_code = code.to_string(); + let client_props = client_props.clone(); + async move { + let token_response_result = + token_response(client_props.client, auth_code).await; + match token_response_result { + Ok(token_response) => { + let id_token = token_response.id_token().unwrap(); + auth_token.set(AuthTokenState { + id_token: Some(id_token.clone()), + refresh_token: token_response + .refresh_token() + .cloned(), + }); + } + Err(error) => { + log::warn! {"{error}"}; + } + } + } + } + } + }} + } + None => { + rsx! { div { "No code provided" } } + } + } + } + } + } + _ => { + rsx! {{}} + } + } +} diff --git a/examples/openid_connect_demo/src/views/mod.rs b/examples/openid_connect_demo/src/views/mod.rs new file mode 100644 index 0000000000..1b8e08a585 --- /dev/null +++ b/examples/openid_connect_demo/src/views/mod.rs @@ -0,0 +1,4 @@ +pub(crate) mod header; +pub(crate) mod home; +pub(crate) mod login; +pub(crate) mod not_found; diff --git a/examples/openid_connect_demo/src/views/not_found.rs b/examples/openid_connect_demo/src/views/not_found.rs new file mode 100644 index 0000000000..833c610674 --- /dev/null +++ b/examples/openid_connect_demo/src/views/not_found.rs @@ -0,0 +1,10 @@ +use dioxus::prelude::*; + +#[component] +pub fn NotFound(route: Vec) -> Element { + rsx! { + div{ + {route.join("")} + } + } +} diff --git a/examples/optional_props.rs b/examples/optional_props.rs index 334852bea1..ad54ed33cd 100644 --- a/examples/optional_props.rs +++ b/examples/optional_props.rs @@ -6,7 +6,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/overlay.rs b/examples/overlay.rs index 59a9619d6a..e4e2e80592 100644 --- a/examples/overlay.rs +++ b/examples/overlay.rs @@ -11,9 +11,7 @@ use dioxus::desktop::{ use dioxus::prelude::*; fn main() { - dioxus::launch::builder() - .with_cfg(make_config()) - .launch(app); + LaunchBuilder::desktop().with_cfg(make_config()).launch(app); } fn app() -> Element { @@ -22,9 +20,9 @@ fn app() -> Element { _ = use_global_shortcut("cmd+g", move || show_overlay.toggle()); rsx! { - document::Link { + head::Link { rel: "stylesheet", - href: asset!("/examples/assets/overlay.css"), + href: asset!("./examples/assets/overlay.css"), } if show_overlay() { div { diff --git a/examples/popup.rs b/examples/popup.rs index e708b9cd9f..131d536edb 100644 --- a/examples/popup.rs +++ b/examples/popup.rs @@ -5,7 +5,7 @@ use dioxus::prelude::*; use std::rc::Rc; fn main() { - dioxus::launch(app); + launch_desktop(app); } fn app() -> Element { diff --git a/examples/query_segment_search.rs b/examples/query_segment_search.rs index 4366cc0118..018f475e45 100644 --- a/examples/query_segment_search.rs +++ b/examples/query_segment_search.rs @@ -2,7 +2,7 @@ //! //! The enum router makes it easy to use your route as state in your app. This example shows how to use the router to encode search text into the url and decode it back into a string. //! -//! Run this example on desktop with +//! Run this example on desktop with //! ```sh //! dx serve --example query_segment_search //! ``` @@ -14,7 +14,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(|| { + launch(|| { rsx! { Router:: {} } @@ -29,7 +29,7 @@ enum Route { // The each query segment must implement and Display. // You can use multiple query segments separated by `&`s. - #[route("/search?:query&:word_count")] + #[route("/search?:query&:word_count")] Search { query: String, word_count: usize, diff --git a/examples/read_size.rs b/examples/read_size.rs index dbde9353c7..1965597cfa 100644 --- a/examples/read_size.rs +++ b/examples/read_size.rs @@ -9,7 +9,7 @@ use std::rc::Rc; use dioxus::{html::geometry::euclid::Rect, prelude::*}; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { @@ -28,7 +28,7 @@ fn app() -> Element { }; rsx!( - document::Stylesheet { href: asset!("/examples/assets/read_size.css") } + head::Link { rel: "stylesheet", href: asset!("./examples/assets/read_size.css") } div { width: "50%", height: "50%", diff --git a/examples/readme.rs b/examples/readme.rs index 1c36c65c00..8f412a749d 100644 --- a/examples/readme.rs +++ b/examples/readme.rs @@ -8,7 +8,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/reducer.rs b/examples/reducer.rs index 32a15368e8..7c434e5858 100644 --- a/examples/reducer.rs +++ b/examples/reducer.rs @@ -7,17 +7,17 @@ use dioxus::prelude::*; -const STYLE: Asset = asset!("/examples/assets/radio.css"); +const STYLE: &str = asset!("./examples/assets/radio.css"); fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { let mut state = use_signal(|| PlayerState { is_playing: false }); rsx!( - document::Stylesheet { href: STYLE } + head::Link { rel: "stylesheet", href: STYLE } h1 {"Select an option"} // Add some cute animations if the radio is playing! diff --git a/examples/resize.rs b/examples/resize.rs index 52b55bfb8c..eace774a0b 100644 --- a/examples/resize.rs +++ b/examples/resize.rs @@ -8,14 +8,14 @@ use dioxus::prelude::*; use dioxus_elements::geometry::euclid::Size2D; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { let mut dimensions = use_signal(Size2D::zero); rsx!( - document::Stylesheet { href: asset!("/examples/assets/read_size.css") } + head::Link { rel: "stylesheet", href: asset!("./examples/assets/read_size.css") } div { width: "50%", height: "50%", diff --git a/examples/router.rs b/examples/router.rs index 377c595418..13197f57e2 100644 --- a/examples/router.rs +++ b/examples/router.rs @@ -8,12 +8,12 @@ use dioxus::prelude::*; -const STYLE: Asset = asset!("/examples/assets/router.css"); +const STYLE: &str = asset!("./examples/assets/router.css"); fn main() { - dioxus::launch(|| { + launch(|| { rsx! { - document::Stylesheet { href: STYLE } + head::Link { rel: "stylesheet", href: STYLE } Router:: {} } }); diff --git a/examples/router_resource.rs b/examples/router_resource.rs index 6898585f3b..de69f0e40b 100644 --- a/examples/router_resource.rs +++ b/examples/router_resource.rs @@ -15,7 +15,7 @@ enum Route { } fn main() { - dioxus::launch(App); + launch(App); } #[component] diff --git a/examples/rsx_usage.rs b/examples/rsx_usage.rs index b8ae72fd68..3bffe8c700 100644 --- a/examples/rsx_usage.rs +++ b/examples/rsx_usage.rs @@ -39,7 +39,7 @@ //! - Allow top-level fragments fn main() { - dioxus::launch(app) + launch(app) } use core::{fmt, str::FromStr}; @@ -228,9 +228,6 @@ fn app() -> Element { // Or we can shell out to a helper function {format_dollars(10, 50)} - - // some text? - {Some("hello world!")} } } } diff --git a/examples/scroll_to_top.rs b/examples/scroll_to_top.rs index 1cd91e4947..9f4f85955e 100644 --- a/examples/scroll_to_top.rs +++ b/examples/scroll_to_top.rs @@ -8,7 +8,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/shortcut.rs b/examples/shortcut.rs index a6626c27b7..d0192b9782 100644 --- a/examples/shortcut.rs +++ b/examples/shortcut.rs @@ -9,7 +9,7 @@ use dioxus::desktop::use_global_shortcut; use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch_desktop(app); } fn app() -> Element { diff --git a/examples/shorthand.rs b/examples/shorthand.rs index bd2047a8e6..33905d5710 100644 --- a/examples/shorthand.rs +++ b/examples/shorthand.rs @@ -3,7 +3,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/signals.rs b/examples/signals.rs index 6b881587bc..26db9685c1 100644 --- a/examples/signals.rs +++ b/examples/signals.rs @@ -10,7 +10,7 @@ use async_std::task::sleep; use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/simple_list.rs b/examples/simple_list.rs index 32e1d3eb85..3c1b9d71a1 100644 --- a/examples/simple_list.rs +++ b/examples/simple_list.rs @@ -5,7 +5,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/simple_router.rs b/examples/simple_router.rs index 0ec22a001e..e674f2f30e 100644 --- a/examples/simple_router.rs +++ b/examples/simple_router.rs @@ -5,7 +5,7 @@ use dioxus::prelude::*; fn main() { // Launch the router, using our `Route` component as the generic type // This will automatically boot the app to "/" unless otherwise specified - dioxus::launch(|| rsx! { Router:: {} }); + launch(|| rsx! { Router:: {} }); } /// By default, the Routable derive will use the name of the variant as the route diff --git a/examples/streams.rs b/examples/streams.rs index a71b5b9c18..6155cb35f9 100644 --- a/examples/streams.rs +++ b/examples/streams.rs @@ -5,7 +5,7 @@ use dioxus::prelude::*; use futures_util::{future, stream, Stream, StreamExt}; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/suspense.rs b/examples/suspense.rs index 14cd2573e9..014bb3276a 100644 --- a/examples/suspense.rs +++ b/examples/suspense.rs @@ -15,7 +15,7 @@ use dioxus::desktop::{Config, LogicalSize, WindowBuilder}; use dioxus::prelude::*; fn main() { - dioxus::builder() + LaunchBuilder::new() .with_cfg(desktop! { Config::new().with_window( WindowBuilder::new() diff --git a/examples/svg.rs b/examples/svg.rs index b462f30288..d4c23d1ddb 100644 --- a/examples/svg.rs +++ b/examples/svg.rs @@ -10,7 +10,7 @@ use dioxus::prelude::*; use rand::{thread_rng, Rng}; fn main() { - dioxus::launch(|| { + launch(|| { rsx! { div { user_select: "none", webkit_user_select: "none", margin_left: "10%", margin_right: "10%", h1 { "Click die to generate a new value" } diff --git a/examples/tailwind/.gitignore b/examples/tailwind/.gitignore new file mode 100644 index 0000000000..1521c8b765 --- /dev/null +++ b/examples/tailwind/.gitignore @@ -0,0 +1 @@ +dist diff --git a/examples/tailwind/Cargo.toml b/examples/tailwind/Cargo.toml new file mode 100644 index 0000000000..6660b87397 --- /dev/null +++ b/examples/tailwind/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "dioxus-tailwind" +version = "0.0.0" +authors = [] +edition = "2021" +description = "A tailwindcss example using Dioxus" +license = "MIT OR Apache-2.0" +repository = "https://github.com/DioxusLabs/dioxus/" +homepage = "https://dioxuslabs.com" +documentation = "https://dioxuslabs.com" +publish = false + +[dependencies] +manganis = { workspace = true } + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +dioxus = { path = "../../packages/dioxus", features = ["desktop"] } + +[target.'cfg(target_arch = "wasm32")'.dependencies] +dioxus = { path = "../../packages/dioxus", features = ["web"] } diff --git a/examples/tailwind/Dioxus.toml b/examples/tailwind/Dioxus.toml new file mode 100644 index 0000000000..af7fcf3b8f --- /dev/null +++ b/examples/tailwind/Dioxus.toml @@ -0,0 +1,27 @@ +[application] + +# App (Project) Name +name = "Tailwind CSS + Dioxus" + +# Dioxus App Default Platform +# desktop, web, mobile, ssr +default_platform = "web" + +# `build` & `serve` dist path +out_dir = "dist" + +# resource (public) file folder +asset_dir = "public" + +[web.app] + +# HTML title tag content +title = "dioxus | ⛺" + +[web.watcher] + +# when watcher trigger, regenerate the `index.html` +reload_html = true + +# which files or dirs will be watcher monitoring +watch_path = ["src", "public"] diff --git a/examples/tailwind/README.md b/examples/tailwind/README.md new file mode 100644 index 0000000000..6dca4b775e --- /dev/null +++ b/examples/tailwind/README.md @@ -0,0 +1,7 @@ +Example: Basic Tailwind usage + +This example shows how an app might be styled with TailwindCSS. + +## Running + +Our [Tailwind](https://dioxuslabs.com/learn/0.5/cookbook/tailwind) guide explains how to setup and run Dioxus-Tailwind projects. \ No newline at end of file diff --git a/examples/tailwind/input.css b/examples/tailwind/input.css new file mode 100644 index 0000000000..bd6213e1df --- /dev/null +++ b/examples/tailwind/input.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; \ No newline at end of file diff --git a/examples/tailwind/public/tailwind.css b/examples/tailwind/public/tailwind.css new file mode 100644 index 0000000000..65b95316e1 --- /dev/null +++ b/examples/tailwind/public/tailwind.css @@ -0,0 +1,833 @@ +/* +! tailwindcss v3.2.7 | MIT License | https://tailwindcss.com +*/ + +/* +1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) +2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) +*/ + +*, +::before, +::after { + box-sizing: border-box; + /* 1 */ + border-width: 0; + /* 2 */ + border-style: solid; + /* 2 */ + border-color: #e5e7eb; + /* 2 */ +} + +::before, +::after { + --tw-content: ''; +} + +/* +1. Use a consistent sensible line-height in all browsers. +2. Prevent adjustments of font size after orientation changes in iOS. +3. Use a more readable tab size. +4. Use the user's configured `sans` font-family by default. +5. Use the user's configured `sans` font-feature-settings by default. +*/ + +html { + line-height: 1.5; + /* 1 */ + -webkit-text-size-adjust: 100%; + /* 2 */ + -moz-tab-size: 4; + /* 3 */ + -o-tab-size: 4; + tab-size: 4; + /* 3 */ + font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + /* 4 */ + font-feature-settings: normal; + /* 5 */ +} + +/* +1. Remove the margin in all browsers. +2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. +*/ + +body { + margin: 0; + /* 1 */ + line-height: inherit; + /* 2 */ +} + +/* +1. Add the correct height in Firefox. +2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) +3. Ensure horizontal rules are visible by default. +*/ + +hr { + height: 0; + /* 1 */ + color: inherit; + /* 2 */ + border-top-width: 1px; + /* 3 */ +} + +/* +Add the correct text decoration in Chrome, Edge, and Safari. +*/ + +abbr:where([title]) { + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; +} + +/* +Remove the default font size and weight for headings. +*/ + +h1, +h2, +h3, +h4, +h5, +h6 { + font-size: inherit; + font-weight: inherit; +} + +/* +Reset links to optimize for opt-in styling instead of opt-out. +*/ + +a { + color: inherit; + text-decoration: inherit; +} + +/* +Add the correct font weight in Edge and Safari. +*/ + +b, +strong { + font-weight: bolder; +} + +/* +1. Use the user's configured `mono` font family by default. +2. Correct the odd `em` font sizing in all browsers. +*/ + +code, +kbd, +samp, +pre { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + /* 1 */ + font-size: 1em; + /* 2 */ +} + +/* +Add the correct font size in all browsers. +*/ + +small { + font-size: 80%; +} + +/* +Prevent `sub` and `sup` elements from affecting the line height in all browsers. +*/ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* +1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) +2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) +3. Remove gaps between table borders by default. +*/ + +table { + text-indent: 0; + /* 1 */ + border-color: inherit; + /* 2 */ + border-collapse: collapse; + /* 3 */ +} + +/* +1. Change the font styles in all browsers. +2. Remove the margin in Firefox and Safari. +3. Remove default padding in all browsers. +*/ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; + /* 1 */ + font-size: 100%; + /* 1 */ + font-weight: inherit; + /* 1 */ + line-height: inherit; + /* 1 */ + color: inherit; + /* 1 */ + margin: 0; + /* 2 */ + padding: 0; + /* 3 */ +} + +/* +Remove the inheritance of text transform in Edge and Firefox. +*/ + +button, +select { + text-transform: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Remove default button styles. +*/ + +button, +[type='button'], +[type='reset'], +[type='submit'] { + -webkit-appearance: button; + /* 1 */ + background-color: transparent; + /* 2 */ + background-image: none; + /* 2 */ +} + +/* +Use the modern Firefox focus style for all focusable elements. +*/ + +:-moz-focusring { + outline: auto; +} + +/* +Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) +*/ + +:-moz-ui-invalid { + box-shadow: none; +} + +/* +Add the correct vertical alignment in Chrome and Firefox. +*/ + +progress { + vertical-align: baseline; +} + +/* +Correct the cursor style of increment and decrement buttons in Safari. +*/ + +::-webkit-inner-spin-button, +::-webkit-outer-spin-button { + height: auto; +} + +/* +1. Correct the odd appearance in Chrome and Safari. +2. Correct the outline style in Safari. +*/ + +[type='search'] { + -webkit-appearance: textfield; + /* 1 */ + outline-offset: -2px; + /* 2 */ +} + +/* +Remove the inner padding in Chrome and Safari on macOS. +*/ + +::-webkit-search-decoration { + -webkit-appearance: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Change font properties to `inherit` in Safari. +*/ + +::-webkit-file-upload-button { + -webkit-appearance: button; + /* 1 */ + font: inherit; + /* 2 */ +} + +/* +Add the correct display in Chrome and Safari. +*/ + +summary { + display: list-item; +} + +/* +Removes the default spacing and border for appropriate elements. +*/ + +blockquote, +dl, +dd, +h1, +h2, +h3, +h4, +h5, +h6, +hr, +figure, +p, +pre { + margin: 0; +} + +fieldset { + margin: 0; + padding: 0; +} + +legend { + padding: 0; +} + +ol, +ul, +menu { + list-style: none; + margin: 0; + padding: 0; +} + +/* +Prevent resizing textareas horizontally by default. +*/ + +textarea { + resize: vertical; +} + +/* +1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) +2. Set the default placeholder color to the user's configured gray 400 color. +*/ + +input::-moz-placeholder, textarea::-moz-placeholder { + opacity: 1; + /* 1 */ + color: #9ca3af; + /* 2 */ +} + +input::placeholder, +textarea::placeholder { + opacity: 1; + /* 1 */ + color: #9ca3af; + /* 2 */ +} + +/* +Set the default cursor for buttons. +*/ + +button, +[role="button"] { + cursor: pointer; +} + +/* +Make sure disabled buttons don't get the pointer cursor. +*/ + +:disabled { + cursor: default; +} + +/* +1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) +2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) + This can trigger a poorly considered lint error in some tools but is included by design. +*/ + +img, +svg, +video, +canvas, +audio, +iframe, +embed, +object { + display: block; + /* 1 */ + vertical-align: middle; + /* 2 */ +} + +/* +Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) +*/ + +img, +video { + max-width: 100%; + height: auto; +} + +/* Make elements with the HTML hidden attribute stay hidden by default */ + +[hidden] { + display: none; +} + +*, ::before, ::after { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; +} + +::backdrop { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; +} + +.container { + width: 100%; +} + +@media (min-width: 640px) { + .container { + max-width: 640px; + } +} + +@media (min-width: 768px) { + .container { + max-width: 768px; + } +} + +@media (min-width: 1024px) { + .container { + max-width: 1024px; + } +} + +@media (min-width: 1280px) { + .container { + max-width: 1280px; + } +} + +@media (min-width: 1536px) { + .container { + max-width: 1536px; + } +} + +.mx-auto { + margin-left: auto; + margin-right: auto; +} + +.mb-16 { + margin-bottom: 4rem; +} + +.mb-4 { + margin-bottom: 1rem; +} + +.mb-8 { + margin-bottom: 2rem; +} + +.ml-1 { + margin-left: 0.25rem; +} + +.ml-3 { + margin-left: 0.75rem; +} + +.ml-4 { + margin-left: 1rem; +} + +.mr-5 { + margin-right: 1.25rem; +} + +.mt-4 { + margin-top: 1rem; +} + +.flex { + display: flex; +} + +.inline-flex { + display: inline-flex; +} + +.hidden { + display: none; +} + +.h-10 { + height: 2.5rem; +} + +.h-4 { + height: 1rem; +} + +.w-10 { + width: 2.5rem; +} + +.w-4 { + width: 1rem; +} + +.w-5\/6 { + width: 83.333333%; +} + +.flex-col { + flex-direction: column; +} + +.flex-wrap { + flex-wrap: wrap; +} + +.items-center { + align-items: center; +} + +.justify-center { + justify-content: center; +} + +.rounded { + border-radius: 0.25rem; +} + +.rounded-full { + border-radius: 9999px; +} + +.border-0 { + border-width: 0px; +} + +.bg-gray-800 { + --tw-bg-opacity: 1; + background-color: rgb(31 41 55 / var(--tw-bg-opacity)); +} + +.bg-gray-900 { + --tw-bg-opacity: 1; + background-color: rgb(17 24 39 / var(--tw-bg-opacity)); +} + +.bg-indigo-500 { + --tw-bg-opacity: 1; + background-color: rgb(99 102 241 / var(--tw-bg-opacity)); +} + +.object-cover { + -o-object-fit: cover; + object-fit: cover; +} + +.object-center { + -o-object-position: center; + object-position: center; +} + +.p-2 { + padding: 0.5rem; +} + +.p-5 { + padding: 1.25rem; +} + +.px-3 { + padding-left: 0.75rem; + padding-right: 0.75rem; +} + +.px-5 { + padding-left: 1.25rem; + padding-right: 1.25rem; +} + +.px-6 { + padding-left: 1.5rem; + padding-right: 1.5rem; +} + +.py-1 { + padding-top: 0.25rem; + padding-bottom: 0.25rem; +} + +.py-2 { + padding-top: 0.5rem; + padding-bottom: 0.5rem; +} + +.py-24 { + padding-top: 6rem; + padding-bottom: 6rem; +} + +.text-center { + text-align: center; +} + +.text-3xl { + font-size: 1.875rem; + line-height: 2.25rem; +} + +.text-base { + font-size: 1rem; + line-height: 1.5rem; +} + +.text-lg { + font-size: 1.125rem; + line-height: 1.75rem; +} + +.text-xl { + font-size: 1.25rem; + line-height: 1.75rem; +} + +.font-medium { + font-weight: 500; +} + +.leading-relaxed { + line-height: 1.625; +} + +.text-gray-400 { + --tw-text-opacity: 1; + color: rgb(156 163 175 / var(--tw-text-opacity)); +} + +.text-white { + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); +} + +.hover\:bg-gray-700:hover { + --tw-bg-opacity: 1; + background-color: rgb(55 65 81 / var(--tw-bg-opacity)); +} + +.hover\:bg-indigo-600:hover { + --tw-bg-opacity: 1; + background-color: rgb(79 70 229 / var(--tw-bg-opacity)); +} + +.hover\:text-white:hover { + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); +} + +.focus\:outline-none:focus { + outline: 2px solid transparent; + outline-offset: 2px; +} + +@media (min-width: 640px) { + .sm\:text-4xl { + font-size: 2.25rem; + line-height: 2.5rem; + } +} + +@media (min-width: 768px) { + .md\:mb-0 { + margin-bottom: 0px; + } + + .md\:ml-auto { + margin-left: auto; + } + + .md\:mt-0 { + margin-top: 0px; + } + + .md\:w-1\/2 { + width: 50%; + } + + .md\:flex-row { + flex-direction: row; + } + + .md\:items-start { + align-items: flex-start; + } + + .md\:pr-16 { + padding-right: 4rem; + } + + .md\:text-left { + text-align: left; + } +} + +@media (min-width: 1024px) { + .lg\:inline-block { + display: inline-block; + } + + .lg\:w-full { + width: 100%; + } + + .lg\:max-w-lg { + max-width: 32rem; + } + + .lg\:flex-grow { + flex-grow: 1; + } + + .lg\:pr-24 { + padding-right: 6rem; + } +} \ No newline at end of file diff --git a/examples/tailwind/src/main.rs b/examples/tailwind/src/main.rs new file mode 100644 index 0000000000..70ce484c38 --- /dev/null +++ b/examples/tailwind/src/main.rs @@ -0,0 +1,103 @@ +#![allow(non_snake_case)] + +use dioxus::prelude::*; + +const _STYLE: &str = asset!("public/tailwind.css"); + +fn main() { + launch(app); +} + +pub fn app() -> Element { + let grey_background = true; + rsx!( + div { + header { + class: "text-gray-400 body-font", + // you can use optional attributes to optionally apply a tailwind class + class: if grey_background { + "bg-gray-900" + }, + div { class: "container mx-auto flex flex-wrap p-5 flex-col md:flex-row items-center", + a { class: "flex title-font font-medium items-center text-white mb-4 md:mb-0", + StacksIcon {} + span { class: "ml-3 text-xl", "Hello Dioxus!" } + } + nav { class: "md:ml-auto flex flex-wrap items-center text-base justify-center", + a { class: "mr-5 hover:text-white", "First Link" } + a { class: "mr-5 hover:text-white", "Second Link" } + a { class: "mr-5 hover:text-white", "Third Link" } + a { class: "mr-5 hover:text-white", "Fourth Link" } + } + button { class: "inline-flex items-center bg-gray-800 border-0 py-1 px-3 focus:outline-none hover:bg-gray-700 rounded text-base mt-4 md:mt-0", + "Button" + RightArrowIcon {} + } + } + } + + section { class: "text-gray-400 bg-gray-900 body-font", + div { class: "container mx-auto flex px-5 py-24 md:flex-row flex-col items-center", + div { class: "lg:flex-grow md:w-1/2 lg:pr-24 md:pr-16 flex flex-col md:items-start md:text-left mb-16 md:mb-0 items-center text-center", + h1 { class: "title-font sm:text-4xl text-3xl mb-4 font-medium text-white", + br { class: "hidden lg:inline-block" } + "Dioxus Sneak Peek" + } + p { class: "mb-8 leading-relaxed", + + "Dioxus is a new UI framework that makes it easy and simple to write cross-platform apps using web + technologies! It is functional, fast, and portable. Dioxus can run on the web, on the desktop, and + on mobile and embedded platforms." + } + div { class: "flex justify-center", + button { class: "inline-flex text-white bg-indigo-500 border-0 py-2 px-6 focus:outline-none hover:bg-indigo-600 rounded text-lg", + "Learn more" + } + button { class: "ml-4 inline-flex text-gray-400 bg-gray-800 border-0 py-2 px-6 focus:outline-none hover:bg-gray-700 hover:text-white rounded text-lg", + "Build an app" + } + } + } + div { class: "lg:max-w-lg lg:w-full md:w-1/2 w-5/6", + img { + class: "object-cover object-center rounded", + src: "https://i.imgur.com/oK6BLtw.png", + referrerpolicy: "no-referrer", + alt: "hero" + } + } + } + } + } + ) +} + +pub fn StacksIcon() -> Element { + rsx!( + svg { + fill: "none", + stroke: "currentColor", + stroke_linecap: "round", + stroke_linejoin: "round", + stroke_width: "2", + class: "w-10 h-10 text-white p-2 bg-indigo-500 rounded-full", + view_box: "0 0 24 24", + path { d: "M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5" } + } + ) +} + +pub fn RightArrowIcon() -> Element { + rsx!( + svg { + fill: "none", + stroke: "currentColor", + stroke_linecap: "round", + stroke_linejoin: "round", + stroke_width: "2", + class: "w-4 h-4 ml-1", + view_box: "0 0 24 24", + path { d: "M5 12h14M12 5l7 7-7 7" } + } + ) +} diff --git a/examples/tailwind/tailwind.config.js b/examples/tailwind/tailwind.config.js new file mode 100644 index 0000000000..2a69d5803a --- /dev/null +++ b/examples/tailwind/tailwind.config.js @@ -0,0 +1,9 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + mode: "all", + content: ["./src/**/*.{rs,html,css}", "./dist/**/*.html"], + theme: { + extend: {}, + }, + plugins: [], +}; diff --git a/examples/title.rs b/examples/title.rs index f836a7f3d6..ebe258963c 100644 --- a/examples/title.rs +++ b/examples/title.rs @@ -3,7 +3,8 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + tracing_subscriber::fmt::init(); + launch(app); } fn app() -> Element { @@ -13,7 +14,7 @@ fn app() -> Element { div { // You can set the title of the page with the Title component // In web applications, this sets the title in the head. On desktop, it sets the window title - document::Title { "My Application (Count {count})" } + Title { "My Application (Count {count})" } button { onclick: move |_| count += 1, "Up high!" } button { onclick: move |_| count -= 1, "Down low!" } } diff --git a/examples/todomvc.rs b/examples/todomvc.rs index a95fda0e2d..ea2e528a12 100644 --- a/examples/todomvc.rs +++ b/examples/todomvc.rs @@ -3,10 +3,10 @@ use dioxus::prelude::*; use std::collections::HashMap; -const STYLE: Asset = asset!("/examples/assets/todomvc.css"); +const STYLE: &str = asset!("./examples/assets/todomvc.css"); fn main() { - dioxus::launch(app); + launch(app); } #[derive(PartialEq, Eq, Clone, Copy)] @@ -63,7 +63,7 @@ fn app() -> Element { }; rsx! { - document::Stylesheet { href: STYLE } + head::Link { rel: "stylesheet", href: STYLE } section { class: "todoapp", TodoHeader { todos } section { class: "main", diff --git a/examples/weather_app.rs b/examples/weather_app.rs index 7b56bbbe54..395fd81f19 100644 --- a/examples/weather_app.rs +++ b/examples/weather_app.rs @@ -4,7 +4,7 @@ use dioxus::prelude::*; use serde::{Deserialize, Serialize}; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { @@ -19,7 +19,7 @@ fn app() -> Element { let current_weather = use_resource(move || async move { get_weather(&country()).await }); rsx! { - document::Stylesheet { href: "https://unpkg.com/tailwindcss@^2.0/dist/tailwind.min.css" } + head::Link { rel: "stylesheet", href: "https://unpkg.com/tailwindcss@^2.0/dist/tailwind.min.css" } div { class: "mx-auto p-4 bg-gray-100 h-screen flex justify-center", div { class: "flex items-center justify-center flex-row", div { class: "flex items-start justify-center flex-row", diff --git a/examples/web_component.rs b/examples/web_component.rs index a5f831912b..df0515397b 100644 --- a/examples/web_component.rs +++ b/examples/web_component.rs @@ -7,7 +7,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/window_event.rs b/examples/window_event.rs index ca126f6a1d..cb91d2a5b2 100644 --- a/examples/window_event.rs +++ b/examples/window_event.rs @@ -13,7 +13,7 @@ use dioxus::desktop::{window, Config, WindowBuilder}; use dioxus::prelude::*; fn main() { - dioxus::launch::builder() + LaunchBuilder::desktop() .with_cfg( Config::new().with_window( WindowBuilder::new() @@ -26,7 +26,7 @@ fn main() { fn app() -> Element { rsx!( - document::Link { href: "https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css", rel: "stylesheet" } + head::Link { href: "https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css", rel: "stylesheet" } Header {} div { class: "container mx-auto", div { class: "grid grid-cols-5", diff --git a/examples/window_focus.rs b/examples/window_focus.rs index 1213b3e994..447544077b 100644 --- a/examples/window_focus.rs +++ b/examples/window_focus.rs @@ -12,7 +12,7 @@ use dioxus::desktop::{Config, WindowCloseBehaviour}; use dioxus::prelude::*; fn main() { - dioxus::launch::builder() + LaunchBuilder::desktop() .with_cfg(Config::new().with_close_behaviour(WindowCloseBehaviour::CloseWindow)) .launch(app) } diff --git a/examples/window_zoom.rs b/examples/window_zoom.rs index 35d25f4fde..8b10d503b0 100644 --- a/examples/window_zoom.rs +++ b/examples/window_zoom.rs @@ -5,7 +5,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch_desktop(app); } fn app() -> Element { diff --git a/examples/xss_safety.rs b/examples/xss_safety.rs index 904a42b07b..cb8d414dd7 100644 --- a/examples/xss_safety.rs +++ b/examples/xss_safety.rs @@ -5,7 +5,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { From 611bad0e0fc4c4fe4f4ead05e2e3dddf0e430e65 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Sun, 15 Sep 2024 18:06:22 -0700 Subject: [PATCH 108/139] drop changes to examples --- examples/PWA-example/Cargo.toml | 17 - examples/PWA-example/Dioxus.toml | 27 - examples/PWA-example/LICENSE | 21 - examples/PWA-example/README.md | 45 - examples/PWA-example/index.html | 30 - examples/PWA-example/public/favicon.ico | Bin 23104 -> 0 bytes examples/PWA-example/public/logo_192.png | Bin 11198 -> 0 bytes examples/PWA-example/public/logo_512.png | Bin 48151 -> 0 bytes examples/PWA-example/public/manifest.json | 34 - examples/PWA-example/public/sw.js | 198 - examples/PWA-example/src/main.rs | 21 - examples/all_events.rs | 6 +- examples/assets/purecss.css | 12 + examples/backgrounded_futures.rs | 2 +- examples/calculator.rs | 6 +- examples/calculator_mutable.rs | 4 +- examples/clock.rs | 10 +- examples/control_focus.rs | 9 +- examples/counters.rs | 6 +- examples/crm.rs | 11 +- examples/custom_assets.rs | 32 +- examples/custom_html.rs | 2 +- examples/custom_menu.rs | 2 +- examples/disabled.rs | 2 +- examples/dog_app.rs | 2 +- examples/dynamic_asset.rs | 6 +- examples/errors.rs | 36 +- examples/eval.rs | 29 +- examples/file_explorer.rs | 8 +- examples/file_upload.rs | 6 +- examples/flat_router.rs | 6 +- examples/form.rs | 2 +- examples/future.rs | 2 +- examples/generic_component.rs | 2 +- examples/global.rs | 6 +- examples/hash_fragment_state.rs | 4 +- examples/hello_world.rs | 2 +- examples/hydration.rs | 2 +- examples/image_generator_openai.rs | 4 +- examples/link.rs | 6 +- examples/login_form.rs | 2 +- examples/memo_chain.rs | 2 +- examples/meta.rs | 13 +- examples/mobile_demo/.gitignore | 10 - examples/mobile_demo/Cargo.lock | 4007 ----------- examples/mobile_demo/Cargo.toml | 51 - examples/mobile_demo/README.md | 11 - examples/mobile_demo/mobile.toml | 8 - examples/mobile_demo/src/index.html | 12 - examples/mobile_demo/src/lib.rs | 90 - examples/multiwindow.rs | 2 +- examples/nested_listeners.rs | 2 +- .../openid_connect_demo/.cargo/config.toml | 5 - examples/openid_connect_demo/.gitignore | 3 - examples/openid_connect_demo/Cargo.lock | 6372 ----------------- examples/openid_connect_demo/Cargo.toml | 36 - examples/openid_connect_demo/Dioxus.toml | 35 - examples/openid_connect_demo/README.md | 12 - examples/openid_connect_demo/src/constants.rs | 2 - examples/openid_connect_demo/src/main.rs | 36 - examples/openid_connect_demo/src/model/mod.rs | 1 - .../openid_connect_demo/src/model/user.rs | 7 - examples/openid_connect_demo/src/oidc.rs | 127 - .../openid_connect_demo/src/props/client.rs | 20 - examples/openid_connect_demo/src/props/mod.rs | 1 - examples/openid_connect_demo/src/router.rs | 16 - examples/openid_connect_demo/src/storage.rs | 35 - .../openid_connect_demo/src/views/header.rs | 221 - .../openid_connect_demo/src/views/home.rs | 5 - .../openid_connect_demo/src/views/login.rs | 86 - examples/openid_connect_demo/src/views/mod.rs | 4 - .../src/views/not_found.rs | 10 - examples/optional_props.rs | 2 +- examples/overlay.rs | 8 +- examples/popup.rs | 2 +- examples/query_segment_search.rs | 6 +- examples/read_size.rs | 4 +- examples/readme.rs | 2 +- examples/reducer.rs | 6 +- examples/resize.rs | 4 +- examples/router.rs | 6 +- examples/router_resource.rs | 2 +- examples/rsx_usage.rs | 5 +- examples/scroll_to_top.rs | 2 +- examples/shortcut.rs | 2 +- examples/shorthand.rs | 2 +- examples/signals.rs | 2 +- examples/simple_list.rs | 2 +- examples/simple_router.rs | 2 +- examples/streams.rs | 2 +- examples/suspense.rs | 2 +- examples/svg.rs | 2 +- examples/tailwind/.gitignore | 1 - examples/tailwind/Cargo.toml | 20 - examples/tailwind/Dioxus.toml | 27 - examples/tailwind/README.md | 7 - examples/tailwind/input.css | 3 - examples/tailwind/public/tailwind.css | 833 --- examples/tailwind/src/main.rs | 103 - examples/tailwind/tailwind.config.js | 9 - examples/title.rs | 5 +- examples/todomvc.rs | 6 +- examples/weather_app.rs | 4 +- examples/web_component.rs | 2 +- examples/window_event.rs | 4 +- examples/window_focus.rs | 2 +- examples/window_zoom.rs | 2 +- examples/xss_safety.rs | 2 +- 108 files changed, 164 insertions(+), 12795 deletions(-) delete mode 100644 examples/PWA-example/Cargo.toml delete mode 100644 examples/PWA-example/Dioxus.toml delete mode 100644 examples/PWA-example/LICENSE delete mode 100644 examples/PWA-example/README.md delete mode 100644 examples/PWA-example/index.html delete mode 100644 examples/PWA-example/public/favicon.ico delete mode 100644 examples/PWA-example/public/logo_192.png delete mode 100644 examples/PWA-example/public/logo_512.png delete mode 100644 examples/PWA-example/public/manifest.json delete mode 100644 examples/PWA-example/public/sw.js delete mode 100644 examples/PWA-example/src/main.rs create mode 100644 examples/assets/purecss.css delete mode 100644 examples/mobile_demo/.gitignore delete mode 100644 examples/mobile_demo/Cargo.lock delete mode 100644 examples/mobile_demo/Cargo.toml delete mode 100644 examples/mobile_demo/README.md delete mode 100644 examples/mobile_demo/mobile.toml delete mode 100644 examples/mobile_demo/src/index.html delete mode 100644 examples/mobile_demo/src/lib.rs delete mode 100644 examples/openid_connect_demo/.cargo/config.toml delete mode 100644 examples/openid_connect_demo/.gitignore delete mode 100644 examples/openid_connect_demo/Cargo.lock delete mode 100644 examples/openid_connect_demo/Cargo.toml delete mode 100644 examples/openid_connect_demo/Dioxus.toml delete mode 100644 examples/openid_connect_demo/README.md delete mode 100644 examples/openid_connect_demo/src/constants.rs delete mode 100644 examples/openid_connect_demo/src/main.rs delete mode 100644 examples/openid_connect_demo/src/model/mod.rs delete mode 100644 examples/openid_connect_demo/src/model/user.rs delete mode 100644 examples/openid_connect_demo/src/oidc.rs delete mode 100644 examples/openid_connect_demo/src/props/client.rs delete mode 100644 examples/openid_connect_demo/src/props/mod.rs delete mode 100644 examples/openid_connect_demo/src/router.rs delete mode 100644 examples/openid_connect_demo/src/storage.rs delete mode 100644 examples/openid_connect_demo/src/views/header.rs delete mode 100644 examples/openid_connect_demo/src/views/home.rs delete mode 100644 examples/openid_connect_demo/src/views/login.rs delete mode 100644 examples/openid_connect_demo/src/views/mod.rs delete mode 100644 examples/openid_connect_demo/src/views/not_found.rs delete mode 100644 examples/tailwind/.gitignore delete mode 100644 examples/tailwind/Cargo.toml delete mode 100644 examples/tailwind/Dioxus.toml delete mode 100644 examples/tailwind/README.md delete mode 100644 examples/tailwind/input.css delete mode 100644 examples/tailwind/public/tailwind.css delete mode 100644 examples/tailwind/src/main.rs delete mode 100644 examples/tailwind/tailwind.config.js diff --git a/examples/PWA-example/Cargo.toml b/examples/PWA-example/Cargo.toml deleted file mode 100644 index bee204f20a..0000000000 --- a/examples/PWA-example/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "dioxus-pwa-example" -version = "0.1.0" -authors = ["Antonio Curavalea "] -edition = "2021" -publish = false - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -dioxus = { workspace = true, features = ["web"] } - -log = "0.4.6" - -# WebAssembly Debug -wasm-logger = "0.2.0" -console_error_panic_hook = "0.1.7" diff --git a/examples/PWA-example/Dioxus.toml b/examples/PWA-example/Dioxus.toml deleted file mode 100644 index a831a28ff7..0000000000 --- a/examples/PWA-example/Dioxus.toml +++ /dev/null @@ -1,27 +0,0 @@ -[application] - -# App (Project) Name -name = "dioxus-pwa-example" - -# Dioxus App Default Platform -# desktop, web, mobile, ssr -default_platform = "web" - -# `build` & `serve` dist path -out_dir = "dist" - -# resource (public) file folder -asset_dir = "public" - -[web.app] - -# HTML title tag content -title = "dioxus | ⛺" - -[web.watcher] - -# when watcher trigger, regenerate the `index.html` -reload_html = true - -# which files or dirs will be watcher monitoring -watch_path = ["src", "public"] diff --git a/examples/PWA-example/LICENSE b/examples/PWA-example/LICENSE deleted file mode 100644 index bcdd828e9c..0000000000 --- a/examples/PWA-example/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2022 Dioxus - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/examples/PWA-example/README.md b/examples/PWA-example/README.md deleted file mode 100644 index d501df2126..0000000000 --- a/examples/PWA-example/README.md +++ /dev/null @@ -1,45 +0,0 @@ -# Dioxus PWA example - -This is a basic example of a progressive web app (PWA) using Dioxus and Dioxus CLI. -Currently PWA functionality requires the use of a service worker and manifest file, so this isn't 100% Rust yet. - -It is also very much usable as a template for your projects, if you're aiming to create a PWA. - -## Try the example - -Make sure you have Dioxus CLI installed (if you're unsure, run `cargo install dioxus-cli --locked`). - -You can run `dx serve` in this directory to start the web server locally, or run -`dx build --release` to build the project so you can deploy it on a separate web-server. - -## Project Structure - -``` -├── Cargo.toml -├── Dioxus.toml -├── index.html // Custom HTML is needed for this, to load the SW and manifest. -├── LICENSE -├── public -│ ├── favicon.ico -│ ├── logo_192.png -│ ├── logo_512.png -│ ├── manifest.json // The manifest file - edit this as you need to. -│ └── sw.js // The service worker - you must edit this for actual projects. -├── README.md -└── src - └── main.rs -``` - -## Resources - -If you're just getting started with PWAs, here are some useful resources: - -- [PWABuilder docs](https://docs.pwabuilder.com/#/) -- [MDN article on PWAs](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps) - -For service worker scripting (in JavaScript): - -- [Service worker guide from PWABuilder](https://docs.pwabuilder.com/#/home/sw-intro) -- [Service worker examples, also from PWABuilder](https://github.com/pwa-builder/pwabuilder-serviceworkers) - -If you want to stay as close to 100% Rust as possible, you can try using [wasi-worker](https://github.com/dunnock/wasi-worker) to replace the JS service worker file. The JSON manifest will still be required though. diff --git a/examples/PWA-example/index.html b/examples/PWA-example/index.html deleted file mode 100644 index 44bfa59cc4..0000000000 --- a/examples/PWA-example/index.html +++ /dev/null @@ -1,30 +0,0 @@ - - - - {app_title} - - - - - - {style_include} - - -
      - - {script_include} - - \ No newline at end of file diff --git a/examples/PWA-example/public/favicon.ico b/examples/PWA-example/public/favicon.ico deleted file mode 100644 index b11015bdbacdb3b99e03555edb85d78467b37c7d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23104 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 W+`s-{ME)24jHW23DqAIO8vH-~t)grI diff --git a/examples/PWA-example/public/logo_192.png b/examples/PWA-example/public/logo_192.png deleted file mode 100644 index 112eb5ee49ca4e69d742a99269d1ae08a970dd7a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11198 zcmb_?Ra6{Lu=nEb?hxF9yDSody9Os%aCdii2?TcwED+olcMBfe-JQiQ|Htp)d%5R! z&vc!sIaSkLReiep*Au0pB#nklj0^w(&}3!4sD04x|87M1k5+Bf7U6@yT8b-*0|0di zC@&^(AJ3GgGHQwdfHxfg5Eu#oKt5Q3hX8;ZI{SC@)^YwqeYlRo)x6;R!k;HnDoy}g6~<^prouqOt?fX9 zL|F^nQVA5+21WT=`p7IQqFnI{1)58Jv!N1 z{?CdT)b|eN%iNa>i-Sr=Sl9EgE~RPLmhF_CwdGjHqpc^~Bd({>7vc0&&qUJ~KPc2+ zyaCHt;@YrA0dr})&a9c&s0kdh!*Iah(=HIDTV!U$&}2n5s;`EYfD?tX=rGVZL6 zDtv;{#Oh5yR7#@~L#}l;(H*geqA>-?qJqK-`L7Ue*gG12aU*tDA@kVDgVoJtYKs`H zh%32n7Ic!={um9B|8P)BmeWPgfqlZ1kd$KpkzFiU!0H7vO)KgNI@Q*KQEd+LGk*{h zpPUUWrL5t1%LUmDCC*gj&egGo^m5kzzOuLb%5crZATf)o8Jfc+8JJdUuF&{r1KO+^ zV2ffaG`f_h(J^4`5U4zXKuv#d8=+vQL!X}hHAtG#u@Y4(k(ZU|u1!8)vKWNoEd*c7 zI-A5SM3oP22T-B*O8w^%m3{RJ)bjQG(p14KKO_c;;$PH9Z|9*R>l1!P^()Y*u9Bmq zj<} z4`3S&Z`ApjXiYQIwv`sju3V@}5%Ifp{bu|Ggf!zEX@)Zoy$_~ag@zZ}26c|Ir^)u;pS^6R zNv{smj;!ad4|8}gn>;L_Yp}+<4jP~IbWo4+Xu;u=b3_h@37kwP%IuZeS9up$Q9&t+ zMdeyJv5%~ZAxjQR)+1Z#(diW(X$3C&)CXv0u0bDKhrOLExl(UyV7Zs)>uWYTYyM3f z4dnv>T$hOx?$K-v0HOo?1L72x_|p6(l!HIYprDFvll7j}s#`fzK00!~0>9FVJwk4{&&8QJ?gB`ciJw%}Q5H0Nl|LTF_5 zg&Z&Mt@%5IqTT=iV2!J-G9j3$7{W__Otofl0C0@pQ6}D&Xu{7F{Q&^ln7j>7cvtSf zxb!vBJi|@5;WRnee$k|kSNN;;9Y571{5r|BV_=vE)>yW-#F*PJr{27yZe!K={Yc#w zoKBJzc={gm;MQ}G*8VqE>A+i@R#gyfETiKYaNj7Lzx=2{B7jVi*hJ)ACvr8@(<7c)|-C8 z_5^XLw_6v)-Jphz{95kd(u7WMZ&#e-GKm<)@Hqp#5>=4I#CP%T;-y+?7iL=&s{8&K zXAhdRS0%^i5q}h8I^z}Zr4Dy$2~kKOhcKshvY^n~mahgK*~ z>|oqRgZcLSWWNniw0@e?c@H~KzZL*!CpLaqP}Fx-BvD%sxQR4i*WlVf=HYR*N_xA6 z^bx{OuJH7W4`DH|19PtzwZ(@b8^8_0K>l>-VOMyT%m^yRj-sUPRzB7T3B%K02IAB>_Tg0SxS#)v5ypGJMD#=i$0 z|NH|=c|*t-CnqHP-k8Kle~nRaM{BPG8+!p()z+r$L47zA`Tvd+3vxPLo27RSb~RSocUQ zz6IEkvx+Uojv@NKM(AMgiMbLjpcS7gT6z5WJ0x4!(3Af|!*YD_l1S>Wrr+Z(O`o#$ z-eeBfRxRJo{-(F!DIR1)4cFBMULoGPA;bIl2%KgpQ<5T|(}Yr{8jYOWv2nOTlxULI z@2~7%#cw)G;9iDK6N#r&K?|XA7$_02l(a|tKOhBdzB~6Rl^sV$4(|5m$GELds1SF1 zKaK0%K8Kl&S7NvUbhqI~0I8MdZy9-khBh9jq4ghvuO}1n1~Wo(=Saq0fXUYW-ktWC9bgys)^tuFCX;%V zGHR-F`|$d;FV{?g_7l2FX@BZn{Y&3E6^8*9qN0pMrDDlYp+++yin1SL-TjvtkSDI; z)4vm#)~%QK&%3z*Y>vkXp>F>-X7Mfq**N zJ+_tA&)k4C6R0`JOm+svPNd59bdDihg!iJBA?LUZ-yz0;FsHu>$3MynX>;ZplErVC(^E`xRlHOgG&>cCx)o& zwjfgMp6)lR+T|&R(aBWY5Ey~7nYZWp$STD0U76FxL`X_Keu$v_3;%9sDMN;rlqW>+ z>b@b)V+@1%Dn9Sk!8d{>EztT8zZrQl!0?CiDt~Id_S9$gnX86k>fEfV{8d4CKOyOC z*l>@)GZF`>&BHm{r(jn$kuWopf1b^dL`<=9eC~eKnTT4F^T=;{?b{15?{~h+&UVtD zEDZxXNm{L-0JsS4oc{n(^PDsALd&b;5s6pI0La@mUHHDkUbB{rf$h zU>a)=k?;CzYWO#^R*Q}CN{5kz7~*Vc^{C|Rf&pOnK~kuS&?$~E$O@1gnJaRfE|k?Y z{jqOy>vmfDIA7)rf$HRk1xMzDRLuJn9SwE)-(ah=c=>i@IS=pFz#ZC$1c`d?Ib}%= zdFm-AD^ucUYAqV?trwnswR*YZ)h_gPYhA5z;~uc z_@%mC1?O*e9V~SDeJMZ4HgXzqmLN0z)FG-Ee3eNr#K+4kRf5?He3q<10Z@Wrb({K% zJZQa~eCWduG*BH1ip-J2G+HqA_z;_HC6`RGz6F@^5RR~&zcVh(`j)yzhV>*(t&;eR zZ-6f3-Y)iHP{Kb4RCq|B`Va3ZUiyEm$qm?Vui%0*KY}r(H`<$YBjA2g@jFt~92&eO zt2o6mkG$cVPeN^VtbUjrW&W^=SQ;GDwcmjgRdm!gJZGXtX;XIuzN(SwEH+`oA1PRC1%3*M5AcM>1BOTE#Wk70LhDz z&N@2jhz8~OA#Aj3I`4rco#1X8;vp$`P-&g2C{JMK$3s4&hk$@@TiVf`$9%3*yFzQE zP`8;2O*JiUZ+|u44!SNq5p0A~aJF|3&i?xnCK=&6M)o~p%6Zdgjp}HXp4HgYYM*Pd zapQrg9GZzU1%Yvk`4SajyzkUgmkqz?`WD`BuPeQ>>#X%2XZo(e+yfxPeN{Y5zQ^ZU zny9na3W}Udv6YSWLxtjt6+CKfiQI(HC|W=u83-c}S;WN%NHVDd-cbYBPyz4_0mOa2DwmsxcveCMJI%(QB;%B=ktjxck$j{B}agaYf zd)+rsK95`To*XxRfP79lgAq8p^%A37_;6PNba@T}N#4!nD=>HH_TOR=$<}zNKYn6l z)9mb(--4(&!XNyhRtT`0fY5*JQj~@5uC|G5WcrCmzqJOmIsU6#Doy;1#BjdgAn!s# zdeytBZll9B+DPx=N_vHUJn5jFKwO0acY0ztQA($S$OoF&akyLjb7I+ts8W*o=tkfL&Jp1kdAQvJc&2qlv>TirGz}8(m67R28Da{nAsC zs=4wTa-P+3e@clV+?OOxA#R`zvti_c2NcG8%$R)H+bH~fJWFU8@YUcKE3=Zr(3-8z4T`_Z zUKIu8azyxX)7IYLsVr?wPQ(a=`h#M8H(C$+s^N7%>$Som57N8g?(RH=cgrg}r2)ypk^n!))H6nZztKle2`GY6rr@QHipuP`3SIh(IwwejfA3c&7BLj?BOKAK5=rNXJD#{W5oI z*N!ykZr#%?vl-l9c0Ek<@3t#tgf{SX)emMhfTZKnkt;^~oEFnCu=J-n#9SLpz8&Yt zD)}q#NwWP?PqsM34KvVcY`E!d&JOgD0c5p%5SfMwJh6jJsWUPl+yL8xHP}<*`cXuvLg-X6dhBP>QAuVKtn7T>!nCV9EOUi|t18(A zp$nD{wu@fC@p{?AU=7o?wU46wb;5`xkj>#2V&%(-d+#yYUG2V$~e10f!#X7|b3S8xQIS7`#zEpJ9i64PfRmXd}d zIl0jLUw$EebNcEcVJ1JWEru8g=ibq+Pe7Cckh35d}Pwu z=+wPCX@});35L-IIFz-c*^;E^eg5VbfKZYKQs;JqtJKP{k_RMexe~*qHF(8TBs8d` z3bt^6NvHWcZMoh=pZpeNMCkL-ui!Be0MLi(NEz;~ZU1tjlr=0y(zU7U8`Sz3ythLI zEw5^Ys&Vyo9aV1JzjEiSLz@Lx9^1>>;dGP?rXtUdLauR&Z6*_c=4q{I#Ao5%um#$a z@6)S_2cfOkz&(vk6hh_&AU6V`U72+!p>hCYbp5<`KKs?v9my5T*8>hi zv}H%7X7=d;(Bmyh`Qk|^s5$trtlrFnt<4Jw#(DhPU%91_FcY-d^i|#{2QLixMQGB< zg10RKb8siJ1zWgY7sv4S&LWjLM^Hy5gWUc)PDfnEzWIj_)x_+6R~_i#bpE-)R5!Beahlo}mh8^=RcHR{LJ z;+*PWk>>ki!cY#caWT)Wtd?eH+^_yAyNW>e>KnZF(g`tmhP(=>1=c%%;g$A z)c1Zw6=COB?1$Yc(tmm{ab(b4H42J8HEV5v*%$@wYLZ&^X{x+rWeXGRdaU7NDR=HV z1k;Q-SGZqFNd5J>PI7Gzq~9%XA}HEF@ymP!0z#SM&1_DsmH)2JWQT>#*zzYhyP+~A z6Vdgen<6)QF#uK5jqGJ}+0OSU!s@(p0I%nH6L}NB!od0X#V%2(87=MYp!emyd~6nU zUA}G9@udVaHPdfAikl-JVd)4&S>jDdfJK2`pPN(|I1nYSB@Tq_ zxv`_9$ADWJuESXyhy5o(q)V+W)|W8Bw>O-gBH)t$7(D{Ky4p>Y7c(;tZ>N|El26}3 z-n7#XFr$JVSufXoPCDvwm|$g}BygO@S}w*UF~{Jyc=wk2|= zog7ypxQ55ETD-W}3lv!5o{qk9pz!2{|D)hdf}ENFj=SB6tw=qgHUM?#pn(=OL+h1@ zr{_Wg{!W?kJ9o%Ct8D?h$im=n8LsG5Zg?h+AyXHY&>&z-WD#(8r_P5LYdk@XYZ0$U zl&%xdT zn4EJ0m0zxS!r_ZjH6$L^KSnf`F$c}xEGoldH;ezL)ZinF_0fbnW0C535BH#1lAubB z=WzHz@Kk|Fa$^ynfEYS`1$Xf0@(0Gd%bH7ZG9$7){VQ2dFQ*R`qK|CB8l?TOkW0EO}<3=(2)WM;2d*<}ESNh|kp&qKWb2@i$lBWNCjey42F#xX)rJ zSxT5JV_@2z;kMOC`kTf`4t=VjD%lLM3W+o~9WGo!onWo+!zx11c&^?tiJ4u@n7zvE z4&?MNS(C5L)%4_wDcby2ui1&w?YF4lD= zC*|46vW#o%PoUp|MaryU07Z6Z;8}_+E~+F=bCBP3X4U|_SI|4mWKC5ZfnY~U%lF1;|p;X zXE*Q)Yi&&iDb_Hg+cSErrtJ6Xe(LFXqITRXl(w7cXL3X9sP6B@QBVv<1uJX7-#Q?; znp=cxZ(QHy?uw9coLsjzNgKXBC*YS5;s`aw7@UbAx#D#A8VV@C05c;jN=3?nr?c3QWP#Z&E$cA^(;>-Nt`7TeM zPpQpG>mAi|OCH}gBjbR#ugJ@NB~;~sZFkc%1B*d8W~PrdV`w))wz#+~BgsOClB^V8 z9o3=zge^uK4vsFs&5M}p1}#4CcrU0_=MJF?=G_2q2AF-+^RBCXy=b$%4ea`Uwx%e2 z%Py*T`IyVUkVRs06r}=2d^?E1s%f?0YWpi~H6Rx{ym~@R1^cE#md2H=@3)S^BElgx z%UetDUsH?349H`OETQEi)$FZYe3sN^V{XY8{B2YoHC{rIhWT&RX8mz@{Sj@kYk7n7 zF#uzG7Is5-W zpI&hPh1Gwyeh0rf_c5zzAz{42b+s`Kc7m6Jff>auFNmST`j2>Ph)GJ?>=(mi`fqCn z!p;4fWG+yuIqR7Z$ugnjX@=thW*<>Vt(G5OqjC1HJF6jf=l5L9mWfVS$g)5v1@Z;Y)g z=?UM#qzl?iOch=xiGnzHknPh`FnElF>_XYQxUI|mg%`1_JMDGT> zhs&es)JjA}JrYEMN2L7;X;m(2=sq0EG#2ywnYh)rSKiOh4wy{E6S25?jC8Spj|rXI zMg?+a(q8d*y4jF1C6PLZful0I(MqlzPAXV#u$M?Xag9`@ z{|D#?10Iq28_#Fg!R%O`t#)MU-X*@AzK+A>cTmmpu=YusAE&Hj^GcK=;0(Q%+TNJV ztQk=ZXD7*9icpl{WjLn|A83dnCr#%{6O08sOw=y`&Q6M*}AS^IqlY~2URjP0&O%I7O_ z^zq3!L!xdz_on%PsB4A&v1##SSYZ$(gB*Dbe2n&UNj$KKYKacRzu0#8Y$pzXgM#Jm zsZk7h>7=dEgl$NZmlxhx#O3((6H2oiLWQA>@kpe=+)4|eQkkXu>VRM&&S>G!$bi1_ zP*F)Bg{H3g0Bh7PI;!|H&V!BAJ7usPx~OF5&-@$#;W*PY%xo}(hgQ@Mi+Xjo9NLih z_R_jvUM+fc1rsSKJu)Bd4hxhaE|?f3gY#xgOE!Tf>^P)leemrIf=T(sG+0P!_cwso zHA}dauZF31`w@9#FB+42QnMIw#72^QJ<^NaMHK*$=!v{7$dLWM$FoEpq0Wm`u!Et^ zIrq-)vB91=7DC~y0|4r14NI3Ibnti$&P$29)e#Q7reFpSwhZoPM6^`zmIA`{g3CZK z6-51Gr2=4zONiXz>YtQfPYxLbu-N>p+(?z((8)}`d3vwNrfMaPGP?`1v`ZLXX%vRM4_(wm?H+GNkm8ArhL7Rz zJ;&!B>+LP3Jr!lXnmlgSiv8rKb^V9|e1SI9;4Q_vOtF|-E)5}y6-S!kHK3d%-}S#X zdzcbD2Tse1i7b-Ac7(4|O30foe}d(VW<((^&KtDR#u|U#{av9nb8#ipdkxj;X*ZI^ zCZog=Zw~<#Do`Uca>d(+dD>JO#p>Wgs6xpKeLq0~B`Y-PsZnP(%f&Da#Y-!sh?oAq z@Jzz^2g*ddOC7K)JA$h6J@Dw_I-d<+XN?Tk4X>>KD1tPi;K@T6LV=z z)HgY$FzG}^y|C5W8B-SEnUY51Qe7}EPaa-bK+=~}b4WW*E1?E+8SJJyoTUN=1G#2) z4RZA0^-A)VPf#FJl8n!y@$u6?e-}3|C(VcW+@33YNTiC0sM*1~<&S@Ffcd+FPq>!k z3n&GuI&I0|7iYS*x=bh8IIkbOsQ`2~UP8Hl$D`5R2=l7h&lITGwY2-pIU_;~ZnOyd z<#FX&(U!U{!&f$cucl}5El=8v)Ol{G%c)(^D5$8IEVI8_Nx?AAj`A=ks`ofChY+to zCg8lnIkmCkqqNUS6{;DpnUptL?|L=l`$wyLFfNz>T+Vg!7W7Uu*uFEX2{uQlYOuw` zKQoxm-s+L>;RFCv|NTQAH8jc$dA1pXj= zY|)_bLtV||Q^xYN+mi!)^BI@5iKZW2OwZRH>rHsFuVo2Oqnsa|Jk+$@kfSB*!f@w@ zP6z0(S3~%4$i^#nrn51-KSE1hPFUfpiUN-max@im>N=B5hJt)o7Atl5cN8q_OVACe*$qd~#k){n3&G3PzmM}F(Y?`%4sfos+s27v zLF85-SU^k(aWhg4qsc6j)m+EXkxG6&9-xa&9B_zwH6ECd%8Gsqu}Q}=*4qC_J&w6m@EiU?+z3mTYdxKbal+wo4YrcD%Shlq+!7;8;;*?8VxXC*ct%pz<}^fa&=jW-P8;BfGK;-WVnyOepi zg^pYa?rKPWiaq@oj>%vF9dLqvFrHue#89C#Tp8n*+=}&?_Jpq|CR#)%ndq2G*f(=m z@|}W#5y!|9WvR&piEYLL3njj$|6oJPX4aGM$#29O>QSTa9lk|ZJ2Zg1t4UNUxomki zzwCU8+OFY{QnxtIb-17bDSfY`(q6E#$)+RAhuy3Z(AB`MP^T=V-rtY}-x<6`1!7Jn zc8F%8o-kCG%$eI4%P&?CUaKzd3q$Y6#S6Su)m4rAGsQ6!`+FTmcoo-l&|spCi9xgf z@Yq_uXEktS1dhWuI{VgLOqTZQ-hFetS0z}*Ph(C?UDU55%Ui{)5iWy#M&@7rT!Y!P z%2$6V_&zPlVmVPA!Xmcn>jzOFlAieK3O))))IFj^ez7_s%-S=Z4Z`R&H%1y4XL{02 zVwKQ3o+sNiS{RW@Cylv_3oh2Qti-}v3PwMtf_pP|qNhT~vM3GvJD2KhOEC#F(j z3NM<7UR5>SvLUE{O`tQ=Vnf0pLXxu`jvm)>gq`U^p?`amU_ewIeixEpZ=^fX&i=+4 z=qou;#uVm^3Jd5<@5W62z6Ke(E*muEwYRt55Vq8nvK12~R$yW#au}07lK93a`0- zT{i(OilHAqrC8ly5|>hJnHY|iDwqgy(LeITC)2P?(TG7@&;W+(%B0;eWrf_7d=Vka znY@02(=Z~_cR#45rufnbXQ~|;EtGB$lI8h59sa;c_(L+BdR+jI7d)j%C1#~0N-8Ys(5r6|z2&+iDY2S_VCGlN93&4aC@D=w z&|P9whk3+@ZsR38@xvmt0DsI3PU(;~a_pmqutI^>9c>j25rS2!4~5SvO9XX}v0!HG zbncXNJ4S(8EwVlfNkO}!@)1Aj@1|>@(b`O`e?Z6x8q_JY;VvsxEE4n=<<;Zk*s|wF zeI+z5wPIK+Ju#F^Xj3wXLB8>95ar1QfRYck7P-Y_9NgwGzJe`;6SeRsmM|Fz@JM+2 zxp6!;(%j(c^uI1dE^k0|!21Sh0kP)eZ&q4YDIHfc6IXLVQ)lxJ0^nrl(1BKK(xeb`EA%7GD2<0mgRu+z$bOtd!E18VTdz{{cpayD|U( diff --git a/examples/PWA-example/public/logo_512.png b/examples/PWA-example/public/logo_512.png deleted file mode 100644 index 43129772a892a5ca82dfdc8bb4d9027cc830ddf5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 48151 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 Unavailable', { - status: 503, - statusText: 'Service Unavailable', - headers: new Headers({ - 'Content-Type': 'text/html' - }) - }); - } - }) - ); -}); - -/* The activate event fires after a service worker has been successfully installed. - It is most useful when phasing out an older version of a service worker, as at - this point you know that the new worker was installed correctly. In this example, - we delete old caches that don't match the version in the worker we just finished - installing. -*/ -self.addEventListener("activate", function (event) { - /* Just like with the install event, event.waitUntil blocks activate on a promise. - Activation will fail unless the promise is fulfilled. - */ - //console.log('WORKER: activate event in progress.'); - - event.waitUntil( - caches - /* This method returns a promise which will resolve to an array of available - cache keys. - */ - .keys() - .then(function (keys) { - // We return a promise that settles when all outdated caches are deleted. - return Promise.all( - keys - .filter(function (key) { - // Filter by keys that don't start with the latest version prefix. - return !key.startsWith(version); - }) - .map(function (key) { - /* Return a promise that's fulfilled - when each outdated cache is deleted. - */ - return caches.delete(key); - }) - ); - }) - .then(function () { - //console.log('WORKER: activate completed.'); - }) - ); -}); diff --git a/examples/PWA-example/src/main.rs b/examples/PWA-example/src/main.rs deleted file mode 100644 index 3e4d5b2361..0000000000 --- a/examples/PWA-example/src/main.rs +++ /dev/null @@ -1,21 +0,0 @@ -use dioxus::prelude::*; - -fn main() { - // init debug tool for WebAssembly - wasm_logger::init(wasm_logger::Config::default()); - console_error_panic_hook::set_once(); - - launch(app); -} - -fn app() -> Element { - rsx! ( - div { style: "text-align: center;", - h1 { "🌗 Dioxus 🚀" } - h3 { "Frontend that scales." } - p { - "Dioxus is a portable, performant, and ergonomic framework for building cross-platform user interfaces in Rust." - } - } - ) -} diff --git a/examples/all_events.rs b/examples/all_events.rs index 2a04ee2bb3..4e4ca3d6d5 100644 --- a/examples/all_events.rs +++ b/examples/all_events.rs @@ -6,10 +6,8 @@ use dioxus::prelude::*; use std::{collections::VecDeque, fmt::Debug, rc::Rc}; -const STYLE: &str = asset!("./examples/assets/events.css"); - fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { @@ -26,7 +24,7 @@ fn app() -> Element { }; rsx! { - head::Link { rel: "stylesheet", href: STYLE } + document::Stylesheet { href: asset!("/examples/assets/events.css") } div { id: "container", // focusing is necessary to catch keyboard events div { id: "receiver", tabindex: 0, diff --git a/examples/assets/purecss.css b/examples/assets/purecss.css new file mode 100644 index 0000000000..dd301cc364 --- /dev/null +++ b/examples/assets/purecss.css @@ -0,0 +1,12 @@ +/*! +Pure v2.0.6 +Copyright 2013 Yahoo! +Licensed under the BSD License. +https://github.com/pure-css/pure/blob/master/LICENSE +*/ +/*! +normalize.css v | MIT License | git.io/normalize +Copyright (c) Nicolas Gallagher and Jonathan Neal +*/ +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ +html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{-webkit-box-sizing:content-box;box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{-webkit-box-sizing:border-box;box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{-webkit-box-sizing:border-box;box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}html{font-family:sans-serif}.hidden,[hidden]{display:none!important}.pure-img{max-width:100%;height:auto;display:block}.pure-g{letter-spacing:-.31em;text-rendering:optimizespeed;font-family:FreeSans,Arimo,"Droid Sans",Helvetica,Arial,sans-serif;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-line-pack:start;align-content:flex-start}@media all and (-ms-high-contrast:none),(-ms-high-contrast:active){table .pure-g{display:block}}.opera-only :-o-prefocus,.pure-g{word-spacing:-0.43em}.pure-u{display:inline-block;letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-g [class*=pure-u]{font-family:sans-serif}.pure-u-1,.pure-u-1-1,.pure-u-1-12,.pure-u-1-2,.pure-u-1-24,.pure-u-1-3,.pure-u-1-4,.pure-u-1-5,.pure-u-1-6,.pure-u-1-8,.pure-u-10-24,.pure-u-11-12,.pure-u-11-24,.pure-u-12-24,.pure-u-13-24,.pure-u-14-24,.pure-u-15-24,.pure-u-16-24,.pure-u-17-24,.pure-u-18-24,.pure-u-19-24,.pure-u-2-24,.pure-u-2-3,.pure-u-2-5,.pure-u-20-24,.pure-u-21-24,.pure-u-22-24,.pure-u-23-24,.pure-u-24-24,.pure-u-3-24,.pure-u-3-4,.pure-u-3-5,.pure-u-3-8,.pure-u-4-24,.pure-u-4-5,.pure-u-5-12,.pure-u-5-24,.pure-u-5-5,.pure-u-5-6,.pure-u-5-8,.pure-u-6-24,.pure-u-7-12,.pure-u-7-24,.pure-u-7-8,.pure-u-8-24,.pure-u-9-24{display:inline-block;letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-u-1-24{width:4.1667%}.pure-u-1-12,.pure-u-2-24{width:8.3333%}.pure-u-1-8,.pure-u-3-24{width:12.5%}.pure-u-1-6,.pure-u-4-24{width:16.6667%}.pure-u-1-5{width:20%}.pure-u-5-24{width:20.8333%}.pure-u-1-4,.pure-u-6-24{width:25%}.pure-u-7-24{width:29.1667%}.pure-u-1-3,.pure-u-8-24{width:33.3333%}.pure-u-3-8,.pure-u-9-24{width:37.5%}.pure-u-2-5{width:40%}.pure-u-10-24,.pure-u-5-12{width:41.6667%}.pure-u-11-24{width:45.8333%}.pure-u-1-2,.pure-u-12-24{width:50%}.pure-u-13-24{width:54.1667%}.pure-u-14-24,.pure-u-7-12{width:58.3333%}.pure-u-3-5{width:60%}.pure-u-15-24,.pure-u-5-8{width:62.5%}.pure-u-16-24,.pure-u-2-3{width:66.6667%}.pure-u-17-24{width:70.8333%}.pure-u-18-24,.pure-u-3-4{width:75%}.pure-u-19-24{width:79.1667%}.pure-u-4-5{width:80%}.pure-u-20-24,.pure-u-5-6{width:83.3333%}.pure-u-21-24,.pure-u-7-8{width:87.5%}.pure-u-11-12,.pure-u-22-24{width:91.6667%}.pure-u-23-24{width:95.8333%}.pure-u-1,.pure-u-1-1,.pure-u-24-24,.pure-u-5-5{width:100%}.pure-button{display:inline-block;line-height:normal;white-space:nowrap;vertical-align:middle;text-align:center;cursor:pointer;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-box-sizing:border-box;box-sizing:border-box}.pure-button::-moz-focus-inner{padding:0;border:0}.pure-button-group{letter-spacing:-.31em;text-rendering:optimizespeed}.opera-only :-o-prefocus,.pure-button-group{word-spacing:-0.43em}.pure-button-group .pure-button{letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-button{font-family:inherit;font-size:100%;padding:.5em 1em;color:rgba(0,0,0,.8);border:none transparent;background-color:#e6e6e6;text-decoration:none;border-radius:2px}.pure-button-hover,.pure-button:focus,.pure-button:hover{background-image:-webkit-gradient(linear,left top,left bottom,from(transparent),color-stop(40%,rgba(0,0,0,.05)),to(rgba(0,0,0,.1)));background-image:linear-gradient(transparent,rgba(0,0,0,.05) 40%,rgba(0,0,0,.1))}.pure-button:focus{outline:0}.pure-button-active,.pure-button:active{-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.15) inset,0 0 6px rgba(0,0,0,.2) inset;box-shadow:0 0 0 1px rgba(0,0,0,.15) inset,0 0 6px rgba(0,0,0,.2) inset;border-color:#000}.pure-button-disabled,.pure-button-disabled:active,.pure-button-disabled:focus,.pure-button-disabled:hover,.pure-button[disabled]{border:none;background-image:none;opacity:.4;cursor:not-allowed;-webkit-box-shadow:none;box-shadow:none;pointer-events:none}.pure-button-hidden{display:none}.pure-button-primary,.pure-button-selected,a.pure-button-primary,a.pure-button-selected{background-color:#0078e7;color:#fff}.pure-button-group .pure-button{margin:0;border-radius:0;border-right:1px solid rgba(0,0,0,.2)}.pure-button-group .pure-button:first-child{border-top-left-radius:2px;border-bottom-left-radius:2px}.pure-button-group .pure-button:last-child{border-top-right-radius:2px;border-bottom-right-radius:2px;border-right:none}.pure-form input[type=color],.pure-form input[type=date],.pure-form input[type=datetime-local],.pure-form input[type=datetime],.pure-form input[type=email],.pure-form input[type=month],.pure-form input[type=number],.pure-form input[type=password],.pure-form input[type=search],.pure-form input[type=tel],.pure-form input[type=text],.pure-form input[type=time],.pure-form input[type=url],.pure-form input[type=week],.pure-form select,.pure-form textarea{padding:.5em .6em;display:inline-block;border:1px solid #ccc;-webkit-box-shadow:inset 0 1px 3px #ddd;box-shadow:inset 0 1px 3px #ddd;border-radius:4px;vertical-align:middle;-webkit-box-sizing:border-box;box-sizing:border-box}.pure-form input:not([type]){padding:.5em .6em;display:inline-block;border:1px solid #ccc;-webkit-box-shadow:inset 0 1px 3px #ddd;box-shadow:inset 0 1px 3px #ddd;border-radius:4px;-webkit-box-sizing:border-box;box-sizing:border-box}.pure-form input[type=color]{padding:.2em .5em}.pure-form input[type=color]:focus,.pure-form input[type=date]:focus,.pure-form input[type=datetime-local]:focus,.pure-form input[type=datetime]:focus,.pure-form input[type=email]:focus,.pure-form input[type=month]:focus,.pure-form input[type=number]:focus,.pure-form input[type=password]:focus,.pure-form input[type=search]:focus,.pure-form input[type=tel]:focus,.pure-form input[type=text]:focus,.pure-form input[type=time]:focus,.pure-form input[type=url]:focus,.pure-form input[type=week]:focus,.pure-form select:focus,.pure-form textarea:focus{outline:0;border-color:#129fea}.pure-form input:not([type]):focus{outline:0;border-color:#129fea}.pure-form input[type=checkbox]:focus,.pure-form input[type=file]:focus,.pure-form input[type=radio]:focus{outline:thin solid #129FEA;outline:1px auto #129FEA}.pure-form .pure-checkbox,.pure-form .pure-radio{margin:.5em 0;display:block}.pure-form input[type=color][disabled],.pure-form input[type=date][disabled],.pure-form input[type=datetime-local][disabled],.pure-form input[type=datetime][disabled],.pure-form input[type=email][disabled],.pure-form input[type=month][disabled],.pure-form input[type=number][disabled],.pure-form input[type=password][disabled],.pure-form input[type=search][disabled],.pure-form input[type=tel][disabled],.pure-form input[type=text][disabled],.pure-form input[type=time][disabled],.pure-form input[type=url][disabled],.pure-form input[type=week][disabled],.pure-form select[disabled],.pure-form textarea[disabled]{cursor:not-allowed;background-color:#eaeded;color:#cad2d3}.pure-form input:not([type])[disabled]{cursor:not-allowed;background-color:#eaeded;color:#cad2d3}.pure-form input[readonly],.pure-form select[readonly],.pure-form textarea[readonly]{background-color:#eee;color:#777;border-color:#ccc}.pure-form input:focus:invalid,.pure-form select:focus:invalid,.pure-form textarea:focus:invalid{color:#b94a48;border-color:#e9322d}.pure-form input[type=checkbox]:focus:invalid:focus,.pure-form input[type=file]:focus:invalid:focus,.pure-form input[type=radio]:focus:invalid:focus{outline-color:#e9322d}.pure-form select{height:2.25em;border:1px solid #ccc;background-color:#fff}.pure-form select[multiple]{height:auto}.pure-form label{margin:.5em 0 .2em}.pure-form fieldset{margin:0;padding:.35em 0 .75em;border:0}.pure-form legend{display:block;width:100%;padding:.3em 0;margin-bottom:.3em;color:#333;border-bottom:1px solid #e5e5e5}.pure-form-stacked input[type=color],.pure-form-stacked input[type=date],.pure-form-stacked input[type=datetime-local],.pure-form-stacked input[type=datetime],.pure-form-stacked input[type=email],.pure-form-stacked input[type=file],.pure-form-stacked input[type=month],.pure-form-stacked input[type=number],.pure-form-stacked input[type=password],.pure-form-stacked input[type=search],.pure-form-stacked input[type=tel],.pure-form-stacked input[type=text],.pure-form-stacked input[type=time],.pure-form-stacked input[type=url],.pure-form-stacked input[type=week],.pure-form-stacked label,.pure-form-stacked select,.pure-form-stacked textarea{display:block;margin:.25em 0}.pure-form-stacked input:not([type]){display:block;margin:.25em 0}.pure-form-aligned input,.pure-form-aligned select,.pure-form-aligned textarea,.pure-form-message-inline{display:inline-block;vertical-align:middle}.pure-form-aligned textarea{vertical-align:top}.pure-form-aligned .pure-control-group{margin-bottom:.5em}.pure-form-aligned .pure-control-group label{text-align:right;display:inline-block;vertical-align:middle;width:10em;margin:0 1em 0 0}.pure-form-aligned .pure-controls{margin:1.5em 0 0 11em}.pure-form .pure-input-rounded,.pure-form input.pure-input-rounded{border-radius:2em;padding:.5em 1em}.pure-form .pure-group fieldset{margin-bottom:10px}.pure-form .pure-group input,.pure-form .pure-group textarea{display:block;padding:10px;margin:0 0 -1px;border-radius:0;position:relative;top:-1px}.pure-form .pure-group input:focus,.pure-form .pure-group textarea:focus{z-index:3}.pure-form .pure-group input:first-child,.pure-form .pure-group textarea:first-child{top:1px;border-radius:4px 4px 0 0;margin:0}.pure-form .pure-group input:first-child:last-child,.pure-form .pure-group textarea:first-child:last-child{top:1px;border-radius:4px;margin:0}.pure-form .pure-group input:last-child,.pure-form .pure-group textarea:last-child{top:-2px;border-radius:0 0 4px 4px;margin:0}.pure-form .pure-group button{margin:.35em 0}.pure-form .pure-input-1{width:100%}.pure-form .pure-input-3-4{width:75%}.pure-form .pure-input-2-3{width:66%}.pure-form .pure-input-1-2{width:50%}.pure-form .pure-input-1-3{width:33%}.pure-form .pure-input-1-4{width:25%}.pure-form-message-inline{display:inline-block;padding-left:.3em;color:#666;vertical-align:middle;font-size:.875em}.pure-form-message{display:block;color:#666;font-size:.875em}@media only screen and (max-width :480px){.pure-form button[type=submit]{margin:.7em 0 0}.pure-form input:not([type]),.pure-form input[type=color],.pure-form input[type=date],.pure-form input[type=datetime-local],.pure-form input[type=datetime],.pure-form input[type=email],.pure-form input[type=month],.pure-form input[type=number],.pure-form input[type=password],.pure-form input[type=search],.pure-form input[type=tel],.pure-form input[type=text],.pure-form input[type=time],.pure-form input[type=url],.pure-form input[type=week],.pure-form label{margin-bottom:.3em;display:block}.pure-group input:not([type]),.pure-group input[type=color],.pure-group input[type=date],.pure-group input[type=datetime-local],.pure-group input[type=datetime],.pure-group input[type=email],.pure-group input[type=month],.pure-group input[type=number],.pure-group input[type=password],.pure-group input[type=search],.pure-group input[type=tel],.pure-group input[type=text],.pure-group input[type=time],.pure-group input[type=url],.pure-group input[type=week]{margin-bottom:0}.pure-form-aligned .pure-control-group label{margin-bottom:.3em;text-align:left;display:block;width:100%}.pure-form-aligned .pure-controls{margin:1.5em 0 0 0}.pure-form-message,.pure-form-message-inline{display:block;font-size:.75em;padding:.2em 0 .8em}}.pure-menu{-webkit-box-sizing:border-box;box-sizing:border-box}.pure-menu-fixed{position:fixed;left:0;top:0;z-index:3}.pure-menu-item,.pure-menu-list{position:relative}.pure-menu-list{list-style:none;margin:0;padding:0}.pure-menu-item{padding:0;margin:0;height:100%}.pure-menu-heading,.pure-menu-link{display:block;text-decoration:none;white-space:nowrap}.pure-menu-horizontal{width:100%;white-space:nowrap}.pure-menu-horizontal .pure-menu-list{display:inline-block}.pure-menu-horizontal .pure-menu-heading,.pure-menu-horizontal .pure-menu-item,.pure-menu-horizontal .pure-menu-separator{display:inline-block;vertical-align:middle}.pure-menu-item .pure-menu-item{display:block}.pure-menu-children{display:none;position:absolute;left:100%;top:0;margin:0;padding:0;z-index:3}.pure-menu-horizontal .pure-menu-children{left:0;top:auto;width:inherit}.pure-menu-active>.pure-menu-children,.pure-menu-allow-hover:hover>.pure-menu-children{display:block;position:absolute}.pure-menu-has-children>.pure-menu-link:after{padding-left:.5em;content:"\25B8";font-size:small}.pure-menu-horizontal .pure-menu-has-children>.pure-menu-link:after{content:"\25BE"}.pure-menu-scrollable{overflow-y:scroll;overflow-x:hidden}.pure-menu-scrollable .pure-menu-list{display:block}.pure-menu-horizontal.pure-menu-scrollable .pure-menu-list{display:inline-block}.pure-menu-horizontal.pure-menu-scrollable{white-space:nowrap;overflow-y:hidden;overflow-x:auto;padding:.5em 0}.pure-menu-horizontal .pure-menu-children .pure-menu-separator,.pure-menu-separator{background-color:#ccc;height:1px;margin:.3em 0}.pure-menu-horizontal .pure-menu-separator{width:1px;height:1.3em;margin:0 .3em}.pure-menu-horizontal .pure-menu-children .pure-menu-separator{display:block;width:auto}.pure-menu-heading{text-transform:uppercase;color:#565d64}.pure-menu-link{color:#777}.pure-menu-children{background-color:#fff}.pure-menu-heading,.pure-menu-link{padding:.5em 1em}.pure-menu-disabled{opacity:.5}.pure-menu-disabled .pure-menu-link:hover{background-color:transparent;cursor:default}.pure-menu-active>.pure-menu-link,.pure-menu-link:focus,.pure-menu-link:hover{background-color:#eee}.pure-menu-selected>.pure-menu-link,.pure-menu-selected>.pure-menu-link:visited{color:#000}.pure-table{border-collapse:collapse;border-spacing:0;empty-cells:show;border:1px solid #cbcbcb}.pure-table caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.pure-table td,.pure-table th{border-left:1px solid #cbcbcb;border-width:0 0 0 1px;font-size:inherit;margin:0;overflow:visible;padding:.5em 1em}.pure-table thead{background-color:#e0e0e0;color:#000;text-align:left;vertical-align:bottom}.pure-table td{background-color:transparent}.pure-table-odd td{background-color:#f2f2f2}.pure-table-striped tr:nth-child(2n-1) td{background-color:#f2f2f2}.pure-table-bordered td{border-bottom:1px solid #cbcbcb}.pure-table-bordered tbody>tr:last-child>td{border-bottom-width:0}.pure-table-horizontal td,.pure-table-horizontal th{border-width:0 0 1px 0;border-bottom:1px solid #cbcbcb}.pure-table-horizontal tbody>tr:last-child>td{border-bottom-width:0} diff --git a/examples/backgrounded_futures.rs b/examples/backgrounded_futures.rs index 4cafad439e..4fabef09f7 100644 --- a/examples/backgrounded_futures.rs +++ b/examples/backgrounded_futures.rs @@ -11,7 +11,7 @@ use async_std::task::sleep; use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/calculator.rs b/examples/calculator.rs index 9bf2a04618..b373923f6b 100644 --- a/examples/calculator.rs +++ b/examples/calculator.rs @@ -12,10 +12,10 @@ use dioxus::events::*; use dioxus::html::input_data::keyboard_types::Key; use dioxus::prelude::*; -const STYLE: &str = asset!("./examples/assets/calculator.css"); +const STYLE: Asset = asset!("/examples/assets/calculator.css"); fn main() { - LaunchBuilder::desktop() + dioxus::launch::builder() .with_cfg(desktop!({ use dioxus::desktop::{Config, LogicalSize, WindowBuilder}; Config::new().with_window( @@ -54,7 +54,7 @@ fn app() -> Element { }; rsx! { - head::Link { rel: "stylesheet", href: STYLE } + document::Stylesheet { href: STYLE } div { id: "wrapper", div { class: "app", div { class: "calculator", tabindex: "0", onkeydown: handle_key_down_event, diff --git a/examples/calculator_mutable.rs b/examples/calculator_mutable.rs index 2f933ebbea..9d1ec05fbc 100644 --- a/examples/calculator_mutable.rs +++ b/examples/calculator_mutable.rs @@ -13,7 +13,7 @@ use dioxus::html::MouseEvent; use dioxus::prelude::*; fn main() { - LaunchBuilder::desktop() + dioxus::launch::builder() .with_cfg( Config::new().with_window( WindowBuilder::new() @@ -29,7 +29,7 @@ fn app() -> Element { let mut state = use_signal(Calculator::new); rsx! { - head::Link { rel: "stylesheet", href: asset!("./examples/assets/calculator.css") } + document::Stylesheet { href: asset!("/examples/assets/calculator.css") } div { id: "wrapper", div { class: "app", div { diff --git a/examples/clock.rs b/examples/clock.rs index dc168a5ec2..d344311fae 100644 --- a/examples/clock.rs +++ b/examples/clock.rs @@ -5,18 +5,18 @@ use async_std::task::sleep; use dioxus::prelude::*; use web_time::Instant; -const STYLE: &str = asset!("./examples/assets/clock.css"); +const STYLE: Asset = asset!("/examples/assets/clock.css"); fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { let mut millis = use_signal(|| 0); use_future(move || async move { - // Save our initial timea - let start = Instant::now(); + // Save our initial time + let start = std::time::Instant::now(); loop { sleep(std::time::Duration::from_millis(27)).await; @@ -36,7 +36,7 @@ fn app() -> Element { ); rsx! { - head::Link { rel: "stylesheet", href: STYLE } + document::Stylesheet { href: STYLE } div { id: "app", div { id: "title", "Carpe diem 🎉" } div { id: "clock-display", "{time}" } diff --git a/examples/control_focus.rs b/examples/control_focus.rs index c69293c56d..6a383fa662 100644 --- a/examples/control_focus.rs +++ b/examples/control_focus.rs @@ -5,13 +5,8 @@ use std::rc::Rc; -use async_std::task::sleep; -use dioxus::prelude::*; - -const STYLE: &str = asset!("./examples/assets/roulette.css"); - fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { @@ -40,7 +35,7 @@ fn app() -> Element { }); rsx! { - head::Link { rel: "stylesheet", href: STYLE } + document::Stylesheet { href: asset!("/examples/assets/roulette.css") } h1 { "Input Roulette" } button { onclick: move |_| running.toggle(), "Toggle roulette" } div { id: "roulette-grid", diff --git a/examples/counters.rs b/examples/counters.rs index f2d8c4203c..63013a1cc5 100644 --- a/examples/counters.rs +++ b/examples/counters.rs @@ -2,10 +2,8 @@ use dioxus::prelude::*; -const STYLE: &str = asset!("./examples/assets/counter.css"); - fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { @@ -16,7 +14,7 @@ fn app() -> Element { let sum = use_memo(move || counters.read().iter().copied().sum::()); rsx! { - head::Link { rel: "stylesheet", href: STYLE } + document::Stylesheet { href: asset!("/examples/assets/counter.css") } div { id: "controls", button { onclick: move |_| counters.write().push(0), "Add counter" } diff --git a/examples/crm.rs b/examples/crm.rs index 21f07466cd..ca83f58d40 100644 --- a/examples/crm.rs +++ b/examples/crm.rs @@ -12,7 +12,7 @@ use dioxus::prelude::*; fn main() { - LaunchBuilder::new() + dioxus::builder() .with_cfg(desktop!({ use dioxus::desktop::{LogicalSize, WindowBuilder}; dioxus::desktop::Config::default() @@ -20,13 +20,8 @@ fn main() { })) .launch(|| { rsx! { - head::Link { - rel: "stylesheet", - href: asset!("https://unpkg.com/purecss@2.0.6/build/pure-min.css"), - integrity: "sha384-Uu6IeWbM+gzNVXJcM9XV3SohHtmWE+3VGi496jvgX1jyvDTXfdK+rfZc8C1Aehk5", - crossorigin: "anonymous" - } - head::Link { rel: "stylesheet", href: asset!("./examples/assets/crm.css") } + document::Stylesheet { href: asset!("/examples/assets/crm.css") } + document::Link { href: asset!("/examples/assets/purecss.css") } h1 { "Dioxus CRM Example" } Router:: {} } diff --git a/examples/custom_assets.rs b/examples/custom_assets.rs index 6c927d21a4..8a56530110 100644 --- a/examples/custom_assets.rs +++ b/examples/custom_assets.rs @@ -1,27 +1,37 @@ //! A simple example on how to use assets loading from the filesystem. //! -//! If the feature "collect-assets" is enabled, the assets will be collected via the dioxus CLI and embedded into the -//! final bundle. This lets you do various useful things like minify, compress, and optimize your assets. +//! Dioxus provides an asset!() macro which properly handles asset loading and bundling for you. +//! For bundling, asset!() must be paired with a tool that handles mangansis-link sections. The dioxus-cli +//! handles this for you, but this means you can't just simply `cargo build --release` to build and +//! distribute your app. //! -//! We can still use assets without the CLI middleware, but generally larger apps will benefit from it. - +//! You can run this example with `cargo run --example assets` or `dx serve --example assets`. +//! When manganis is not active, the asset!() macro will fallback to the path of the asset on +//! your filesystem. use dioxus::prelude::*; -#[cfg(not(feature = "collect-assets"))] -static ASSET_PATH: &str = "examples/assets/logo.png"; - -#[cfg(feature = "collect-assets")] -static ASSET_PATH: &str = asset!("examples/assets/logo.png".format(ImageType::Avif)); +/// asset!() will mark this asset as a dependency of the app without actually including it in the +/// generated code. This is better than include_str!() or include_bytes!() since it works +/// for web apps as well as native and mobile apps. +/// +/// When used with web apps, manganis will detect the import of the image, optimize it, and put it +/// in the output dist folder in the right location, ensuring no two images have the same name. +static IMAGE: ImageAsset = asset!("/examples/assets/logo.png".image().format(ImageType::Avif)); fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { rsx! { div { h1 { "This should show an image:" } - img { src: ASSET_PATH.to_string() } + img { src: IMAGE } + + // temporarily keep support for these too + img { src: "/Users/jonkelley/Development/dioxus/examples/assets/logo.png" } + img { src: "/examples/assets/logo.png" } + img { src: "examples/assets/logo.png" } } } } diff --git a/examples/custom_html.rs b/examples/custom_html.rs index dbbe334f05..f1ea4b91ad 100644 --- a/examples/custom_html.rs +++ b/examples/custom_html.rs @@ -4,7 +4,7 @@ use dioxus::prelude::*; fn main() { - LaunchBuilder::new() + dioxus::launch::builder() .with_cfg( dioxus::desktop::Config::new().with_custom_index( r#" diff --git a/examples/custom_menu.rs b/examples/custom_menu.rs index f78f33c884..da1085bfd5 100644 --- a/examples/custom_menu.rs +++ b/examples/custom_menu.rs @@ -28,7 +28,7 @@ fn main() { let config = dioxus::desktop::Config::new().with_menu(menu); // Launch the app with the custom menu - LaunchBuilder::new().with_cfg(config).launch(app) + dioxus::launch::builder().with_cfg(config).launch(app) } fn app() -> Element { diff --git a/examples/disabled.rs b/examples/disabled.rs index 6aaa70b9d4..b48b1a912e 100644 --- a/examples/disabled.rs +++ b/examples/disabled.rs @@ -5,7 +5,7 @@ use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/dog_app.rs b/examples/dog_app.rs index 1c53c8731f..09bf5f0a96 100644 --- a/examples/dog_app.rs +++ b/examples/dog_app.rs @@ -11,7 +11,7 @@ use dioxus::prelude::*; use std::collections::HashMap; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/dynamic_asset.rs b/examples/dynamic_asset.rs index 14a33c4809..b741ba57af 100644 --- a/examples/dynamic_asset.rs +++ b/examples/dynamic_asset.rs @@ -7,10 +7,8 @@ use dioxus::desktop::{use_asset_handler, wry::http::Response}; use dioxus::prelude::*; -const STYLE: &str = asset!("./examples/assets/custom_assets.css"); - fn main() { - launch_desktop(app); + dioxus::launch(app); } fn app() -> Element { @@ -24,7 +22,7 @@ fn app() -> Element { }); rsx! { - head::Link { rel: "stylesheet", href: STYLE } + document::Stylesheet { href: asset!("/examples/assets/custom_assets.css") } h1 { "Dynamic Assets" } img { src: "/logos/logo.png" } } diff --git a/examples/errors.rs b/examples/errors.rs index 4727cb5e03..fe16598e95 100644 --- a/examples/errors.rs +++ b/examples/errors.rs @@ -11,7 +11,11 @@ use dioxus::prelude::*; fn main() { - launch(|| rsx! { Router:: {} }); + dioxus::launch(|| { + rsx! { + Router:: {} + } + }); } /// You can use an ErrorBoundary to catch errors in children and display a warning @@ -34,11 +38,8 @@ fn ParseNumber() -> Element { h1 { "Error handler demo" } button { onclick: move |_| { - // You can return a result from an event handler which lets you easily quit rendering early if something fails let data: i32 = "0.5".parse()?; - println!("parsed {data}"); - Ok(()) }, "Click to throw an error" @@ -58,10 +59,7 @@ fn Show() -> Element { if let Some(error) = error.show() { {error} } else { - pre { - color: "red", - "{error}" - } + pre { color: "red", "{error}" } } } } @@ -88,15 +86,10 @@ fn ParseNumberWithShow() -> Element { border_width: "2px", border_radius: "5px", p { "Failed to parse data" } - Link { - to: Route::Home {}, - "Go back to the homepage" - } + Link { to: Route::Home {}, "Go back to the homepage" } } })?; - println!("parsed {data}"); - Ok(()) }, "Click to throw an error" @@ -139,22 +132,13 @@ fn Home() -> Element { rsx! { ul { li { - Link { - to: Route::Simple {}, - "Simple errors" - } + Link { to: Route::Simple {}, "Simple errors" } } li { - Link { - to: Route::Panic {}, - "Capture panics" - } + Link { to: Route::Panic {}, "Capture panics" } } li { - Link { - to: Route::Show {}, - "Show errors" - } + Link { to: Route::Show {}, "Show errors" } } } } diff --git a/examples/eval.rs b/examples/eval.rs index 9cfd15e941..68d26c8b09 100644 --- a/examples/eval.rs +++ b/examples/eval.rs @@ -7,7 +7,7 @@ use async_std::task::sleep; use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { @@ -19,29 +19,24 @@ fn app() -> Element { // The `eval` is available in the prelude - and simply takes a block of JS. // Dioxus' eval is interesting since it allows sending messages to and from the JS code using the `await dioxus.recv()` // builtin function. This allows you to create a two-way communication channel between Rust and JS. - let mut eval = eval( + let mut eval = document::eval( r#" - dioxus.send("Hi from JS!"); - let msg = await dioxus.recv(); - console.log(msg); - return "hi from JS!"; + return "hi from JS!"; "#, ); - // Send a message to the JS code. - eval.send("Hi from Rust!".into()).unwrap(); - - // Our line on the JS side will log the message and then return "hello world". - let res = eval.recv().await.unwrap(); - // This will print "Hi from JS!" and "Hi from Rust!". - println!("{:?}", eval.await); + let res = eval.await; + + println!("hello from js! {:?}", res); res }); - match future.value().as_ref() { - Some(v) => rsx!( p { "{v}" } ), - _ => rsx!( p { "waiting.." } ), - } + todo!() + // future.read_unchecked().as_ref().map(|f| match f { + // Some(Ok(v)) => rsx!( p { "{v:?}" } ), + // Some(Err(e)) => rsx!( p { "{v:?}" } ), + // None => rsx!( p { "waiting.." } ), + // }) } diff --git a/examples/file_explorer.rs b/examples/file_explorer.rs index 1ac571d2e8..c5d5f31756 100644 --- a/examples/file_explorer.rs +++ b/examples/file_explorer.rs @@ -9,7 +9,7 @@ use dioxus::desktop::{Config, WindowBuilder}; use dioxus::prelude::*; fn main() { - LaunchBuilder::desktop() + dioxus::launch::builder() .with_cfg(Config::new().with_window(WindowBuilder::new().with_resizable(true))) .launch(app) } @@ -18,12 +18,12 @@ fn app() -> Element { let mut files = use_signal(Files::new); rsx! { - head::Link { + document::Link { rel: "stylesheet", - href: asset!("./examples/assets/fileexplorer.css") + href: asset!("/examples/assets/fileexplorer.css") } div { - head::Link { href: "https://fonts.googleapis.com/icon?family=Material+Icons", rel: "stylesheet" } + document::Link { href: "https://fonts.googleapis.com/icon?family=Material+Icons", rel: "stylesheet" } header { i { class: "material-icons icon-menu", "menu" } h1 { "Files: " {files.read().current()} } diff --git a/examples/file_upload.rs b/examples/file_upload.rs index 9fb74072bb..ffc1858238 100644 --- a/examples/file_upload.rs +++ b/examples/file_upload.rs @@ -8,10 +8,10 @@ use std::sync::Arc; use dioxus::prelude::*; use dioxus::{html::HasFileData, prelude::dioxus_elements::FileEngine}; -const STYLE: &str = asset!("./examples/assets/file_upload.css"); +const STYLE: Asset = asset!("/examples/assets/file_upload.css"); fn main() { - launch(app); + dioxus::launch(app); } struct UploadedFile { @@ -43,7 +43,7 @@ fn app() -> Element { }; rsx! { - head::Link { rel: "stylesheet", href: STYLE } + document::Stylesheet { href: STYLE } h1 { "File Upload Example" } p { "Drop a .txt, .rs, or .js file here to read it" } diff --git a/examples/flat_router.rs b/examples/flat_router.rs index 79a9d2a3f2..d537ee4c1b 100644 --- a/examples/flat_router.rs +++ b/examples/flat_router.rs @@ -9,12 +9,12 @@ use dioxus::prelude::*; -const STYLE: &str = asset!("./examples/assets/flat_router.css"); +const STYLE: Asset = asset!("/examples/assets/flat_router.css"); fn main() { - launch(|| { + dioxus::launch(|| { rsx! { - head::Link { rel: "stylesheet", href: STYLE } + document::Stylesheet { href: STYLE } Router:: {} } }) diff --git a/examples/form.rs b/examples/form.rs index 91b09ecd34..ff6154b9d4 100644 --- a/examples/form.rs +++ b/examples/form.rs @@ -7,7 +7,7 @@ use dioxus::prelude::*; use std::collections::HashMap; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/future.rs b/examples/future.rs index f4ddc10468..e32386fe83 100644 --- a/examples/future.rs +++ b/examples/future.rs @@ -7,7 +7,7 @@ use async_std::task::sleep; use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/generic_component.rs b/examples/generic_component.rs index 6af34e542b..520f257787 100644 --- a/examples/generic_component.rs +++ b/examples/generic_component.rs @@ -7,7 +7,7 @@ use dioxus::prelude::*; use std::fmt::Display; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/global.rs b/examples/global.rs index 0a08485b02..693b912e93 100644 --- a/examples/global.rs +++ b/examples/global.rs @@ -7,18 +7,18 @@ use dioxus::prelude::*; -const STYLE: &str = asset!("./examples/assets/counter.css"); +const STYLE: Asset = asset!("/examples/assets/counter.css"); static COUNT: GlobalSignal = Signal::global(|| 0); static DOUBLED_COUNT: GlobalMemo = Memo::global(|| COUNT() * 2); fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { rsx! { - head::Link { rel: "stylesheet", href: STYLE } + document::Stylesheet { href: STYLE } Increment {} Decrement {} Reset {} diff --git a/examples/hash_fragment_state.rs b/examples/hash_fragment_state.rs index ed77c8c46e..bc0ec2cc88 100644 --- a/examples/hash_fragment_state.rs +++ b/examples/hash_fragment_state.rs @@ -2,7 +2,7 @@ //! //! You can set up two way data binding between the url hash and signals. //! -//! Run this example on desktop with +//! Run this example on desktop with //! ```sh //! dx serve --example hash_fragment_state --features=ciborium,base64 //! ``` @@ -19,7 +19,7 @@ use dioxus::prelude::*; use serde::{Deserialize, Serialize}; fn main() { - launch(|| { + dioxus::launch(|| { rsx! { Router:: {} } diff --git a/examples/hello_world.rs b/examples/hello_world.rs index ee33c22309..20c9af934a 100644 --- a/examples/hello_world.rs +++ b/examples/hello_world.rs @@ -12,7 +12,7 @@ use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/hydration.rs b/examples/hydration.rs index 1e7de2706f..cffacc86aa 100644 --- a/examples/hydration.rs +++ b/examples/hydration.rs @@ -13,7 +13,7 @@ use dioxus::desktop::Config; use dioxus::prelude::*; fn main() { - LaunchBuilder::desktop() + dioxus::launch::builder() .with_cfg(Config::new().with_prerendered({ // We build the dom a first time, then pre-render it to HTML let pre_rendered_dom = VirtualDom::prebuilt(app); diff --git a/examples/image_generator_openai.rs b/examples/image_generator_openai.rs index 474c46de90..67c743c427 100644 --- a/examples/image_generator_openai.rs +++ b/examples/image_generator_openai.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; use serde_json::{json, Error}; fn main() { - launch(app) + dioxus::launch(app) } fn app() -> Element { @@ -36,7 +36,7 @@ fn app() -> Element { }); rsx! { - head::Link { rel: "stylesheet", href: "https://unpkg.com/bulma@0.9.0/css/bulma.min.css" } + document::Stylesheet { href: "https://unpkg.com/bulma@0.9.0/css/bulma.min.css" } div { class: "container", div { class: "columns", div { class: "column", diff --git a/examples/link.rs b/examples/link.rs index ff29b65626..7d1f4be609 100644 --- a/examples/link.rs +++ b/examples/link.rs @@ -8,15 +8,15 @@ use dioxus::prelude::*; -const STYLE: &str = asset!("./examples/assets/links.css"); +const STYLE: Asset = asset!("/examples/assets/links.css"); fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { rsx! ( - head::Link { rel: "stylesheet", href: STYLE } + document::Stylesheet { href: STYLE } Router:: {} ) } diff --git a/examples/login_form.rs b/examples/login_form.rs index 39e874cc99..617723466c 100644 --- a/examples/login_form.rs +++ b/examples/login_form.rs @@ -9,7 +9,7 @@ use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/memo_chain.rs b/examples/memo_chain.rs index 205fe87693..ab57089397 100644 --- a/examples/memo_chain.rs +++ b/examples/memo_chain.rs @@ -6,7 +6,7 @@ use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/meta.rs b/examples/meta.rs index fae916258f..9d8e1a4ae0 100644 --- a/examples/meta.rs +++ b/examples/meta.rs @@ -3,8 +3,7 @@ use dioxus::prelude::*; fn main() { - tracing_subscriber::fmt::init(); - launch(app); + dioxus::launch(app); } fn app() -> Element { @@ -12,23 +11,23 @@ fn app() -> Element { // You can use the Meta component to render a meta tag into the head of the page // Meta tags are useful to provide information about the page to search engines and social media sites // This example sets up meta tags for the open graph protocol for social media previews - Meta { + document::Meta { property: "og:title", content: "My Site", } - Meta { + document::Meta { property: "og:type", content: "website", } - Meta { + document::Meta { property: "og:url", content: "https://www.example.com", } - Meta { + document::Meta { property: "og:image", content: "https://example.com/image.jpg", } - Meta { + document::Meta { name: "description", content: "My Site is a site", } diff --git a/examples/mobile_demo/.gitignore b/examples/mobile_demo/.gitignore deleted file mode 100644 index e1e084c4bb..0000000000 --- a/examples/mobile_demo/.gitignore +++ /dev/null @@ -1,10 +0,0 @@ -# Rust -target/ -**/*.rs.bk - -# cargo-mobile2 -.cargo/ -/gen - -# macOS -.DS_Store diff --git a/examples/mobile_demo/Cargo.lock b/examples/mobile_demo/Cargo.lock deleted file mode 100644 index 7f50292263..0000000000 --- a/examples/mobile_demo/Cargo.lock +++ /dev/null @@ -1,4007 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "addr2line" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "ahash" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy", -] - -[[package]] -name = "aho-corasick" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" -dependencies = [ - "memchr", -] - -[[package]] -name = "allocator-api2" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" - -[[package]] -name = "android_log-sys" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85965b6739a430150bdd138e2374a98af0c3ee0d030b3bb7fc3bddff58d0102e" - -[[package]] -name = "android_logger" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ec2333c185d826313162cee39d3fcc6a84ba08114a839bebf53b961e7e75773" -dependencies = [ - "android_log-sys", - "env_logger 0.7.1", - "lazy_static", - "log", -] - -[[package]] -name = "anyhow" -version = "1.0.72" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" - -[[package]] -name = "async-channel" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" -dependencies = [ - "concurrent-queue", - "event-listener", - "futures-core", -] - -[[package]] -name = "async-lock" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa24f727524730b077666307f2734b4a1a1c57acb79193127dcc8914d5242dd7" -dependencies = [ - "event-listener", -] - -[[package]] -name = "async-task" -version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae" - -[[package]] -name = "async-trait" -version = "0.1.72" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "atk" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4af014b17dd80e8af9fa689b2d4a211ddba6eb583c1622f35d0cb543f6b17e4" -dependencies = [ - "atk-sys", - "glib", - "libc", -] - -[[package]] -name = "atk-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "251e0b7d90e33e0ba930891a505a9a35ece37b2dd37a14f3ffc306c13b980009" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "atomic-waker" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3" - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "backtrace" -version = "0.3.68" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - -[[package]] -name = "base64" -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" -dependencies = [ - "serde", -] - -[[package]] -name = "block" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "blocking" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77231a1c8f801696fc0123ec6150ce92cffb8e164a02afb9c8ddee0e9b65ad65" -dependencies = [ - "async-channel", - "async-lock", - "async-task", - "atomic-waker", - "fastrand", - "futures-lite", - "log", -] - -[[package]] -name = "bumpalo" -version = "3.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" - -[[package]] -name = "bytemuck" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "bytes" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" - -[[package]] -name = "cairo-rs" -version = "0.18.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" -dependencies = [ - "bitflags 2.4.2", - "cairo-sys-rs", - "glib", - "libc", - "once_cell", - "thiserror", -] - -[[package]] -name = "cairo-sys-rs" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" -dependencies = [ - "glib-sys", - "libc", - "system-deps", -] - -[[package]] -name = "cc" -version = "1.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" - -[[package]] -name = "cesu8" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" - -[[package]] -name = "cfb" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" -dependencies = [ - "byteorder", - "fnv", - "uuid", -] - -[[package]] -name = "cfg-expr" -version = "0.15.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b40ccee03b5175c18cde8f37e7d2a33bcef6f8ec8f7cc0d81090d1bb380949c9" -dependencies = [ - "smallvec", - "target-lexicon", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "cfg_aliases" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" - -[[package]] -name = "ciborium" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" -dependencies = [ - "ciborium-io", - "ciborium-ll", - "serde", -] - -[[package]] -name = "ciborium-io" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" - -[[package]] -name = "ciborium-ll" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" -dependencies = [ - "ciborium-io", - "half", -] - -[[package]] -name = "cocoa" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" -dependencies = [ - "bitflags 1.3.2", - "block", - "cocoa-foundation", - "core-foundation", - "core-graphics", - "foreign-types 0.5.0", - "libc", - "objc", -] - -[[package]] -name = "cocoa-foundation" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "931d3837c286f56e3c58423ce4eba12d08db2374461a785c86f672b08b5650d6" -dependencies = [ - "bitflags 1.3.2", - "block", - "core-foundation", - "core-graphics-types", - "foreign-types 0.3.2", - "libc", - "objc", -] - -[[package]] -name = "color_quant" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" - -[[package]] -name = "combine" -version = "4.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" -dependencies = [ - "bytes", - "memchr", -] - -[[package]] -name = "concurrent-queue" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "const_format" -version = "0.2.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3a214c7af3d04997541b18d432afaff4c455e79e2029079647e72fc2bd27673" -dependencies = [ - "const_format_proc_macros", -] - -[[package]] -name = "const_format_proc_macros" -version = "0.2.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - -[[package]] -name = "constcat" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd7e35aee659887cbfb97aaf227ac12cad1a9d7c71e55ff3376839ed4e282d08" - -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - -[[package]] -name = "convert_case" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "core-foundation" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" - -[[package]] -name = "core-graphics" -version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "970a29baf4110c26fedbc7f82107d42c23f7e88e404c4577ed73fe99ff85a212" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "core-graphics-types", - "foreign-types 0.5.0", - "libc", -] - -[[package]] -name = "core-graphics-types" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bb142d41022986c1d8ff29103a1411c8a3dfad3552f87a4f8dc50d61d4f4e33" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "libc", -] - -[[package]] -name = "cpufeatures" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" -dependencies = [ - "libc", -] - -[[package]] -name = "crc32fast" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[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 = "cssparser" -version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a" -dependencies = [ - "cssparser-macros", - "dtoa-short", - "itoa 0.4.8", - "matches", - "phf 0.8.0", - "proc-macro2", - "quote", - "smallvec", - "syn 1.0.109", -] - -[[package]] -name = "cssparser-macros" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" -dependencies = [ - "quote", - "syn 2.0.52", -] - -[[package]] -name = "darling" -version = "0.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "darling_macro" -version = "0.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" -dependencies = [ - "darling_core", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "dashmap" -version = "5.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" -dependencies = [ - "cfg-if", - "hashbrown 0.14.0", - "lock_api", - "once_cell", - "parking_lot_core", -] - -[[package]] -name = "derive_more" -version = "0.99.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" -dependencies = [ - "convert_case 0.4.0", - "proc-macro2", - "quote", - "rustc_version", - "syn 1.0.109", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", -] - -[[package]] -name = "dioxus" -version = "0.5.0-alpha.0" -dependencies = [ - "dioxus-config-macro", - "dioxus-core", - "dioxus-core-macro", - "dioxus-desktop", - "dioxus-fullstack", - "dioxus-hooks", - "dioxus-hot-reload", - "dioxus-html", - "dioxus-mobile", - "dioxus-signals", -] - -[[package]] -name = "dioxus-cli-config" -version = "0.5.0-alpha.0" -dependencies = [ - "once_cell", - "serde", - "serde_json", - "tracing", -] - -[[package]] -name = "dioxus-config-macro" -version = "0.5.0-alpha.0" -dependencies = [ - "proc-macro2", - "quote", -] - -[[package]] -name = "dioxus-core" -version = "0.5.0-alpha.0" -dependencies = [ - "futures-channel", - "futures-util", - "longest-increasing-subsequence", - "rustc-hash", - "serde", - "slab", - "tracing", - "tracing-subscriber", -] - -[[package]] -name = "dioxus-core-macro" -version = "0.5.0-alpha.0" -dependencies = [ - "constcat", - "convert_case 0.6.0", - "dioxus-rsx", - "prettyplease", - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "dioxus-debug-cell" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ea539174bb236e0e7dc9c12b19b88eae3cb574dedbd0252a2d43ea7e6de13e2" - -[[package]] -name = "dioxus-desktop" -version = "0.5.0-alpha.0" -dependencies = [ - "async-trait", - "core-foundation", - "dioxus-cli-config", - "dioxus-core", - "dioxus-hooks", - "dioxus-hot-reload", - "dioxus-html", - "dioxus-interpreter-js", - "dunce", - "futures-channel", - "futures-util", - "generational-box", - "global-hotkey", - "infer", - "muda", - "objc", - "objc_id", - "rfd", - "rustc-hash", - "serde", - "serde_json", - "slab", - "tao", - "thiserror", - "tokio", - "tracing", - "urlencoding", - "webbrowser", - "wry 0.37.0", -] - -[[package]] -name = "dioxus-fullstack" -version = "0.5.0-alpha.0" -dependencies = [ - "async-trait", - "base64", - "bytes", - "ciborium", - "dioxus-hot-reload", - "dioxus-lib", - "dioxus-mobile", - "dioxus_server_macro", - "futures-util", - "once_cell", - "serde", - "serde_json", - "server_fn", - "tracing", -] - -[[package]] -name = "dioxus-hooks" -version = "0.5.0-alpha.0" -dependencies = [ - "dioxus-core", - "dioxus-debug-cell", - "dioxus-signals", - "futures-channel", - "futures-util", - "generational-box", - "slab", - "thiserror", - "tracing", -] - -[[package]] -name = "dioxus-hot-reload" -version = "0.5.0-alpha.0" -dependencies = [ - "dioxus-core", - "dioxus-html", - "dioxus-rsx", - "interprocess", - "serde", - "serde_json", -] - -[[package]] -name = "dioxus-html" -version = "0.5.0-alpha.0" -dependencies = [ - "async-trait", - "dioxus-core", - "dioxus-html-internal-macro", - "enumset", - "euclid", - "futures-channel", - "generational-box", - "keyboard-types", - "serde", - "serde-value", - "serde_json", - "serde_repr", - "tokio", - "web-sys", -] - -[[package]] -name = "dioxus-html-internal-macro" -version = "0.5.0-alpha.0" -dependencies = [ - "convert_case 0.6.0", - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "dioxus-interpreter-js" -version = "0.5.0-alpha.0" -dependencies = [ - "dioxus-core", - "dioxus-html", - "md5", - "sledgehammer_bindgen", - "sledgehammer_utils", -] - -[[package]] -name = "dioxus-lib" -version = "0.5.0-alpha.0" -dependencies = [ - "dioxus-core", - "dioxus-core-macro", - "dioxus-hooks", - "dioxus-html", - "dioxus-rsx", - "dioxus-signals", -] - -[[package]] -name = "dioxus-mobile" -version = "0.5.0-alpha.0" -dependencies = [ - "dioxus-desktop", -] - -[[package]] -name = "dioxus-rsx" -version = "0.5.0-alpha.0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", - "tracing", -] - -[[package]] -name = "dioxus-signals" -version = "0.5.0-alpha.0" -dependencies = [ - "dioxus-core", - "futures-channel", - "futures-util", - "generational-box", - "once_cell", - "parking_lot", - "rustc-hash", - "tracing", -] - -[[package]] -name = "dioxus_server_macro" -version = "0.5.0-alpha.0" -dependencies = [ - "convert_case 0.6.0", - "proc-macro2", - "quote", - "server_fn_macro", - "syn 2.0.52", -] - -[[package]] -name = "dispatch" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" - -[[package]] -name = "dlopen2" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1297103d2bbaea85724fcee6294c2d50b1081f9ad47d0f6f6f61eda65315a6" -dependencies = [ - "dlopen2_derive", - "libc", - "once_cell", - "winapi", -] - -[[package]] -name = "dlopen2_derive" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "dtoa" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" - -[[package]] -name = "dtoa-short" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbaceec3c6e4211c79e7b1800fb9680527106beb2f9c51904a3210c03a448c74" -dependencies = [ - "dtoa", -] - -[[package]] -name = "dunce" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" - -[[package]] -name = "enumset" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e875f1719c16de097dee81ed675e2d9bb63096823ed3f0ca827b7dea3028bbbb" -dependencies = [ - "enumset_derive", -] - -[[package]] -name = "enumset_derive" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08b6c6ab82d70f08844964ba10c7babb716de2ecaeab9be5717918a5177d3af" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "env_logger" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" -dependencies = [ - "log", - "regex", -] - -[[package]] -name = "env_logger" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", -] - -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - -[[package]] -name = "euclid" -version = "0.22.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f253bc5c813ca05792837a0ff4b3a580336b224512d48f7eda1d7dd9210787" -dependencies = [ - "num-traits", - "serde", -] - -[[package]] -name = "event-listener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - -[[package]] -name = "fastrand" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] - -[[package]] -name = "fdeflate" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10" -dependencies = [ - "simd-adler32", -] - -[[package]] -name = "field-offset" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" -dependencies = [ - "memoffset", - "rustc_version", -] - -[[package]] -name = "flate2" -version = "1.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "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.52", -] - -[[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.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "futf" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" -dependencies = [ - "mac", - "new_debug_unreachable", -] - -[[package]] -name = "futures" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" - -[[package]] -name = "futures-executor" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" - -[[package]] -name = "futures-lite" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" -dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", -] - -[[package]] -name = "futures-macro" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "futures-sink" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" - -[[package]] -name = "futures-task" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" - -[[package]] -name = "futures-util" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "fxhash" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" -dependencies = [ - "byteorder", -] - -[[package]] -name = "gdk" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5ba081bdef3b75ebcdbfc953699ed2d7417d6bd853347a42a37d76406a33646" -dependencies = [ - "cairo-rs", - "gdk-pixbuf", - "gdk-sys", - "gio", - "glib", - "libc", - "pango", -] - -[[package]] -name = "gdk-pixbuf" -version = "0.18.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" -dependencies = [ - "gdk-pixbuf-sys", - "gio", - "glib", - "libc", - "once_cell", -] - -[[package]] -name = "gdk-pixbuf-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" -dependencies = [ - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "gdk-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31ff856cb3386dae1703a920f803abafcc580e9b5f711ca62ed1620c25b51ff2" -dependencies = [ - "cairo-sys-rs", - "gdk-pixbuf-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "pango-sys", - "pkg-config", - "system-deps", -] - -[[package]] -name = "gdkwayland-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a90fbf5c033c65d93792192a49a8efb5bb1e640c419682a58bb96f5ae77f3d4a" -dependencies = [ - "gdk-sys", - "glib-sys", - "gobject-sys", - "libc", - "pkg-config", - "system-deps", -] - -[[package]] -name = "gdkx11" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2ea8a4909d530f79921290389cbd7c34cb9d623bfe970eaae65ca5f9cd9cce" -dependencies = [ - "gdk", - "gdkx11-sys", - "gio", - "glib", - "libc", - "x11", -] - -[[package]] -name = "gdkx11-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fee8f00f4ee46cad2939b8990f5c70c94ff882c3028f3cc5abf950fa4ab53043" -dependencies = [ - "gdk-sys", - "glib-sys", - "libc", - "system-deps", - "x11", -] - -[[package]] -name = "generational-box" -version = "0.5.0-alpha.0" -dependencies = [ - "parking_lot", -] - -[[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 = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - -[[package]] -name = "getrandom" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.11.0+wasi-snapshot-preview1", -] - -[[package]] -name = "gimli" -version = "0.27.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" - -[[package]] -name = "gio" -version = "0.18.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-util", - "gio-sys", - "glib", - "libc", - "once_cell", - "pin-project-lite", - "smallvec", - "thiserror", -] - -[[package]] -name = "gio-sys" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", - "winapi", -] - -[[package]] -name = "glib" -version = "0.18.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" -dependencies = [ - "bitflags 2.4.2", - "futures-channel", - "futures-core", - "futures-executor", - "futures-task", - "futures-util", - "gio-sys", - "glib-macros", - "glib-sys", - "gobject-sys", - "libc", - "memchr", - "once_cell", - "smallvec", - "thiserror", -] - -[[package]] -name = "glib-macros" -version = "0.18.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" -dependencies = [ - "heck", - "proc-macro-crate 2.0.2", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "glib-sys" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" -dependencies = [ - "libc", - "system-deps", -] - -[[package]] -name = "global-hotkey" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0d37e95d3937251ee2019709389bb793c1237f16d45fc0fe7b2464b5f97c68" -dependencies = [ - "crossbeam-channel", - "keyboard-types", - "once_cell", - "thiserror", - "windows-sys 0.52.0", - "x11-dl", -] - -[[package]] -name = "gloo-net" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43aaa242d1239a8822c15c645f02166398da4f8b5c4bae795c1f5b44e9eee173" -dependencies = [ - "futures-channel", - "futures-core", - "futures-sink", - "gloo-utils", - "http 0.2.9", - "js-sys", - "pin-project", - "serde", - "serde_json", - "thiserror", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "gloo-utils" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" -dependencies = [ - "js-sys", - "serde", - "serde_json", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gobject-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" -dependencies = [ - "glib-sys", - "libc", - "system-deps", -] - -[[package]] -name = "gtk" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93c4f5e0e20b60e10631a5f06da7fe3dda744b05ad0ea71fee2f47adf865890c" -dependencies = [ - "atk", - "cairo-rs", - "field-offset", - "futures-channel", - "gdk", - "gdk-pixbuf", - "gio", - "glib", - "gtk-sys", - "gtk3-macros", - "libc", - "pango", - "pkg-config", -] - -[[package]] -name = "gtk-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771437bf1de2c1c0b496c11505bdf748e26066bbe942dfc8f614c9460f6d7722" -dependencies = [ - "atk-sys", - "cairo-sys-rs", - "gdk-pixbuf-sys", - "gdk-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "pango-sys", - "system-deps", -] - -[[package]] -name = "gtk3-macros" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6063efb63db582968fb7df72e1ae68aa6360dcfb0a75143f34fc7d616bad75e" -dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "half" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5eceaaeec696539ddaf7b333340f1af35a5aa87ae3e4f3ead0532f72affab2e" -dependencies = [ - "cfg-if", - "crunchy", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hashbrown" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" -dependencies = [ - "ahash", - "allocator-api2", -] - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" - -[[package]] -name = "home" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" -dependencies = [ - "windows-sys 0.48.0", -] - -[[package]] -name = "html5ever" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" -dependencies = [ - "log", - "mac", - "markup5ever", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "http" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" -dependencies = [ - "bytes", - "fnv", - "itoa 1.0.9", -] - -[[package]] -name = "http" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" -dependencies = [ - "bytes", - "fnv", - "itoa 1.0.9", -] - -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "idna" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "image" -version = "0.24.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "527909aa81e20ac3a44803521443a765550f09b5130c2c2fa1ea59c2f8f50a3a" -dependencies = [ - "bytemuck", - "byteorder", - "color_quant", - "num-rational", - "num-traits", -] - -[[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", -] - -[[package]] -name = "indexmap" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" -dependencies = [ - "equivalent", - "hashbrown 0.14.0", -] - -[[package]] -name = "infer" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6c16b11a665b26aeeb9b1d7f954cdeb034be38dd00adab4f2ae921a8fee804" -dependencies = [ - "cfb", -] - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "interprocess" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81f2533f3be42fffe3b5e63b71aeca416c1c3bc33e4e27be018521e76b1f38fb" -dependencies = [ - "blocking", - "cfg-if", - "futures-core", - "futures-io", - "intmap", - "libc", - "once_cell", - "rustc_version", - "spinning", - "thiserror", - "to_method", - "winapi", -] - -[[package]] -name = "intmap" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae52f28f45ac2bc96edb7714de995cffc174a395fb0abf5bff453587c980d7b9" - -[[package]] -name = "itoa" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" - -[[package]] -name = "itoa" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" - -[[package]] -name = "javascriptcore-rs" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca5671e9ffce8ffba57afc24070e906da7fc4b1ba66f2cabebf61bf2ea257fcc" -dependencies = [ - "bitflags 1.3.2", - "glib", - "javascriptcore-rs-sys", -] - -[[package]] -name = "javascriptcore-rs-sys" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1be78d14ffa4b75b66df31840478fef72b51f8c2465d4ca7c194da9f7a5124" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "jni" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec" -dependencies = [ - "cesu8", - "combine", - "jni-sys", - "log", - "thiserror", - "walkdir", -] - -[[package]] -name = "jni" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" -dependencies = [ - "cesu8", - "cfg-if", - "combine", - "jni-sys", - "log", - "thiserror", - "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 = "js-sys" -version = "0.3.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "keyboard-types" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" -dependencies = [ - "bitflags 2.4.2", - "serde", - "unicode-segmentation", -] - -[[package]] -name = "kuchikiki" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e4755b7b995046f510a7520c42b2fed58b77bd94d5a87a8eb43d2fd126da8" -dependencies = [ - "cssparser", - "html5ever", - "indexmap 1.9.3", - "matches", - "selectors", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "libc" -version = "0.2.147" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" - -[[package]] -name = "libxdo" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00333b8756a3d28e78def82067a377de7fa61b24909000aeaa2b446a948d14db" -dependencies = [ - "libxdo-sys", -] - -[[package]] -name = "libxdo-sys" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db23b9e7e2b7831bbd8aac0bbeeeb7b68cbebc162b227e7052e8e55829a09212" -dependencies = [ - "libc", - "x11", -] - -[[package]] -name = "lock_api" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" - -[[package]] -name = "longest-increasing-subsequence" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3bd0dd2cd90571056fdb71f6275fada10131182f84899f4b2a916e565d81d86" - -[[package]] -name = "lru" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" -dependencies = [ - "hashbrown 0.14.0", -] - -[[package]] -name = "mac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" - -[[package]] -name = "malloc_buf" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" -dependencies = [ - "libc", -] - -[[package]] -name = "markup5ever" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" -dependencies = [ - "log", - "phf 0.10.1", - "phf_codegen 0.10.0", - "string_cache", - "string_cache_codegen", - "tendril", -] - -[[package]] -name = "matches" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" - -[[package]] -name = "md5" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "memoffset" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" -dependencies = [ - "autocfg", -] - -[[package]] -name = "miniz_oxide" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" -dependencies = [ - "adler", - "simd-adler32", -] - -[[package]] -name = "mobile-demo" -version = "0.1.0" -dependencies = [ - "android_logger", - "anyhow", - "core-foundation", - "dioxus", - "env_logger 0.9.3", - "jni 0.19.0", - "log", - "paste", - "wry 0.35.2", -] - -[[package]] -name = "muda" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c47e7625990fc1af2226ea4f34fb2412b03c12639fcb91868581eb3a6893453" -dependencies = [ - "cocoa", - "crossbeam-channel", - "gtk", - "keyboard-types", - "libxdo", - "objc", - "once_cell", - "png", - "thiserror", - "windows-sys 0.52.0", -] - -[[package]] -name = "ndk" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" -dependencies = [ - "bitflags 1.3.2", - "jni-sys", - "ndk-sys", - "num_enum", - "raw-window-handle 0.5.2", - "thiserror", -] - -[[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.4.1+23.1.7779620" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cf2aae958bd232cac5069850591667ad422d263686d75b52a065f9badeee5a3" -dependencies = [ - "jni-sys", -] - -[[package]] -name = "new_debug_unreachable" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" - -[[package]] -name = "nodrop" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" - -[[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "winapi", -] - -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" -dependencies = [ - "autocfg", -] - -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi 0.3.2", - "libc", -] - -[[package]] -name = "num_enum" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" -dependencies = [ - "num_enum_derive", -] - -[[package]] -name = "num_enum_derive" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" -dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "objc" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" -dependencies = [ - "malloc_buf", - "objc_exception", -] - -[[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_exception" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" -dependencies = [ - "cc", -] - -[[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.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" - -[[package]] -name = "ordered-float" -version = "2.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7940cf2ca942593318d07fcf2596cdca60a85c9e7fab408a5e21a4f9dcd40d87" -dependencies = [ - "num-traits", -] - -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - -[[package]] -name = "pango" -version = "0.18.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" -dependencies = [ - "gio", - "glib", - "libc", - "once_cell", - "pango-sys", -] - -[[package]] -name = "pango-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "parking" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e" - -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets 0.48.1", -] - -[[package]] -name = "paste" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" - -[[package]] -name = "percent-encoding" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" - -[[package]] -name = "phf" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" -dependencies = [ - "phf_macros", - "phf_shared 0.8.0", - "proc-macro-hack", -] - -[[package]] -name = "phf" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" -dependencies = [ - "phf_shared 0.10.0", -] - -[[package]] -name = "phf_codegen" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" -dependencies = [ - "phf_generator 0.8.0", - "phf_shared 0.8.0", -] - -[[package]] -name = "phf_codegen" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" -dependencies = [ - "phf_generator 0.10.0", - "phf_shared 0.10.0", -] - -[[package]] -name = "phf_generator" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" -dependencies = [ - "phf_shared 0.8.0", - "rand 0.7.3", -] - -[[package]] -name = "phf_generator" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" -dependencies = [ - "phf_shared 0.10.0", - "rand 0.8.5", -] - -[[package]] -name = "phf_macros" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" -dependencies = [ - "phf_generator 0.8.0", - "phf_shared 0.8.0", - "proc-macro-hack", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "phf_shared" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" -dependencies = [ - "siphasher", -] - -[[package]] -name = "phf_shared" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" -dependencies = [ - "siphasher", -] - -[[package]] -name = "pin-project" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkg-config" -version = "0.3.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" - -[[package]] -name = "png" -version = "0.17.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59871cc5b6cce7eaccca5a802b4173377a1c2ba90654246789a8fa2334426d11" -dependencies = [ - "bitflags 1.3.2", - "crc32fast", - "fdeflate", - "flate2", - "miniz_oxide", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "precomputed-hash" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" - -[[package]] -name = "prettyplease" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" -dependencies = [ - "proc-macro2", - "syn 2.0.52", -] - -[[package]] -name = "proc-macro-crate" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" -dependencies = [ - "once_cell", - "toml_edit 0.19.14", -] - -[[package]] -name = "proc-macro-crate" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" -dependencies = [ - "toml_datetime", - "toml_edit 0.20.2", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro-hack" -version = "0.5.20+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" - -[[package]] -name = "proc-macro2" -version = "1.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", - "rand_pcg", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", -] - -[[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 0.6.4", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.10", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "rand_pcg" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "raw-window-handle" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" - -[[package]] -name = "raw-window-handle" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42a9830a0e1b9fb145ebb365b8bc4ccd75f290f98c0247deafbbe2c75cefb544" - -[[package]] -name = "redox_syscall" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "regex" -version = "1.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7b6d6190b7594385f61bd3911cd1be99dfddcfc365a4160cc2ab5bff4aed294" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" - -[[package]] -name = "rfd" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c9e7b57df6e8472152674607f6cc68aa14a748a3157a857a94f516e11aeacc2" -dependencies = [ - "block", - "dispatch", - "glib-sys", - "gobject-sys", - "gtk-sys", - "js-sys", - "log", - "objc", - "objc-foundation", - "objc_id", - "raw-window-handle 0.5.2", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "windows-sys 0.48.0", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" - -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - -[[package]] -name = "rustc_version" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver", -] - -[[package]] -name = "ryu" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" - -[[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 = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "selectors" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe" -dependencies = [ - "bitflags 1.3.2", - "cssparser", - "derive_more", - "fxhash", - "log", - "matches", - "phf 0.8.0", - "phf_codegen 0.8.0", - "precomputed-hash", - "servo_arc", - "smallvec", - "thin-slice", -] - -[[package]] -name = "semver" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" - -[[package]] -name = "send_wrapper" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" -dependencies = [ - "futures-core", -] - -[[package]] -name = "serde" -version = "1.0.180" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ea67f183f058fe88a4e3ec6e2788e003840893b91bac4559cabedd00863b3ed" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde-value" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" -dependencies = [ - "ordered-float", - "serde", -] - -[[package]] -name = "serde_derive" -version = "1.0.180" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24e744d7782b686ab3b73267ef05697159cc0e5abbed3f47f9933165e5219036" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "serde_json" -version = "1.0.104" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" -dependencies = [ - "itoa 1.0.9", - "ryu", - "serde", -] - -[[package]] -name = "serde_qs" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0431a35568651e363364210c91983c1da5eb29404d9f0928b67d4ebcfa7d330c" -dependencies = [ - "percent-encoding", - "serde", - "thiserror", -] - -[[package]] -name = "serde_repr" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "serde_spanned" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" -dependencies = [ - "serde", -] - -[[package]] -name = "server_fn" -version = "0.6.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2955da1dc5fcd970c182ebf1089af6c5f19051e1f286a21f7b96490a49b7a531" -dependencies = [ - "bytes", - "const_format", - "dashmap", - "futures", - "gloo-net", - "http 1.1.0", - "js-sys", - "once_cell", - "send_wrapper", - "serde", - "serde_json", - "serde_qs", - "server_fn_macro_default", - "thiserror", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-streams", - "web-sys", - "xxhash-rust", -] - -[[package]] -name = "server_fn_macro" -version = "0.6.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adfdd051ef905fdb3da20942b0c52d536158d7489a724e14cc2fd47323e7ca91" -dependencies = [ - "const_format", - "convert_case 0.6.0", - "proc-macro2", - "quote", - "syn 2.0.52", - "xxhash-rust", -] - -[[package]] -name = "server_fn_macro_default" -version = "0.6.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "060af1def72353a779fcc184c53e1965d3055a38b9e827f2259b2bff2d9c371e" -dependencies = [ - "server_fn_macro", - "syn 2.0.52", -] - -[[package]] -name = "servo_arc" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432" -dependencies = [ - "nodrop", - "stable_deref_trait", -] - -[[package]] -name = "sha2" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "simd-adler32" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" - -[[package]] -name = "siphasher" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" - -[[package]] -name = "slab" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" -dependencies = [ - "autocfg", -] - -[[package]] -name = "sledgehammer_bindgen" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcfaf791ff02f48f3518ce825d32cf419c13a43c1d8b1232f74ac89f339c46d2" -dependencies = [ - "sledgehammer_bindgen_macro", -] - -[[package]] -name = "sledgehammer_bindgen_macro" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdd941cc539bd3dc694edaf9d0c4e1221d02baa67c6b45ec04fad1024d9e8139" -dependencies = [ - "quote", - "syn 2.0.52", -] - -[[package]] -name = "sledgehammer_utils" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f20798defa0e9d4eff9ca451c7f84774c7378a9c3b5a40112cfa2b3eadb97ae2" -dependencies = [ - "lru", - "once_cell", - "rustc-hash", -] - -[[package]] -name = "smallvec" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" - -[[package]] -name = "soup3" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "471f924a40f31251afc77450e781cb26d55c0b650842efafc9c6cbd2f7cc4f9f" -dependencies = [ - "futures-channel", - "gio", - "glib", - "libc", - "soup3-sys", -] - -[[package]] -name = "soup3-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ebe8950a680a12f24f15ebe1bf70db7af98ad242d9db43596ad3108aab86c27" -dependencies = [ - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "spinning" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d4f0e86297cad2658d92a707320d87bf4e6ae1050287f51d19b67ef3f153a7b" -dependencies = [ - "lock_api", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - -[[package]] -name = "string_cache" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" -dependencies = [ - "new_debug_unreachable", - "once_cell", - "parking_lot", - "phf_shared 0.10.0", - "precomputed-hash", - "serde", -] - -[[package]] -name = "string_cache_codegen" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" -dependencies = [ - "phf_generator 0.10.0", - "phf_shared 0.10.0", - "proc-macro2", - "quote", -] - -[[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.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "system-deps" -version = "6.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30c2de8a4d8f4b823d634affc9cd2a74ec98c53a756f317e529a48046cbf71f3" -dependencies = [ - "cfg-expr", - "heck", - "pkg-config", - "toml", - "version-compare", -] - -[[package]] -name = "tao" -version = "0.26.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccba570365293ca309d60f30fdac2c5271b732dc762e6154e59c85d2c762a0a1" -dependencies = [ - "bitflags 1.3.2", - "cocoa", - "core-foundation", - "core-graphics", - "crossbeam-channel", - "dispatch", - "dlopen2", - "gdkwayland-sys", - "gdkx11-sys", - "gtk", - "image", - "instant", - "jni 0.21.1", - "lazy_static", - "libc", - "log", - "ndk", - "ndk-context", - "ndk-sys", - "objc", - "once_cell", - "parking_lot", - "png", - "raw-window-handle 0.5.2", - "raw-window-handle 0.6.0", - "scopeguard", - "tao-macros", - "unicode-segmentation", - "url", - "windows", - "windows-implement", - "windows-version", - "x11-dl", -] - -[[package]] -name = "tao-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b27a4bcc5eb524658234589bdffc7e7bfb996dbae6ce9393bfd39cb4159b445" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "target-lexicon" -version = "0.12.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a" - -[[package]] -name = "tendril" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" -dependencies = [ - "futf", - "mac", - "utf-8", -] - -[[package]] -name = "termcolor" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "thin-slice" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" - -[[package]] -name = "thiserror" -version = "1.0.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "thread_local" -version = "1.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" -dependencies = [ - "cfg-if", - "once_cell", -] - -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "to_method" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7c4ceeeca15c8384bbc3e011dbd8fccb7f068a440b752b7d9b32ceb0ca0e2e8" - -[[package]] -name = "tokio" -version = "1.29.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" -dependencies = [ - "autocfg", - "backtrace", - "bytes", - "num_cpus", - "pin-project-lite", - "tokio-macros", -] - -[[package]] -name = "tokio-macros" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "toml" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit 0.19.14", -] - -[[package]] -name = "toml_datetime" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_edit" -version = "0.19.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" -dependencies = [ - "indexmap 2.0.0", - "serde", - "serde_spanned", - "toml_datetime", - "winnow", -] - -[[package]] -name = "toml_edit" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" -dependencies = [ - "indexmap 2.0.0", - "toml_datetime", - "winnow", -] - -[[package]] -name = "tracing" -version = "0.1.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" -dependencies = [ - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "tracing-core" -version = "0.1.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" -dependencies = [ - "nu-ansi-term", - "sharded-slab", - "smallvec", - "thread_local", - "tracing-core", - "tracing-log", -] - -[[package]] -name = "typenum" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" - -[[package]] -name = "unicode-bidi" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" - -[[package]] -name = "unicode-ident" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" - -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-segmentation" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" - -[[package]] -name = "unicode-xid" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" - -[[package]] -name = "url" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", -] - -[[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 = "uuid" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" - -[[package]] -name = "valuable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - -[[package]] -name = "version-compare" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "waker-fn" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" - -[[package]] -name = "walkdir" -version = "2.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - -[[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.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn 2.0.52", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" - -[[package]] -name = "wasm-streams" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" -dependencies = [ - "futures-util", - "js-sys", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "web-sys" -version = "0.3.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webbrowser" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd222aa310eb7532e3fd427a5d7db7e44bc0b0cf1c1e21139c345325511a85b6" -dependencies = [ - "core-foundation", - "home", - "jni 0.21.1", - "log", - "ndk-context", - "objc", - "raw-window-handle 0.5.2", - "url", - "web-sys", -] - -[[package]] -name = "webkit2gtk" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76b1bc1e54c581da1e9f179d0b38512ba358fb1af2d634a1affe42e37172361a" -dependencies = [ - "bitflags 1.3.2", - "cairo-rs", - "gdk", - "gdk-sys", - "gio", - "gio-sys", - "glib", - "glib-sys", - "gobject-sys", - "gtk", - "gtk-sys", - "javascriptcore-rs", - "libc", - "once_cell", - "soup3", - "webkit2gtk-sys", -] - -[[package]] -name = "webkit2gtk-sys" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62daa38afc514d1f8f12b8693d30d5993ff77ced33ce30cd04deebc267a6d57c" -dependencies = [ - "bitflags 1.3.2", - "cairo-sys-rs", - "gdk-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "gtk-sys", - "javascriptcore-rs-sys", - "libc", - "pkg-config", - "soup3-sys", - "system-deps", -] - -[[package]] -name = "webview2-com" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0ae9c7e420783826cf769d2c06ac9ba462f450eca5893bb8c6c6529a4e5dd33" -dependencies = [ - "webview2-com-macros", - "webview2-com-sys", - "windows", - "windows-core", - "windows-implement", - "windows-interface", -] - -[[package]] -name = "webview2-com-macros" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1345798ecd8122468840bcdf1b95e5dc6d2206c5e4b0eafa078d061f59c9bc" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "webview2-com-sys" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6ad85fceee6c42fa3d61239eba5a11401bf38407a849ed5ea1b407df08cca72" -dependencies = [ - "thiserror", - "windows", - "windows-core", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[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.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" -dependencies = [ - "windows-core", - "windows-implement", - "windows-interface", - "windows-targets 0.52.4", -] - -[[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.4", -] - -[[package]] -name = "windows-implement" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12168c33176773b86799be25e2a2ba07c7aab9968b37541f1094dbd7a60c8946" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "windows-interface" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d8dc32e0095a7eeccebd0e3f09e9509365ecb3fc6ac4d6f5f14a3f6392942d1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[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.1", -] - -[[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.4", -] - -[[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.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" -dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", -] - -[[package]] -name = "windows-targets" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" -dependencies = [ - "windows_aarch64_gnullvm 0.52.4", - "windows_aarch64_msvc 0.52.4", - "windows_i686_gnu 0.52.4", - "windows_i686_msvc 0.52.4", - "windows_x86_64_gnu 0.52.4", - "windows_x86_64_gnullvm 0.52.4", - "windows_x86_64_msvc 0.52.4", -] - -[[package]] -name = "windows-version" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75aa004c988e080ad34aff5739c39d0312f4684699d6d71fc8a198d057b8b9b4" -dependencies = [ - "windows-targets 0.52.4", -] - -[[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.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" - -[[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.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" - -[[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.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" - -[[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.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" - -[[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.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" - -[[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.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" - -[[package]] -name = "winnow" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bd122eb777186e60c3fdf765a58ac76e41c582f1f535fbf3314434c6b58f3f7" -dependencies = [ - "memchr", -] - -[[package]] -name = "wry" -version = "0.35.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3016c47c9b6f7029a9da7cd48af8352327226bba0e955f3c92e2966651365a9" -dependencies = [ - "base64", - "block", - "cfg_aliases", - "cocoa", - "core-graphics", - "crossbeam-channel", - "dunce", - "gdkx11", - "gtk", - "html5ever", - "http 0.2.9", - "javascriptcore-rs", - "jni 0.21.1", - "kuchikiki", - "libc", - "log", - "ndk", - "ndk-context", - "ndk-sys", - "objc", - "objc_id", - "once_cell", - "raw-window-handle 0.5.2", - "serde", - "serde_json", - "sha2", - "soup3", - "tao-macros", - "thiserror", - "url", - "webkit2gtk", - "webkit2gtk-sys", - "webview2-com", - "windows", - "windows-implement", - "windows-version", - "x11-dl", -] - -[[package]] -name = "wry" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b717040ba9771fd88eb428c6ea6b555f8e734ff8534f02c13e8f10d97f5935e" -dependencies = [ - "base64", - "block", - "cfg_aliases", - "cocoa", - "core-graphics", - "crossbeam-channel", - "dunce", - "gdkx11", - "gtk", - "html5ever", - "http 0.2.9", - "javascriptcore-rs", - "jni 0.21.1", - "kuchikiki", - "libc", - "log", - "ndk", - "ndk-context", - "ndk-sys", - "objc", - "objc_id", - "once_cell", - "percent-encoding", - "raw-window-handle 0.6.0", - "serde", - "serde_json", - "sha2", - "soup3", - "tao-macros", - "thiserror", - "webkit2gtk", - "webkit2gtk-sys", - "webview2-com", - "windows", - "windows-implement", - "windows-version", - "x11-dl", -] - -[[package]] -name = "x11" -version = "2.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" -dependencies = [ - "libc", - "pkg-config", -] - -[[package]] -name = "x11-dl" -version = "2.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" -dependencies = [ - "libc", - "once_cell", - "pkg-config", -] - -[[package]] -name = "xxhash-rust" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927da81e25be1e1a2901d59b81b37dd2efd1fc9c9345a55007f09bf5a2d3ee03" - -[[package]] -name = "zerocopy" -version = "0.7.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", -] diff --git a/examples/mobile_demo/Cargo.toml b/examples/mobile_demo/Cargo.toml deleted file mode 100644 index 4d4155750b..0000000000 --- a/examples/mobile_demo/Cargo.toml +++ /dev/null @@ -1,51 +0,0 @@ -[package] -name = "mobile-demo" -version = "0.1.0" -authors = ["Jonathan Kelley "] -edition = "2021" - -[lib] -crate-type = ["staticlib", "cdylib", "rlib"] - -[[bin]] -name = "mobile-demo-desktop" -path = "gen/bin/desktop.rs" - -[package.metadata.cargo-android] -app-activity-name = "com.example.mobile_demo.MainActivity" -app-dependencies = [ - "androidx.webkit:webkit:1.6.1", - "androidx.appcompat:appcompat:1.6.1", - "com.google.android.material:material:1.8.0", -] -project-dependencies = ["org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21"] -app-plugins = ["org.jetbrains.kotlin.android"] -app-permissions = ["android.permission.INTERNET"] -app-theme-parent = "Theme.MaterialComponents.DayNight.DarkActionBar" -vulkan-validation = false - -[package.metadata.cargo-android.env-vars] -WRY_ANDROID_PACKAGE = "com.example.mobile_demo" -WRY_ANDROID_LIBRARY = "mobile_demo" -WRY_ANDROID_KOTLIN_FILES_OUT_DIR = "/app/src/main/kotlin/com/example/mobile_demo" - -[package.metadata.cargo-apple.ios] -frameworks = ["WebKit"] - -[dependencies] -anyhow = "1.0.56" -log = "0.4.11" -wry = "0.35.0" -dioxus = { path = "../../packages/dioxus", features = ["mobile"]} - - -[target.'cfg(target_os = "android")'.dependencies] -android_logger = "0.9.0" -jni = "0.19.0" -paste = "1.0" - -[target.'cfg(not(target_os = "android"))'.dependencies] -env_logger = "0.9.0" - -[target.'cfg(target_os = "ios")'.dependencies] -core-foundation = "0.9.3" diff --git a/examples/mobile_demo/README.md b/examples/mobile_demo/README.md deleted file mode 100644 index 4d5ea46ed0..0000000000 --- a/examples/mobile_demo/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Dioxus Mobile demo - -## How this project was generated - -Right now, Dioxus supports mobile targets including iOS and Android. However, our tooling is not mature enough to include the build commands directly. - -This project was generated using [cargo-mobile2](https://github.com/tauri-apps/cargo-mobile2). We have yet to integrate this generation into the Dioxus-CLI. The open issue for this is [#1157](https://github.com/DioxusLabs/dioxus/issues/1157). - -## Running this project - -Because the tooling and ecosystem is still young, Dioxus mobile can be difficult to setup and run. We have [detailed guides](https://dioxuslabs.com/learn/0.5/getting_started) for creating, building, and running on both iOS and Android. diff --git a/examples/mobile_demo/mobile.toml b/examples/mobile_demo/mobile.toml deleted file mode 100644 index 3b87772508..0000000000 --- a/examples/mobile_demo/mobile.toml +++ /dev/null @@ -1,8 +0,0 @@ -[app] -name = "mobile-demo" -stylized-name = "Mobile Demo" -domain = "example.com" -template-pack = "wry" - -[apple] -development-team = "34U4FG9TJ8" diff --git a/examples/mobile_demo/src/index.html b/examples/mobile_demo/src/index.html deleted file mode 100644 index b0e6290e12..0000000000 --- a/examples/mobile_demo/src/index.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - Dioxus app - - - - -
      - - - diff --git a/examples/mobile_demo/src/lib.rs b/examples/mobile_demo/src/lib.rs deleted file mode 100644 index 6fc142d9a3..0000000000 --- a/examples/mobile_demo/src/lib.rs +++ /dev/null @@ -1,90 +0,0 @@ -use anyhow::Result; -use dioxus::mobile::Config; -use dioxus::prelude::*; - -#[cfg(target_os = "android")] -use dioxus::mobile::wry::android_binding; - -#[cfg(target_os = "android")] -fn init_logging() { - android_logger::init_once( - android_logger::Config::default() - .with_min_level(log::Level::Trace) - .with_tag("mobile-demo"), - ); -} - -#[cfg(not(target_os = "android"))] -fn init_logging() { - env_logger::init(); -} - -#[cfg(any(target_os = "android", target_os = "ios"))] -fn stop_unwind T, T>(f: F) -> T { - match std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)) { - Ok(t) => t, - Err(err) => { - eprintln!("attempt to unwind out of `rust` with err: {:?}", err); - std::process::abort() - } - } -} - -#[cfg(any(target_os = "android", target_os = "ios"))] -fn _start_app() { - stop_unwind(|| main().unwrap()); -} - -#[no_mangle] -#[inline(never)] -#[cfg(any(target_os = "android", target_os = "ios"))] -pub extern "C" fn start_app() { - #[cfg(target_os = "android")] - android_binding!(com_example, mobile_demo, _start_app); - #[cfg(target_os = "ios")] - _start_app() -} - -pub fn main() -> Result<()> { - init_logging(); - - // Right now we're going through dioxus-desktop but we'd like to go through dioxus-mobile - // That will seed the index.html with some fixes that prevent the page from scrolling/zooming etc - LaunchBuilder::mobile() - .with_cfg( - // Note that we have to disable the viewport goofiness of the browser. - // Dioxus_mobile should do this for us - Config::default().with_custom_index(include_str!("index.html").to_string()), - ) - .launch(app); - - Ok(()) -} - -fn app() -> Element { - let mut items = use_signal(|| vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); - - log::debug!("Hello from the app"); - - rsx! { - div { - h1 { "Hello, Mobile" } - div { - margin_left: "auto", - margin_right: "auto", - width: "200px", - padding: "10px", - border: "1px solid black", - button { - onclick: move |_| { - items.push(items.len()); - }, - "Add item" - } - for item in items.iter() { - div { "- {item}" } - } - } - } - } -} diff --git a/examples/multiwindow.rs b/examples/multiwindow.rs index 6858f65896..d89a78ee6d 100644 --- a/examples/multiwindow.rs +++ b/examples/multiwindow.rs @@ -7,7 +7,7 @@ use dioxus::prelude::*; fn main() { - launch_desktop(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/nested_listeners.rs b/examples/nested_listeners.rs index 33cf3d4043..c2316aed37 100644 --- a/examples/nested_listeners.rs +++ b/examples/nested_listeners.rs @@ -7,7 +7,7 @@ use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/openid_connect_demo/.cargo/config.toml b/examples/openid_connect_demo/.cargo/config.toml deleted file mode 100644 index fcb494816a..0000000000 --- a/examples/openid_connect_demo/.cargo/config.toml +++ /dev/null @@ -1,5 +0,0 @@ -[env] -DIOXUS_FRONT_ISSUER_URL = "TODO" -DIOXUS_FRONT_CLIENT_ID = "TODO" -DIOXUS_FRONT_CLIENT_SECRET = "TODO" -DIOXUS_FRONT_URL = "http://localhost:8080" diff --git a/examples/openid_connect_demo/.gitignore b/examples/openid_connect_demo/.gitignore deleted file mode 100644 index 919c8ee550..0000000000 --- a/examples/openid_connect_demo/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/target -/dist -.env diff --git a/examples/openid_connect_demo/Cargo.lock b/examples/openid_connect_demo/Cargo.lock deleted file mode 100644 index b7d513c11e..0000000000 --- a/examples/openid_connect_demo/Cargo.lock +++ /dev/null @@ -1,6372 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "addr2line" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "ahash" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy", -] - -[[package]] -name = "aho-corasick" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] - -[[package]] -name = "allocator-api2" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" - -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "anyhow" -version = "1.0.86" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" - -[[package]] -name = "anymap" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33954243bd79057c2de7338850b85983a44588021f8a5fee574a8888c6de4344" - -[[package]] -name = "anymap2" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c" - -[[package]] -name = "ashpd" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd884d7c72877a94102c3715f3b1cd09ff4fac28221add3e57cfbe25c236d093" -dependencies = [ - "async-fs", - "async-net", - "enumflags2", - "futures-channel", - "futures-util", - "rand 0.8.5", - "serde", - "serde_repr", - "url", - "zbus", -] - -[[package]] -name = "askama_escape" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341" - -[[package]] -name = "async-broadcast" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20cd0e2e25ea8e5f7e9df04578dc6cf5c83577fd09b1a46aaf5c85e1c33f2a7e" -dependencies = [ - "event-listener", - "event-listener-strategy", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-channel" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" -dependencies = [ - "concurrent-queue", - "event-listener-strategy", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-executor" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8828ec6e544c02b0d6691d21ed9f9218d0384a82542855073c2a3f58304aaf0" -dependencies = [ - "async-task", - "concurrent-queue", - "fastrand", - "futures-lite", - "slab", -] - -[[package]] -name = "async-fs" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" -dependencies = [ - "async-lock", - "blocking", - "futures-lite", -] - -[[package]] -name = "async-io" -version = "2.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d6baa8f0178795da0e71bc42c9e5d13261aac7ee549853162e66a241ba17964" -dependencies = [ - "async-lock", - "cfg-if", - "concurrent-queue", - "futures-io", - "futures-lite", - "parking", - "polling", - "rustix", - "slab", - "tracing", - "windows-sys 0.52.0", -] - -[[package]] -name = "async-lock" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" -dependencies = [ - "event-listener", - "event-listener-strategy", - "pin-project-lite", -] - -[[package]] -name = "async-net" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" -dependencies = [ - "async-io", - "blocking", - "futures-lite", -] - -[[package]] -name = "async-process" -version = "2.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7eda79bbd84e29c2b308d1dc099d7de8dcc7035e48f4bf5dc4a531a44ff5e2a" -dependencies = [ - "async-channel", - "async-io", - "async-lock", - "async-signal", - "async-task", - "blocking", - "cfg-if", - "event-listener", - "futures-lite", - "rustix", - "tracing", - "windows-sys 0.52.0", -] - -[[package]] -name = "async-recursion" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "async-signal" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "329972aa325176e89114919f2a80fdae4f4c040f66a370b1a1159c6c0f94e7aa" -dependencies = [ - "async-io", - "async-lock", - "atomic-waker", - "cfg-if", - "futures-core", - "futures-io", - "rustix", - "signal-hook-registry", - "slab", - "windows-sys 0.52.0", -] - -[[package]] -name = "async-task" -version = "4.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" - -[[package]] -name = "async-trait" -version = "0.1.80" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "atk" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4af014b17dd80e8af9fa689b2d4a211ddba6eb583c1622f35d0cb543f6b17e4" -dependencies = [ - "atk-sys", - "glib", - "libc", -] - -[[package]] -name = "atk-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "251e0b7d90e33e0ba930891a505a9a35ece37b2dd37a14f3ffc306c13b980009" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "atomic-polyfill" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" -dependencies = [ - "critical-section", -] - -[[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - -[[package]] -name = "autocfg" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" - -[[package]] -name = "axum" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" -dependencies = [ - "async-trait", - "axum-core", - "axum-macros", - "base64 0.21.7", - "bytes", - "futures-util", - "http 1.1.0", - "http-body 1.0.0", - "http-body-util", - "hyper 1.3.1", - "hyper-util", - "itoa 1.0.11", - "matchit", - "memchr", - "mime", - "multer", - "percent-encoding", - "pin-project-lite", - "rustversion", - "serde", - "serde_json", - "serde_path_to_error", - "serde_urlencoded", - "sha1", - "sync_wrapper 1.0.1", - "tokio", - "tokio-tungstenite", - "tower", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "axum-core" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" -dependencies = [ - "async-trait", - "bytes", - "futures-util", - "http 1.1.0", - "http-body 1.0.0", - "http-body-util", - "mime", - "pin-project-lite", - "rustversion", - "sync_wrapper 0.1.2", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "axum-macros" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00c055ee2d014ae5981ce1016374e8213682aa14d9bf40e48ab48b5f3ef20eaa" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "backtrace" -version = "0.3.72" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17c6a35df3749d2e8bb1b7b21a976d82b15548788d2735b9d82f329268f71a11" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - -[[package]] -name = "base16ct" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "base64" -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "base64ct" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" - -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" -dependencies = [ - "serde", -] - -[[package]] -name = "block" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array 0.14.7", -] - -[[package]] -name = "blocking" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" -dependencies = [ - "async-channel", - "async-task", - "futures-io", - "futures-lite", - "piper", -] - -[[package]] -name = "bstr" -version = "1.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" -dependencies = [ - "memchr", - "serde", -] - -[[package]] -name = "bumpalo" -version = "3.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "bytes" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" - -[[package]] -name = "cairo-rs" -version = "0.18.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" -dependencies = [ - "bitflags 2.5.0", - "cairo-sys-rs", - "glib", - "libc", - "once_cell", - "thiserror", -] - -[[package]] -name = "cairo-sys-rs" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" -dependencies = [ - "glib-sys", - "libc", - "system-deps", -] - -[[package]] -name = "camino" -version = "1.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo-platform" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo_metadata" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" -dependencies = [ - "camino", - "cargo-platform", - "semver", - "serde", - "serde_json", - "thiserror", -] - -[[package]] -name = "cc" -version = "1.0.98" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" - -[[package]] -name = "cesu8" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" - -[[package]] -name = "cfb" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" -dependencies = [ - "byteorder", - "fnv", - "uuid", -] - -[[package]] -name = "cfg-expr" -version = "0.15.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" -dependencies = [ - "smallvec", - "target-lexicon", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "cfg_aliases" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" - -[[package]] -name = "chrono" -version = "0.4.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "js-sys", - "num-traits", - "serde", - "wasm-bindgen", - "windows-targets 0.52.5", -] - -[[package]] -name = "ciborium" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" -dependencies = [ - "ciborium-io", - "ciborium-ll", - "serde", -] - -[[package]] -name = "ciborium-io" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" - -[[package]] -name = "ciborium-ll" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" -dependencies = [ - "ciborium-io", - "half", -] - -[[package]] -name = "cobs" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" - -[[package]] -name = "cocoa" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" -dependencies = [ - "bitflags 1.3.2", - "block", - "cocoa-foundation", - "core-foundation", - "core-graphics", - "foreign-types", - "libc", - "objc", -] - -[[package]] -name = "cocoa-foundation" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" -dependencies = [ - "bitflags 1.3.2", - "block", - "core-foundation", - "core-graphics-types", - "libc", - "objc", -] - -[[package]] -name = "combine" -version = "4.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" -dependencies = [ - "bytes", - "memchr", -] - -[[package]] -name = "concurrent-queue" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "console_error_panic_hook" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" -dependencies = [ - "cfg-if", - "wasm-bindgen", -] - -[[package]] -name = "const-oid" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" - -[[package]] -name = "const_format" -version = "0.2.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3a214c7af3d04997541b18d432afaff4c455e79e2029079647e72fc2bd27673" -dependencies = [ - "const_format_proc_macros", -] - -[[package]] -name = "const_format_proc_macros" -version = "0.2.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - -[[package]] -name = "convert_case" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" -dependencies = [ - "unicode-segmentation", -] - -[[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-sys" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" - -[[package]] -name = "core-graphics" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "core-graphics-types", - "foreign-types", - "libc", -] - -[[package]] -name = "core-graphics-types" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "libc", -] - -[[package]] -name = "cpufeatures" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" -dependencies = [ - "libc", -] - -[[package]] -name = "crc32fast" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "critical-section" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" - -[[package]] -name = "crossbeam-channel" -version = "0.5.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" -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.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[package]] -name = "crypto-bigint" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" -dependencies = [ - "generic-array 0.14.7", - "rand_core 0.6.4", - "subtle", - "zeroize", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array 0.14.7", - "typenum", -] - -[[package]] -name = "cssparser" -version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a" -dependencies = [ - "cssparser-macros", - "dtoa-short", - "itoa 0.4.8", - "matches", - "phf 0.8.0", - "proc-macro2", - "quote", - "smallvec", - "syn 1.0.109", -] - -[[package]] -name = "cssparser-macros" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" -dependencies = [ - "quote", - "syn 2.0.66", -] - -[[package]] -name = "curve25519-dalek" -version = "4.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348" -dependencies = [ - "cfg-if", - "cpufeatures", - "curve25519-dalek-derive", - "digest", - "fiat-crypto", - "platforms", - "rustc_version", - "subtle", - "zeroize", -] - -[[package]] -name = "curve25519-dalek-derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "darling" -version = "0.20.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.20.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 2.0.66", -] - -[[package]] -name = "darling_macro" -version = "0.20.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" -dependencies = [ - "darling_core", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "dashmap" -version = "5.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" -dependencies = [ - "cfg-if", - "hashbrown 0.14.5", - "lock_api", - "once_cell", - "parking_lot_core", -] - -[[package]] -name = "data-encoding" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" - -[[package]] -name = "der" -version = "0.7.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" -dependencies = [ - "const-oid", - "pem-rfc7468", - "zeroize", -] - -[[package]] -name = "deranged" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" -dependencies = [ - "powerfmt", - "serde", -] - -[[package]] -name = "derivative" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "derive_more" -version = "0.99.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" -dependencies = [ - "convert_case 0.4.0", - "proc-macro2", - "quote", - "rustc_version", - "syn 1.0.109", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "const-oid", - "crypto-common", - "subtle", -] - -[[package]] -name = "dioxus" -version = "0.5.2" -dependencies = [ - "dioxus-config-macro", - "dioxus-core", - "dioxus-core-macro", - "dioxus-desktop", - "dioxus-fullstack", - "dioxus-hooks", - "dioxus-hot-reload", - "dioxus-html", - "dioxus-liveview", - "dioxus-router", - "dioxus-signals", - "dioxus-ssr", - "dioxus-static-site-generation", - "dioxus-web", - "serde", -] - -[[package]] -name = "dioxus-cli-config" -version = "0.5.2" -dependencies = [ - "once_cell", - "serde", - "serde_json", - "tracing", -] - -[[package]] -name = "dioxus-config-macro" -version = "0.5.2" -dependencies = [ - "proc-macro2", - "quote", -] - -[[package]] -name = "dioxus-core" -version = "0.5.2" -dependencies = [ - "futures-channel", - "futures-util", - "generational-box", - "longest-increasing-subsequence", - "rustc-hash", - "serde", - "slab", - "tracing", - "tracing-subscriber", -] - -[[package]] -name = "dioxus-core-macro" -version = "0.5.2" -dependencies = [ - "convert_case 0.6.0", - "dioxus-rsx", - "prettyplease", - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "dioxus-debug-cell" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ea539174bb236e0e7dc9c12b19b88eae3cb574dedbd0252a2d43ea7e6de13e2" - -[[package]] -name = "dioxus-desktop" -version = "0.5.2" -dependencies = [ - "async-trait", - "cocoa", - "core-foundation", - "dioxus-cli-config", - "dioxus-core", - "dioxus-hooks", - "dioxus-hot-reload", - "dioxus-html", - "dioxus-interpreter-js", - "dunce", - "futures-channel", - "futures-util", - "generational-box", - "global-hotkey", - "infer", - "muda", - "objc", - "objc_id", - "rfd", - "rustc-hash", - "serde", - "serde_json", - "signal-hook", - "slab", - "tao", - "thiserror", - "tokio", - "tracing", - "urlencoding", - "webbrowser", - "wry", -] - -[[package]] -name = "dioxus-fullstack" -version = "0.5.2" -dependencies = [ - "anymap", - "async-trait", - "axum", - "base64 0.21.7", - "bytes", - "ciborium", - "dioxus-cli-config", - "dioxus-desktop", - "dioxus-hot-reload", - "dioxus-lib", - "dioxus-ssr", - "dioxus-web", - "dioxus_server_macro", - "futures-util", - "http 1.1.0", - "hyper 1.3.1", - "once_cell", - "pin-project", - "serde", - "serde_json", - "server_fn", - "thiserror", - "tokio", - "tokio-stream", - "tokio-util", - "tower", - "tower-http", - "tower-layer", - "tracing", - "tracing-futures", - "web-sys", -] - -[[package]] -name = "dioxus-hooks" -version = "0.5.2" -dependencies = [ - "dioxus-core", - "dioxus-debug-cell", - "dioxus-signals", - "futures-channel", - "futures-util", - "generational-box", - "slab", - "thiserror", - "tracing", -] - -[[package]] -name = "dioxus-hot-reload" -version = "0.5.2" -dependencies = [ - "axum", - "chrono", - "dioxus-core", - "dioxus-html", - "dioxus-rsx", - "execute", - "futures-util", - "ignore", - "interprocess-docfix", - "notify", - "once_cell", - "serde", - "serde_json", - "tokio", - "tokio-stream", - "tracing", -] - -[[package]] -name = "dioxus-html" -version = "0.5.2" -dependencies = [ - "async-trait", - "dioxus-core", - "dioxus-html-internal-macro", - "dioxus-rsx", - "enumset", - "euclid", - "futures-channel", - "generational-box", - "keyboard-types", - "serde", - "serde-value", - "serde_json", - "serde_repr", - "tokio", - "tracing", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "dioxus-html-internal-macro" -version = "0.5.2" -dependencies = [ - "convert_case 0.6.0", - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "dioxus-interpreter-js" -version = "0.5.2" -dependencies = [ - "dioxus-core", - "dioxus-html", - "js-sys", - "md5", - "sledgehammer_bindgen", - "sledgehammer_utils", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "dioxus-lib" -version = "0.5.2" -dependencies = [ - "dioxus-config-macro", - "dioxus-core", - "dioxus-core-macro", - "dioxus-hooks", - "dioxus-html", - "dioxus-rsx", - "dioxus-signals", -] - -[[package]] -name = "dioxus-liveview" -version = "0.5.2" -dependencies = [ - "axum", - "dioxus-cli-config", - "dioxus-core", - "dioxus-hot-reload", - "dioxus-html", - "dioxus-interpreter-js", - "futures-channel", - "futures-util", - "generational-box", - "rustc-hash", - "serde", - "serde_json", - "slab", - "thiserror", - "tokio", - "tokio-stream", - "tokio-util", - "tracing", -] - -[[package]] -name = "dioxus-logger" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe09dc9773dc1f1bb0d38529203d6555d08f67aadca5cf955ac3d1a9e69880" -dependencies = [ - "console_error_panic_hook", - "tracing", - "tracing-subscriber", - "tracing-wasm", -] - -[[package]] -name = "dioxus-router" -version = "0.5.2" -dependencies = [ - "dioxus-cli-config", - "dioxus-fullstack", - "dioxus-lib", - "dioxus-router-macro", - "dioxus-ssr", - "gloo", - "gloo-utils 0.1.7", - "http 1.1.0", - "js-sys", - "tokio", - "tracing", - "url", - "urlencoding", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "dioxus-router-macro" -version = "0.5.2" -dependencies = [ - "proc-macro2", - "quote", - "slab", - "syn 2.0.66", -] - -[[package]] -name = "dioxus-rsx" -version = "0.5.2" -dependencies = [ - "dioxus-core", - "internment", - "krates", - "proc-macro2", - "quote", - "syn 2.0.66", - "tracing", -] - -[[package]] -name = "dioxus-sdk" -version = "0.5.0" -source = "git+https://github.com/Dioxuslabs/sdk#d49812fbc9d4506bd3b1ec994f45ef4447f34c79" -dependencies = [ - "cfg-if", - "dioxus", - "dioxus-signals", - "directories", - "futures-util", - "js-sys", - "once_cell", - "postcard", - "rustc-hash", - "serde", - "tokio", - "tracing", - "uuid", - "wasm-bindgen", - "web-sys", - "yazi", -] - -[[package]] -name = "dioxus-signals" -version = "0.5.2" -dependencies = [ - "dioxus-core", - "futures-channel", - "futures-util", - "generational-box", - "once_cell", - "parking_lot", - "rustc-hash", - "serde", - "tracing", -] - -[[package]] -name = "dioxus-ssr" -version = "0.5.2" -dependencies = [ - "askama_escape", - "async-trait", - "chrono", - "dioxus-cli-config", - "dioxus-core", - "dioxus-html", - "generational-box", - "http 1.1.0", - "lru", - "rustc-hash", - "serde_json", - "thiserror", - "tokio", - "tracing", -] - -[[package]] -name = "dioxus-static-site-generation" -version = "0.5.2" -dependencies = [ - "axum", - "dioxus-cli-config", - "dioxus-fullstack", - "dioxus-hot-reload", - "dioxus-lib", - "dioxus-router", - "dioxus-ssr", - "dioxus-web", - "http 1.1.0", - "tokio", - "tower", - "tower-http", - "tracing", -] - -[[package]] -name = "dioxus-web" -version = "0.5.2" -dependencies = [ - "async-trait", - "console_error_panic_hook", - "dioxus-core", - "dioxus-html", - "dioxus-interpreter-js", - "futures-channel", - "futures-util", - "generational-box", - "js-sys", - "rustc-hash", - "serde", - "serde-wasm-bindgen", - "serde_json", - "tracing", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "dioxus_server_macro" -version = "0.5.2" -dependencies = [ - "convert_case 0.6.0", - "proc-macro2", - "quote", - "server_fn_macro", - "syn 2.0.66", -] - -[[package]] -name = "directories" -version = "4.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-sys" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - -[[package]] -name = "dispatch" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" - -[[package]] -name = "dlopen2" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1297103d2bbaea85724fcee6294c2d50b1081f9ad47d0f6f6f61eda65315a6" -dependencies = [ - "dlopen2_derive", - "libc", - "once_cell", - "winapi", -] - -[[package]] -name = "dlopen2_derive" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "dtoa" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" - -[[package]] -name = "dtoa-short" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbaceec3c6e4211c79e7b1800fb9680527106beb2f9c51904a3210c03a448c74" -dependencies = [ - "dtoa", -] - -[[package]] -name = "dunce" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" - -[[package]] -name = "dyn-clone" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" - -[[package]] -name = "ecdsa" -version = "0.16.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" -dependencies = [ - "der", - "digest", - "elliptic-curve", - "rfc6979", - "signature", - "spki", -] - -[[package]] -name = "ed25519" -version = "2.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" -dependencies = [ - "pkcs8", - "signature", -] - -[[package]] -name = "ed25519-dalek" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" -dependencies = [ - "curve25519-dalek", - "ed25519", - "serde", - "sha2", - "subtle", - "zeroize", -] - -[[package]] -name = "either" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" - -[[package]] -name = "elliptic-curve" -version = "0.13.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" -dependencies = [ - "base16ct", - "crypto-bigint", - "digest", - "ff", - "generic-array 0.14.7", - "group", - "hkdf", - "pem-rfc7468", - "pkcs8", - "rand_core 0.6.4", - "sec1", - "subtle", - "zeroize", -] - -[[package]] -name = "embedded-io" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" - -[[package]] -name = "encoding_rs" -version = "0.8.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" -dependencies = [ - "cfg-if", -] - -[[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.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3278c9d5fb675e0a51dabcf4c0d355f692b064171535ba72361be1528a9d8e8d" -dependencies = [ - "enumflags2_derive", - "serde", -] - -[[package]] -name = "enumflags2_derive" -version = "0.7.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c785274071b1b420972453b306eeca06acf4633829db4223b58a2a8c5953bc4" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "enumset" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "226c0da7462c13fb57e5cc9e0dc8f0635e7d27f276a3a7fd30054647f669007d" -dependencies = [ - "enumset_derive", -] - -[[package]] -name = "enumset_derive" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08b6c6ab82d70f08844964ba10c7babb716de2ecaeab9be5717918a5177d3af" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[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.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "euclid" -version = "0.22.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0f0eb73b934648cd7a4a61f1b15391cd95dab0b4da6e2e66c2a072c144b4a20" -dependencies = [ - "num-traits", - "serde", -] - -[[package]] -name = "event-listener" -version = "5.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener-strategy" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" -dependencies = [ - "event-listener", - "pin-project-lite", -] - -[[package]] -name = "execute" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a82608ee96ce76aeab659e9b8d3c2b787bffd223199af88c674923d861ada10" -dependencies = [ - "execute-command-macro", - "execute-command-tokens", - "generic-array 1.0.0", -] - -[[package]] -name = "execute-command-macro" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90dec53d547564e911dc4ff3ecb726a64cf41a6fa01a2370ebc0d95175dd08bd" -dependencies = [ - "execute-command-macro-impl", -] - -[[package]] -name = "execute-command-macro-impl" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce8cd46a041ad005ab9c71263f9a0ff5b529eac0fe4cc9b4a20f4f0765d8cf4b" -dependencies = [ - "execute-command-tokens", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "execute-command-tokens" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69dc321eb6be977f44674620ca3aa21703cb20ffbe560e1ae97da08401ffbcad" - -[[package]] -name = "fastrand" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" - -[[package]] -name = "fdeflate" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" -dependencies = [ - "simd-adler32", -] - -[[package]] -name = "ff" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" -dependencies = [ - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "fiat-crypto" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" - -[[package]] -name = "field-offset" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" -dependencies = [ - "memoffset", - "rustc_version", -] - -[[package]] -name = "filetime" -version = "0.2.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall 0.4.1", - "windows-sys 0.52.0", -] - -[[package]] -name = "fixedbitset" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" - -[[package]] -name = "flate2" -version = "1.0.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foreign-types" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" -dependencies = [ - "foreign-types-macros", - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-macros" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[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 = "fsevent-sys" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" -dependencies = [ - "libc", -] - -[[package]] -name = "futf" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" -dependencies = [ - "mac", - "new_debug_unreachable", -] - -[[package]] -name = "futures" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" - -[[package]] -name = "futures-executor" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" - -[[package]] -name = "futures-lite" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" -dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "parking", - "pin-project-lite", -] - -[[package]] -name = "futures-macro" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "futures-sink" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" - -[[package]] -name = "futures-task" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" - -[[package]] -name = "futures-util" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "fxhash" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" -dependencies = [ - "byteorder", -] - -[[package]] -name = "gdk" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5ba081bdef3b75ebcdbfc953699ed2d7417d6bd853347a42a37d76406a33646" -dependencies = [ - "cairo-rs", - "gdk-pixbuf", - "gdk-sys", - "gio", - "glib", - "libc", - "pango", -] - -[[package]] -name = "gdk-pixbuf" -version = "0.18.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" -dependencies = [ - "gdk-pixbuf-sys", - "gio", - "glib", - "libc", - "once_cell", -] - -[[package]] -name = "gdk-pixbuf-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" -dependencies = [ - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "gdk-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31ff856cb3386dae1703a920f803abafcc580e9b5f711ca62ed1620c25b51ff2" -dependencies = [ - "cairo-sys-rs", - "gdk-pixbuf-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "pango-sys", - "pkg-config", - "system-deps", -] - -[[package]] -name = "gdkwayland-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a90fbf5c033c65d93792192a49a8efb5bb1e640c419682a58bb96f5ae77f3d4a" -dependencies = [ - "gdk-sys", - "glib-sys", - "gobject-sys", - "libc", - "pkg-config", - "system-deps", -] - -[[package]] -name = "gdkx11" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2ea8a4909d530f79921290389cbd7c34cb9d623bfe970eaae65ca5f9cd9cce" -dependencies = [ - "gdk", - "gdkx11-sys", - "gio", - "glib", - "libc", - "x11", -] - -[[package]] -name = "gdkx11-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fee8f00f4ee46cad2939b8990f5c70c94ff882c3028f3cc5abf950fa4ab53043" -dependencies = [ - "gdk-sys", - "glib-sys", - "libc", - "system-deps", - "x11", -] - -[[package]] -name = "generational-box" -version = "0.5.2" -dependencies = [ - "parking_lot", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", - "zeroize", -] - -[[package]] -name = "generic-array" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe739944a5406424e080edccb6add95685130b9f160d5407c639c7df0c5836b0" -dependencies = [ - "typenum", -] - -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - -[[package]] -name = "getrandom" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi 0.11.0+wasi-snapshot-preview1", - "wasm-bindgen", -] - -[[package]] -name = "gimli" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" - -[[package]] -name = "gio" -version = "0.18.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-util", - "gio-sys", - "glib", - "libc", - "once_cell", - "pin-project-lite", - "smallvec", - "thiserror", -] - -[[package]] -name = "gio-sys" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", - "winapi", -] - -[[package]] -name = "glib" -version = "0.18.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" -dependencies = [ - "bitflags 2.5.0", - "futures-channel", - "futures-core", - "futures-executor", - "futures-task", - "futures-util", - "gio-sys", - "glib-macros", - "glib-sys", - "gobject-sys", - "libc", - "memchr", - "once_cell", - "smallvec", - "thiserror", -] - -[[package]] -name = "glib-macros" -version = "0.18.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" -dependencies = [ - "heck 0.4.1", - "proc-macro-crate 2.0.2", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "glib-sys" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" -dependencies = [ - "libc", - "system-deps", -] - -[[package]] -name = "global-hotkey" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89cb13e8c52c87e28a46eae3e5e65b8f0cd465c4c9e67b13d56c70412e792bc3" -dependencies = [ - "bitflags 2.5.0", - "cocoa", - "crossbeam-channel", - "keyboard-types", - "objc", - "once_cell", - "thiserror", - "windows-sys 0.52.0", - "x11-dl", -] - -[[package]] -name = "globset" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" -dependencies = [ - "aho-corasick", - "bstr", - "log", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "gloo" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28999cda5ef6916ffd33fb4a7b87e1de633c47c0dc6d97905fee1cdaa142b94d" -dependencies = [ - "gloo-console", - "gloo-dialogs", - "gloo-events", - "gloo-file", - "gloo-history", - "gloo-net 0.3.1", - "gloo-render", - "gloo-storage", - "gloo-timers", - "gloo-utils 0.1.7", - "gloo-worker", -] - -[[package]] -name = "gloo-console" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b7ce3c05debe147233596904981848862b068862e9ec3e34be446077190d3f" -dependencies = [ - "gloo-utils 0.1.7", - "js-sys", - "serde", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gloo-dialogs" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67062364ac72d27f08445a46cab428188e2e224ec9e37efdba48ae8c289002e6" -dependencies = [ - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gloo-events" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b107f8abed8105e4182de63845afcc7b69c098b7852a813ea7462a320992fc" -dependencies = [ - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gloo-file" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d5564e570a38b43d78bdc063374a0c3098c4f0d64005b12f9bbe87e869b6d7" -dependencies = [ - "gloo-events", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gloo-history" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85725d90bf0ed47063b3930ef28e863658a7905989e9929a8708aab74a1d5e7f" -dependencies = [ - "gloo-events", - "gloo-utils 0.1.7", - "serde", - "serde-wasm-bindgen", - "serde_urlencoded", - "thiserror", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gloo-net" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a66b4e3c7d9ed8d315fd6b97c8b1f74a7c6ecbbc2320e65ae7ed38b7068cc620" -dependencies = [ - "futures-channel", - "futures-core", - "futures-sink", - "gloo-utils 0.1.7", - "http 0.2.12", - "js-sys", - "pin-project", - "serde", - "serde_json", - "thiserror", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "gloo-net" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43aaa242d1239a8822c15c645f02166398da4f8b5c4bae795c1f5b44e9eee173" -dependencies = [ - "futures-channel", - "futures-core", - "futures-sink", - "gloo-utils 0.2.0", - "http 0.2.12", - "js-sys", - "pin-project", - "serde", - "serde_json", - "thiserror", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "gloo-render" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd9306aef67cfd4449823aadcd14e3958e0800aa2183955a309112a84ec7764" -dependencies = [ - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gloo-storage" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d6ab60bf5dbfd6f0ed1f7843da31b41010515c745735c970e821945ca91e480" -dependencies = [ - "gloo-utils 0.1.7", - "js-sys", - "serde", - "serde_json", - "thiserror", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gloo-timers" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "gloo-utils" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037fcb07216cb3a30f7292bd0176b050b7b9a052ba830ef7d5d65f6dc64ba58e" -dependencies = [ - "js-sys", - "serde", - "serde_json", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gloo-utils" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" -dependencies = [ - "js-sys", - "serde", - "serde_json", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gloo-worker" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13471584da78061a28306d1359dd0178d8d6fc1c7c80e5e35d27260346e0516a" -dependencies = [ - "anymap2", - "bincode", - "gloo-console", - "gloo-utils 0.1.7", - "js-sys", - "serde", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "gobject-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" -dependencies = [ - "glib-sys", - "libc", - "system-deps", -] - -[[package]] -name = "group" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" -dependencies = [ - "ff", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "gtk" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93c4f5e0e20b60e10631a5f06da7fe3dda744b05ad0ea71fee2f47adf865890c" -dependencies = [ - "atk", - "cairo-rs", - "field-offset", - "futures-channel", - "gdk", - "gdk-pixbuf", - "gio", - "glib", - "gtk-sys", - "gtk3-macros", - "libc", - "pango", - "pkg-config", -] - -[[package]] -name = "gtk-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771437bf1de2c1c0b496c11505bdf748e26066bbe942dfc8f614c9460f6d7722" -dependencies = [ - "atk-sys", - "cairo-sys-rs", - "gdk-pixbuf-sys", - "gdk-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "pango-sys", - "system-deps", -] - -[[package]] -name = "gtk3-macros" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6063efb63db582968fb7df72e1ae68aa6360dcfb0a75143f34fc7d616bad75e" -dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "h2" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http 0.2.12", - "indexmap 2.2.6", - "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", - "crunchy", -] - -[[package]] -name = "hash32" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" -dependencies = [ - "byteorder", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", - "allocator-api2", -] - -[[package]] -name = "heapless" -version = "0.7.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" -dependencies = [ - "atomic-polyfill", - "hash32", - "rustc_version", - "serde", - "spin 0.9.8", - "stable_deref_trait", -] - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hkdf" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" -dependencies = [ - "hmac", -] - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] - -[[package]] -name = "home" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" -dependencies = [ - "windows-sys 0.52.0", -] - -[[package]] -name = "html5ever" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" -dependencies = [ - "log", - "mac", - "markup5ever", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "http" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" -dependencies = [ - "bytes", - "fnv", - "itoa 1.0.11", -] - -[[package]] -name = "http" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" -dependencies = [ - "bytes", - "fnv", - "itoa 1.0.11", -] - -[[package]] -name = "http-body" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" -dependencies = [ - "bytes", - "http 0.2.12", - "pin-project-lite", -] - -[[package]] -name = "http-body" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" -dependencies = [ - "bytes", - "http 1.1.0", -] - -[[package]] -name = "http-body-util" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" -dependencies = [ - "bytes", - "futures-core", - "http 1.1.0", - "http-body 1.0.0", - "pin-project-lite", -] - -[[package]] -name = "http-range-header" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08a397c49fec283e3d6211adbe480be95aae5f304cfb923e9970e08956d5168a" - -[[package]] -name = "httparse" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" - -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - -[[package]] -name = "hyper" -version = "0.14.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http 0.2.12", - "http-body 0.4.6", - "httparse", - "httpdate", - "itoa 1.0.11", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "http 1.1.0", - "http-body 1.0.0", - "httparse", - "httpdate", - "itoa 1.0.11", - "pin-project-lite", - "smallvec", - "tokio", -] - -[[package]] -name = "hyper-rustls" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" -dependencies = [ - "futures-util", - "http 0.2.12", - "hyper 0.14.29", - "rustls", - "tokio", - "tokio-rustls", -] - -[[package]] -name = "hyper-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b875924a60b96e5d7b9ae7b066540b1dd1cbd90d1828f54c92e02a283351c56" -dependencies = [ - "bytes", - "futures-util", - "http 1.1.0", - "http-body 1.0.0", - "hyper 1.3.1", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" -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 = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "idna" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "ignore" -version = "0.4.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1" -dependencies = [ - "crossbeam-deque", - "globset", - "log", - "memchr", - "regex-automata", - "same-file", - "walkdir", - "winapi-util", -] - -[[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.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" -dependencies = [ - "equivalent", - "hashbrown 0.14.5", - "serde", -] - -[[package]] -name = "infer" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6c16b11a665b26aeeb9b1d7f954cdeb034be38dd00adab4f2ae921a8fee804" -dependencies = [ - "cfb", -] - -[[package]] -name = "inotify" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" -dependencies = [ - "bitflags 1.3.2", - "inotify-sys", - "libc", -] - -[[package]] -name = "inotify-sys" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" -dependencies = [ - "libc", -] - -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "internment" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04e8e537b529b8674e97e9fb82c10ff168a290ac3867a0295f112061ffbca1ef" -dependencies = [ - "hashbrown 0.14.5", - "parking_lot", -] - -[[package]] -name = "interprocess-docfix" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b84ee245c606aeb0841649a9288e3eae8c61b853a8cd5c0e14450e96d53d28f" -dependencies = [ - "blocking", - "cfg-if", - "futures-core", - "futures-io", - "intmap", - "libc", - "once_cell", - "rustc_version", - "spinning", - "thiserror", - "to_method", - "winapi", -] - -[[package]] -name = "intmap" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae52f28f45ac2bc96edb7714de995cffc174a395fb0abf5bff453587c980d7b9" - -[[package]] -name = "inventory" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f958d3d68f4167080a18141e10381e7634563984a537f2a49a30fd8e53ac5767" - -[[package]] -name = "ipnet" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" - -[[package]] -name = "itoa" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" - -[[package]] -name = "javascriptcore-rs" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca5671e9ffce8ffba57afc24070e906da7fc4b1ba66f2cabebf61bf2ea257fcc" -dependencies = [ - "bitflags 1.3.2", - "glib", - "javascriptcore-rs-sys", -] - -[[package]] -name = "javascriptcore-rs-sys" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1be78d14ffa4b75b66df31840478fef72b51f8c2465d4ca7c194da9f7a5124" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "jni" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" -dependencies = [ - "cesu8", - "cfg-if", - "combine", - "jni-sys", - "log", - "thiserror", - "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 = "js-sys" -version = "0.3.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "keyboard-types" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" -dependencies = [ - "bitflags 2.5.0", - "serde", - "unicode-segmentation", -] - -[[package]] -name = "kqueue" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" -dependencies = [ - "kqueue-sys", - "libc", -] - -[[package]] -name = "kqueue-sys" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" -dependencies = [ - "bitflags 1.3.2", - "libc", -] - -[[package]] -name = "krates" -version = "0.16.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcb3baf2360eb25ad31f0ada3add63927ada6db457791979b82ac199f835cb9" -dependencies = [ - "cargo-platform", - "cargo_metadata", - "cfg-expr", - "petgraph", - "semver", -] - -[[package]] -name = "kuchikiki" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e4755b7b995046f510a7520c42b2fed58b77bd94d5a87a8eb43d2fd126da8" -dependencies = [ - "cssparser", - "html5ever", - "indexmap 1.9.3", - "matches", - "selectors", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -dependencies = [ - "spin 0.5.2", -] - -[[package]] -name = "libc" -version = "0.2.155" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" - -[[package]] -name = "libm" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" - -[[package]] -name = "libredox" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" -dependencies = [ - "bitflags 2.5.0", - "libc", -] - -[[package]] -name = "libxdo" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00333b8756a3d28e78def82067a377de7fa61b24909000aeaa2b446a948d14db" -dependencies = [ - "libxdo-sys", -] - -[[package]] -name = "libxdo-sys" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db23b9e7e2b7831bbd8aac0bbeeeb7b68cbebc162b227e7052e8e55829a09212" -dependencies = [ - "libc", - "x11", -] - -[[package]] -name = "linux-raw-sys" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" - -[[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 = "log" -version = "0.4.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" - -[[package]] -name = "longest-increasing-subsequence" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3bd0dd2cd90571056fdb71f6275fada10131182f84899f4b2a916e565d81d86" - -[[package]] -name = "lru" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" -dependencies = [ - "hashbrown 0.14.5", -] - -[[package]] -name = "mac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" - -[[package]] -name = "malloc_buf" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" -dependencies = [ - "libc", -] - -[[package]] -name = "markup5ever" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" -dependencies = [ - "log", - "phf 0.10.1", - "phf_codegen 0.10.0", - "string_cache", - "string_cache_codegen", - "tendril", -] - -[[package]] -name = "matches" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" - -[[package]] -name = "matchit" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" - -[[package]] -name = "md5" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" - -[[package]] -name = "memchr" -version = "2.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" - -[[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 = "mime_guess" -version = "2.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" -dependencies = [ - "mime", - "unicase", -] - -[[package]] -name = "miniz_oxide" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" -dependencies = [ - "adler", - "simd-adler32", -] - -[[package]] -name = "mio" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" -dependencies = [ - "libc", - "log", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.48.0", -] - -[[package]] -name = "muda" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c47e7625990fc1af2226ea4f34fb2412b03c12639fcb91868581eb3a6893453" -dependencies = [ - "cocoa", - "crossbeam-channel", - "gtk", - "keyboard-types", - "libxdo", - "objc", - "once_cell", - "png", - "thiserror", - "windows-sys 0.52.0", -] - -[[package]] -name = "multer" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b" -dependencies = [ - "bytes", - "encoding_rs", - "futures-util", - "http 1.1.0", - "httparse", - "memchr", - "mime", - "spin 0.9.8", - "version_check", -] - -[[package]] -name = "ndk" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" -dependencies = [ - "bitflags 1.3.2", - "jni-sys", - "ndk-sys", - "num_enum", - "raw-window-handle 0.5.2", - "thiserror", -] - -[[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.4.1+23.1.7779620" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cf2aae958bd232cac5069850591667ad422d263686d75b52a065f9badeee5a3" -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.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" -dependencies = [ - "bitflags 2.5.0", - "cfg-if", - "libc", - "memoffset", -] - -[[package]] -name = "nodrop" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" - -[[package]] -name = "notify" -version = "5.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "729f63e1ca555a43fe3efa4f3efdf4801c479da85b432242a7b726f353c88486" -dependencies = [ - "bitflags 1.3.2", - "crossbeam-channel", - "filetime", - "fsevent-sys", - "inotify", - "kqueue", - "libc", - "mio", - "walkdir", - "windows-sys 0.45.0", -] - -[[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "winapi", -] - -[[package]] -name = "num-bigint-dig" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" -dependencies = [ - "byteorder", - "lazy_static", - "libm", - "num-integer", - "num-iter", - "num-traits", - "rand 0.8.5", - "smallvec", - "zeroize", -] - -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - -[[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-iter" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" -dependencies = [ - "autocfg", - "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", - "libm", -] - -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "num_enum" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" -dependencies = [ - "num_enum_derive", -] - -[[package]] -name = "num_enum_derive" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" -dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "oauth2" -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c38841cdd844847e3e7c8d29cef9dcfed8877f8f56f9071f77843ecf3baf937f" -dependencies = [ - "base64 0.13.1", - "chrono", - "getrandom 0.2.15", - "http 0.2.12", - "rand 0.8.5", - "reqwest", - "serde", - "serde_json", - "serde_path_to_error", - "sha2", - "thiserror", - "url", -] - -[[package]] -name = "objc" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" -dependencies = [ - "malloc_buf", - "objc_exception", -] - -[[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_exception" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" -dependencies = [ - "cc", -] - -[[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.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" - -[[package]] -name = "openid_auth_demo" -version = "0.1.0" -dependencies = [ - "anyhow", - "console_error_panic_hook", - "dioxus", - "dioxus-logger", - "dioxus-sdk", - "form_urlencoded", - "log", - "openidconnect", - "serde", - "uuid", -] - -[[package]] -name = "openidconnect" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f47e80a9cfae4462dd29c41e987edd228971d6565553fbc14b8a11e666d91590" -dependencies = [ - "base64 0.13.1", - "chrono", - "dyn-clone", - "ed25519-dalek", - "hmac", - "http 0.2.12", - "itertools", - "log", - "oauth2", - "p256", - "p384", - "rand 0.8.5", - "rsa", - "serde", - "serde-value", - "serde_derive", - "serde_json", - "serde_path_to_error", - "serde_plain", - "serde_with", - "sha2", - "subtle", - "thiserror", - "url", -] - -[[package]] -name = "ordered-float" -version = "2.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" -dependencies = [ - "num-traits", -] - -[[package]] -name = "ordered-stream" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" -dependencies = [ - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - -[[package]] -name = "p256" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" -dependencies = [ - "ecdsa", - "elliptic-curve", - "primeorder", - "sha2", -] - -[[package]] -name = "p384" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" -dependencies = [ - "ecdsa", - "elliptic-curve", - "primeorder", - "sha2", -] - -[[package]] -name = "pango" -version = "0.18.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" -dependencies = [ - "gio", - "glib", - "libc", - "once_cell", - "pango-sys", -] - -[[package]] -name = "pango-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "parking" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" - -[[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", - "libc", - "redox_syscall 0.5.1", - "smallvec", - "windows-targets 0.52.5", -] - -[[package]] -name = "pem-rfc7468" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" -dependencies = [ - "base64ct", -] - -[[package]] -name = "percent-encoding" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" - -[[package]] -name = "petgraph" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" -dependencies = [ - "fixedbitset", - "indexmap 2.2.6", -] - -[[package]] -name = "phf" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" -dependencies = [ - "phf_macros", - "phf_shared 0.8.0", - "proc-macro-hack", -] - -[[package]] -name = "phf" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" -dependencies = [ - "phf_shared 0.10.0", -] - -[[package]] -name = "phf_codegen" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" -dependencies = [ - "phf_generator 0.8.0", - "phf_shared 0.8.0", -] - -[[package]] -name = "phf_codegen" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" -dependencies = [ - "phf_generator 0.10.0", - "phf_shared 0.10.0", -] - -[[package]] -name = "phf_generator" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" -dependencies = [ - "phf_shared 0.8.0", - "rand 0.7.3", -] - -[[package]] -name = "phf_generator" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" -dependencies = [ - "phf_shared 0.10.0", - "rand 0.8.5", -] - -[[package]] -name = "phf_macros" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" -dependencies = [ - "phf_generator 0.8.0", - "phf_shared 0.8.0", - "proc-macro-hack", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "phf_shared" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" -dependencies = [ - "siphasher", -] - -[[package]] -name = "phf_shared" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" -dependencies = [ - "siphasher", -] - -[[package]] -name = "pin-project" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" - -[[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.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1d5c74c9876f070d3e8fd503d748c7d974c3e48da8f41350fa5222ef9b4391" -dependencies = [ - "atomic-waker", - "fastrand", - "futures-io", -] - -[[package]] -name = "pkcs1" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" -dependencies = [ - "der", - "pkcs8", - "spki", -] - -[[package]] -name = "pkcs8" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" -dependencies = [ - "der", - "spki", -] - -[[package]] -name = "pkg-config" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" - -[[package]] -name = "platforms" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7" - -[[package]] -name = "png" -version = "0.17.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" -dependencies = [ - "bitflags 1.3.2", - "crc32fast", - "fdeflate", - "flate2", - "miniz_oxide", -] - -[[package]] -name = "polling" -version = "3.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6a007746f34ed64099e88783b0ae369eaa3da6392868ba262e2af9b8fbaea1" -dependencies = [ - "cfg-if", - "concurrent-queue", - "hermit-abi", - "pin-project-lite", - "rustix", - "tracing", - "windows-sys 0.52.0", -] - -[[package]] -name = "pollster" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2" - -[[package]] -name = "postcard" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a55c51ee6c0db07e68448e336cf8ea4131a620edefebf9893e759b2d793420f8" -dependencies = [ - "cobs", - "embedded-io", - "heapless", - "serde", -] - -[[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.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "precomputed-hash" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" - -[[package]] -name = "prettyplease" -version = "0.2.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" -dependencies = [ - "proc-macro2", - "syn 2.0.66", -] - -[[package]] -name = "primeorder" -version = "0.13.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" -dependencies = [ - "elliptic-curve", -] - -[[package]] -name = "proc-macro-crate" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" -dependencies = [ - "once_cell", - "toml_edit 0.19.15", -] - -[[package]] -name = "proc-macro-crate" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" -dependencies = [ - "toml_datetime", - "toml_edit 0.20.2", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro-hack" -version = "0.5.20+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" - -[[package]] -name = "proc-macro2" -version = "1.0.85" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", - "rand_pcg", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", -] - -[[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 0.6.4", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.15", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "rand_pcg" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "raw-window-handle" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" - -[[package]] -name = "raw-window-handle" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" - -[[package]] -name = "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.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" -dependencies = [ - "bitflags 2.5.0", -] - -[[package]] -name = "redox_users" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" -dependencies = [ - "getrandom 0.2.15", - "libredox", - "thiserror", -] - -[[package]] -name = "regex" -version = "1.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" - -[[package]] -name = "reqwest" -version = "0.11.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" -dependencies = [ - "base64 0.21.7", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.29", - "hyper-rustls", - "ipnet", - "js-sys", - "log", - "mime", - "mime_guess", - "once_cell", - "percent-encoding", - "pin-project-lite", - "rustls", - "rustls-pemfile", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper 0.1.2", - "system-configuration", - "tokio", - "tokio-rustls", - "tokio-util", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-streams", - "web-sys", - "webpki-roots", - "winreg", -] - -[[package]] -name = "rfc6979" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" -dependencies = [ - "hmac", - "subtle", -] - -[[package]] -name = "rfd" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25a73a7337fc24366edfca76ec521f51877b114e42dab584008209cca6719251" -dependencies = [ - "ashpd", - "block", - "dispatch", - "js-sys", - "log", - "objc", - "objc-foundation", - "objc_id", - "pollster", - "raw-window-handle 0.6.2", - "urlencoding", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "windows-sys 0.48.0", -] - -[[package]] -name = "ring" -version = "0.17.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" -dependencies = [ - "cc", - "cfg-if", - "getrandom 0.2.15", - "libc", - "spin 0.9.8", - "untrusted", - "windows-sys 0.52.0", -] - -[[package]] -name = "rsa" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" -dependencies = [ - "const-oid", - "digest", - "num-bigint-dig", - "num-integer", - "num-traits", - "pkcs1", - "pkcs8", - "rand_core 0.6.4", - "signature", - "spki", - "subtle", - "zeroize", -] - -[[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_version" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver", -] - -[[package]] -name = "rustix" -version = "0.38.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" -dependencies = [ - "bitflags 2.5.0", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.52.0", -] - -[[package]] -name = "rustls" -version = "0.21.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" -dependencies = [ - "log", - "ring", - "rustls-webpki", - "sct", -] - -[[package]] -name = "rustls-pemfile" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" -dependencies = [ - "base64 0.21.7", -] - -[[package]] -name = "rustls-webpki" -version = "0.101.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "rustversion" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" - -[[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 = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "sct" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "sec1" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" -dependencies = [ - "base16ct", - "der", - "generic-array 0.14.7", - "pkcs8", - "subtle", - "zeroize", -] - -[[package]] -name = "selectors" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe" -dependencies = [ - "bitflags 1.3.2", - "cssparser", - "derive_more", - "fxhash", - "log", - "matches", - "phf 0.8.0", - "phf_codegen 0.8.0", - "precomputed-hash", - "servo_arc", - "smallvec", - "thin-slice", -] - -[[package]] -name = "semver" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" -dependencies = [ - "serde", -] - -[[package]] -name = "send_wrapper" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" -dependencies = [ - "futures-core", -] - -[[package]] -name = "serde" -version = "1.0.203" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde-value" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" -dependencies = [ - "ordered-float", - "serde", -] - -[[package]] -name = "serde-wasm-bindgen" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3b143e2833c57ab9ad3ea280d21fd34e285a42837aeb0ee301f4f41890fa00e" -dependencies = [ - "js-sys", - "serde", - "wasm-bindgen", -] - -[[package]] -name = "serde_derive" -version = "1.0.203" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "serde_json" -version = "1.0.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" -dependencies = [ - "itoa 1.0.11", - "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 1.0.11", - "serde", -] - -[[package]] -name = "serde_plain" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1fc6db65a611022b23a0dec6975d63fb80a302cb3388835ff02c097258d50" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_qs" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0431a35568651e363364210c91983c1da5eb29404d9f0928b67d4ebcfa7d330c" -dependencies = [ - "percent-encoding", - "serde", - "thiserror", -] - -[[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.66", -] - -[[package]] -name = "serde_spanned" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" -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 1.0.11", - "ryu", - "serde", -] - -[[package]] -name = "serde_with" -version = "3.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad483d2ab0149d5a5ebcd9972a3852711e0153d863bf5a5d0391d28883c4a20" -dependencies = [ - "base64 0.22.1", - "chrono", - "hex", - "indexmap 1.9.3", - "indexmap 2.2.6", - "serde", - "serde_derive", - "serde_json", - "serde_with_macros", - "time", -] - -[[package]] -name = "serde_with_macros" -version = "3.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65569b702f41443e8bc8bbb1c5779bd0450bbe723b56198980e80ec45780bce2" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "server_fn" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06e6e5467a2cd93ce1accfdfd8b859404f0b3b2041131ffd774fabf666b8219" -dependencies = [ - "axum", - "bytes", - "const_format", - "dashmap", - "futures", - "gloo-net 0.5.0", - "http 1.1.0", - "http-body-util", - "hyper 1.3.1", - "inventory", - "js-sys", - "once_cell", - "reqwest", - "send_wrapper", - "serde", - "serde_json", - "serde_qs", - "server_fn_macro_default", - "thiserror", - "tower", - "tower-layer", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-streams", - "web-sys", - "xxhash-rust", -] - -[[package]] -name = "server_fn_macro" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09c216bb1c1ac890151397643c663c875a1836adf0b269be4e389cb1b48c173c" -dependencies = [ - "const_format", - "convert_case 0.6.0", - "proc-macro2", - "quote", - "syn 2.0.66", - "xxhash-rust", -] - -[[package]] -name = "server_fn_macro_default" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00783df297ec85ea605779f2fef9cbec98981dffe2e01e1a9845c102ee1f1ae6" -dependencies = [ - "server_fn_macro", - "syn 2.0.66", -] - -[[package]] -name = "servo_arc" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432" -dependencies = [ - "nodrop", - "stable_deref_trait", -] - -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha2" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "signal-hook" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" -dependencies = [ - "libc", - "signal-hook-registry", -] - -[[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 = "signature" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" -dependencies = [ - "digest", - "rand_core 0.6.4", -] - -[[package]] -name = "simd-adler32" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" - -[[package]] -name = "siphasher" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" - -[[package]] -name = "slab" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] - -[[package]] -name = "sledgehammer_bindgen" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcfaf791ff02f48f3518ce825d32cf419c13a43c1d8b1232f74ac89f339c46d2" -dependencies = [ - "sledgehammer_bindgen_macro", - "wasm-bindgen", -] - -[[package]] -name = "sledgehammer_bindgen_macro" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdd941cc539bd3dc694edaf9d0c4e1221d02baa67c6b45ec04fad1024d9e8139" -dependencies = [ - "quote", - "syn 2.0.66", -] - -[[package]] -name = "sledgehammer_utils" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f20798defa0e9d4eff9ca451c7f84774c7378a9c3b5a40112cfa2b3eadb97ae2" -dependencies = [ - "lru", - "once_cell", - "rustc-hash", -] - -[[package]] -name = "smallvec" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" - -[[package]] -name = "socket2" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "soup3" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "471f924a40f31251afc77450e781cb26d55c0b650842efafc9c6cbd2f7cc4f9f" -dependencies = [ - "futures-channel", - "gio", - "glib", - "libc", - "soup3-sys", -] - -[[package]] -name = "soup3-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ebe8950a680a12f24f15ebe1bf70db7af98ad242d9db43596ad3108aab86c27" -dependencies = [ - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] - -[[package]] -name = "spinning" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d4f0e86297cad2658d92a707320d87bf4e6ae1050287f51d19b67ef3f153a7b" -dependencies = [ - "lock_api", -] - -[[package]] -name = "spki" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" -dependencies = [ - "base64ct", - "der", -] - -[[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 = "string_cache" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" -dependencies = [ - "new_debug_unreachable", - "once_cell", - "parking_lot", - "phf_shared 0.10.0", - "precomputed-hash", - "serde", -] - -[[package]] -name = "string_cache_codegen" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" -dependencies = [ - "phf_generator 0.10.0", - "phf_shared 0.10.0", - "proc-macro2", - "quote", -] - -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - -[[package]] -name = "subtle" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" - -[[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.66" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "sync_wrapper" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - -[[package]] -name = "sync_wrapper" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" - -[[package]] -name = "system-configuration" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" -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 0.5.0", - "pkg-config", - "toml", - "version-compare", -] - -[[package]] -name = "tao" -version = "0.26.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69ebbccb78deb5a36744c079eea2981b4a48ecbbe6b1b2ffbaa528bea3f5e5db" -dependencies = [ - "bitflags 1.3.2", - "cocoa", - "core-foundation", - "core-graphics", - "crossbeam-channel", - "dispatch", - "dlopen2", - "gdkwayland-sys", - "gdkx11-sys", - "gtk", - "instant", - "jni", - "lazy_static", - "libc", - "log", - "ndk", - "ndk-context", - "ndk-sys", - "objc", - "once_cell", - "parking_lot", - "raw-window-handle 0.5.2", - "raw-window-handle 0.6.2", - "scopeguard", - "tao-macros", - "unicode-segmentation", - "url", - "windows 0.54.0", - "windows-version", - "x11-dl", -] - -[[package]] -name = "tao-macros" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec114582505d158b669b136e6851f85840c109819d77c42bb7c0709f727d18c2" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "target-lexicon" -version = "0.12.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" - -[[package]] -name = "tempfile" -version = "3.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" -dependencies = [ - "cfg-if", - "fastrand", - "rustix", - "windows-sys 0.52.0", -] - -[[package]] -name = "tendril" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" -dependencies = [ - "futf", - "mac", - "utf-8", -] - -[[package]] -name = "thin-slice" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" - -[[package]] -name = "thiserror" -version = "1.0.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "thread_local" -version = "1.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" -dependencies = [ - "cfg-if", - "once_cell", -] - -[[package]] -name = "time" -version = "0.3.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" -dependencies = [ - "deranged", - "itoa 1.0.11", - "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.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" -dependencies = [ - "num-conv", - "time-core", -] - -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "to_method" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7c4ceeeca15c8384bbc3e011dbd8fccb7f068a440b752b7d9b32ceb0ca0e2e8" - -[[package]] -name = "tokio" -version = "1.38.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" -dependencies = [ - "backtrace", - "bytes", - "libc", - "mio", - "num_cpus", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "windows-sys 0.48.0", -] - -[[package]] -name = "tokio-macros" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "tokio-rustls" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" -dependencies = [ - "rustls", - "tokio", -] - -[[package]] -name = "tokio-stream" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", - "tokio-util", -] - -[[package]] -name = "tokio-tungstenite" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" -dependencies = [ - "futures-util", - "log", - "tokio", - "tungstenite", -] - -[[package]] -name = "tokio-util" -version = "0.7.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "futures-util", - "hashbrown 0.14.5", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "toml" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit 0.20.2", -] - -[[package]] -name = "toml_datetime" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_edit" -version = "0.19.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" -dependencies = [ - "indexmap 2.2.6", - "toml_datetime", - "winnow", -] - -[[package]] -name = "toml_edit" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" -dependencies = [ - "indexmap 2.2.6", - "serde", - "serde_spanned", - "toml_datetime", - "winnow", -] - -[[package]] -name = "tower" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" -dependencies = [ - "futures-core", - "futures-util", - "pin-project", - "pin-project-lite", - "tokio", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-http" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" -dependencies = [ - "bitflags 2.5.0", - "bytes", - "futures-util", - "http 1.1.0", - "http-body 1.0.0", - "http-body-util", - "http-range-header", - "httpdate", - "mime", - "mime_guess", - "percent-encoding", - "pin-project-lite", - "tokio", - "tokio-util", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-layer" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" - -[[package]] -name = "tower-service" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" - -[[package]] -name = "tracing" -version = "0.1.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" -dependencies = [ - "log", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "tracing-core" -version = "0.1.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-futures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" -dependencies = [ - "pin-project", - "tracing", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" -dependencies = [ - "nu-ansi-term", - "sharded-slab", - "smallvec", - "thread_local", - "tracing-core", - "tracing-log", -] - -[[package]] -name = "tracing-wasm" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4575c663a174420fa2d78f4108ff68f65bf2fbb7dd89f33749b6e826b3626e07" -dependencies = [ - "tracing", - "tracing-subscriber", - "wasm-bindgen", -] - -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - -[[package]] -name = "tungstenite" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" -dependencies = [ - "byteorder", - "bytes", - "data-encoding", - "http 1.1.0", - "httparse", - "log", - "rand 0.8.5", - "sha1", - "thiserror", - "url", - "utf-8", -] - -[[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", -] - -[[package]] -name = "unicase" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" -dependencies = [ - "version_check", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" - -[[package]] -name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "unicode-normalization" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-segmentation" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" - -[[package]] -name = "unicode-xid" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "url" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" -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 = "uuid" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" -dependencies = [ - "getrandom 0.2.15", - "wasm-bindgen", -] - -[[package]] -name = "valuable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - -[[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.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[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 = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - -[[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.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn 2.0.66", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" - -[[package]] -name = "wasm-streams" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" -dependencies = [ - "futures-util", - "js-sys", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "web-sys" -version = "0.3.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webbrowser" -version = "0.8.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db67ae75a9405634f5882791678772c94ff5f16a66535aae186e26aa0841fc8b" -dependencies = [ - "core-foundation", - "home", - "jni", - "log", - "ndk-context", - "objc", - "raw-window-handle 0.5.2", - "url", - "web-sys", -] - -[[package]] -name = "webkit2gtk" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76b1bc1e54c581da1e9f179d0b38512ba358fb1af2d634a1affe42e37172361a" -dependencies = [ - "bitflags 1.3.2", - "cairo-rs", - "gdk", - "gdk-sys", - "gio", - "gio-sys", - "glib", - "glib-sys", - "gobject-sys", - "gtk", - "gtk-sys", - "javascriptcore-rs", - "libc", - "once_cell", - "soup3", - "webkit2gtk-sys", -] - -[[package]] -name = "webkit2gtk-sys" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62daa38afc514d1f8f12b8693d30d5993ff77ced33ce30cd04deebc267a6d57c" -dependencies = [ - "bitflags 1.3.2", - "cairo-sys-rs", - "gdk-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "gtk-sys", - "javascriptcore-rs-sys", - "libc", - "pkg-config", - "soup3-sys", - "system-deps", -] - -[[package]] -name = "webpki-roots" -version = "0.25.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" - -[[package]] -name = "webview2-com" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0ae9c7e420783826cf769d2c06ac9ba462f450eca5893bb8c6c6529a4e5dd33" -dependencies = [ - "webview2-com-macros", - "webview2-com-sys", - "windows 0.52.0", - "windows-core 0.52.0", - "windows-implement 0.52.0", - "windows-interface 0.52.0", -] - -[[package]] -name = "webview2-com-macros" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1345798ecd8122468840bcdf1b95e5dc6d2206c5e4b0eafa078d061f59c9bc" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "webview2-com-sys" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6ad85fceee6c42fa3d61239eba5a11401bf38407a849ed5ea1b407df08cca72" -dependencies = [ - "thiserror", - "windows 0.52.0", - "windows-core 0.52.0", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" -dependencies = [ - "windows-sys 0.52.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.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" -dependencies = [ - "windows-core 0.52.0", - "windows-implement 0.52.0", - "windows-interface 0.52.0", - "windows-targets 0.52.5", -] - -[[package]] -name = "windows" -version = "0.54.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49" -dependencies = [ - "windows-core 0.54.0", - "windows-implement 0.53.0", - "windows-interface 0.53.0", - "windows-targets 0.52.5", -] - -[[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.5", -] - -[[package]] -name = "windows-core" -version = "0.54.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65" -dependencies = [ - "windows-result", - "windows-targets 0.52.5", -] - -[[package]] -name = "windows-implement" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12168c33176773b86799be25e2a2ba07c7aab9968b37541f1094dbd7a60c8946" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "windows-implement" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942ac266be9249c84ca862f0a164a39533dc2f6f33dc98ec89c8da99b82ea0bd" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "windows-interface" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d8dc32e0095a7eeccebd0e3f09e9509365ecb3fc6ac4d6f5f14a3f6392942d1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "windows-interface" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da33557140a288fae4e1d5f8873aaf9eb6613a9cf82c3e070223ff177f598b60" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "windows-result" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "749f0da9cc72d82e600d8d2e44cadd0b9eedb9038f71a1c58556ac1c5791813b" -dependencies = [ - "windows-targets 0.52.5", -] - -[[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.5", -] - -[[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.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" -dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", - "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", -] - -[[package]] -name = "windows-version" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6998aa457c9ba8ff2fb9f13e9d2a930dabcea28f1d0ab94d687d8b3654844515" -dependencies = [ - "windows-targets 0.52.5", -] - -[[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.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" - -[[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.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" - -[[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.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" - -[[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.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" - -[[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.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" - -[[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.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" - -[[package]] -name = "winnow" -version = "0.5.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" -dependencies = [ - "memchr", -] - -[[package]] -name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - -[[package]] -name = "wry" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b717040ba9771fd88eb428c6ea6b555f8e734ff8534f02c13e8f10d97f5935e" -dependencies = [ - "base64 0.21.7", - "block", - "cfg_aliases", - "cocoa", - "core-graphics", - "crossbeam-channel", - "dunce", - "gdkx11", - "gtk", - "html5ever", - "http 0.2.12", - "javascriptcore-rs", - "jni", - "kuchikiki", - "libc", - "log", - "ndk", - "ndk-context", - "ndk-sys", - "objc", - "objc_id", - "once_cell", - "percent-encoding", - "raw-window-handle 0.6.2", - "serde", - "serde_json", - "sha2", - "soup3", - "tao-macros", - "thiserror", - "webkit2gtk", - "webkit2gtk-sys", - "webview2-com", - "windows 0.52.0", - "windows-implement 0.52.0", - "windows-version", - "x11-dl", -] - -[[package]] -name = "x11" -version = "2.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" -dependencies = [ - "libc", - "pkg-config", -] - -[[package]] -name = "x11-dl" -version = "2.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" -dependencies = [ - "libc", - "once_cell", - "pkg-config", -] - -[[package]] -name = "xdg-home" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21e5a325c3cb8398ad6cf859c1135b25dd29e186679cf2da7581d9679f63b38e" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "xxhash-rust" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927da81e25be1e1a2901d59b81b37dd2efd1fc9c9345a55007f09bf5a2d3ee03" - -[[package]] -name = "yazi" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c94451ac9513335b5e23d7a8a2b61a7102398b8cca5160829d313e84c9d98be1" - -[[package]] -name = "zbus" -version = "4.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b8e3d6ae3342792a6cc2340e4394334c7402f3d793b390d2c5494a4032b3030" -dependencies = [ - "async-broadcast", - "async-executor", - "async-fs", - "async-io", - "async-lock", - "async-process", - "async-recursion", - "async-task", - "async-trait", - "blocking", - "derivative", - "enumflags2", - "event-listener", - "futures-core", - "futures-sink", - "futures-util", - "hex", - "nix", - "ordered-stream", - "rand 0.8.5", - "serde", - "serde_repr", - "sha1", - "static_assertions", - "tracing", - "uds_windows", - "windows-sys 0.52.0", - "xdg-home", - "zbus_macros", - "zbus_names", - "zvariant", -] - -[[package]] -name = "zbus_macros" -version = "4.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7a3e850ff1e7217a3b7a07eba90d37fe9bb9e89a310f718afcde5885ca9b6d7" -dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro2", - "quote", - "regex", - "syn 1.0.109", - "zvariant_utils", -] - -[[package]] -name = "zbus_names" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" -dependencies = [ - "serde", - "static_assertions", - "zvariant", -] - -[[package]] -name = "zerocopy" -version = "0.7.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "zeroize" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" - -[[package]] -name = "zvariant" -version = "4.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e09e8be97d44eeab994d752f341e67b3b0d80512a8b315a0671d47232ef1b65" -dependencies = [ - "endi", - "enumflags2", - "serde", - "static_assertions", - "url", - "zvariant_derive", -] - -[[package]] -name = "zvariant_derive" -version = "4.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72a5857e2856435331636a9fbb415b09243df4521a267c5bedcd5289b4d5799e" -dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro2", - "quote", - "syn 1.0.109", - "zvariant_utils", -] - -[[package]] -name = "zvariant_utils" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00bedb16a193cc12451873fee2a1bc6550225acece0e36f333e68326c73c8172" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] diff --git a/examples/openid_connect_demo/Cargo.toml b/examples/openid_connect_demo/Cargo.toml deleted file mode 100644 index 72ae74dc98..0000000000 --- a/examples/openid_connect_demo/Cargo.toml +++ /dev/null @@ -1,36 +0,0 @@ -[package] -name = "openid_auth_demo" -version = "0.1.0" -edition = "2021" -publish = false - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -anyhow = "1.0.86" -console_error_panic_hook = "0.1" -dioxus = { path = "../../packages/dioxus", default_features = true, features = [ - "router", - "signals", -], version = "*" } -dioxus-logger = "0.5.1" -dioxus-sdk = { git = "https://github.com/Dioxuslabs/sdk", features = [ - "storage", -] } -form_urlencoded = "1.2.1" -log = "0.4" -openidconnect = "3.5.0" -serde = { version = "1.0.203", features = ["derive"] } -uuid = "1.8" - -[features] -default = ["web"] -server = ["dioxus/axum"] -web = ["dioxus/web"] -desktop = ["dioxus/desktop"] -fullstack = ["dioxus/fullstack"] - -# since we're using dioxus from local path, inform dioxus-sdk to use it as well -[patch.crates-io] -dioxus = { path = "../../packages/dioxus" } -dioxus-signals = { path = "../../packages/signals" } diff --git a/examples/openid_connect_demo/Dioxus.toml b/examples/openid_connect_demo/Dioxus.toml deleted file mode 100644 index 3a130e2ea3..0000000000 --- a/examples/openid_connect_demo/Dioxus.toml +++ /dev/null @@ -1,35 +0,0 @@ -[application] - -# dioxus project name -name = "OpenID Connect authentication demo" - -# default platform -# you can also use `dioxus serve/build --platform XXX` to use other platform -# value: web | desktop -default_platform = "web" - -# Web `build` & `serve` dist path -out_dir = "dist" - -# resource (static) file folder -asset_dir = "public" - -# hot reload by default -hot_reload = true - -[web.app] - -# HTML title tag content -title = "OpenID Connect authentication demo" - -[web.watcher] - -index_on_404 = true - -watch_path = ["src"] - -[application.plugins] - -available = true - -required = [] diff --git a/examples/openid_connect_demo/README.md b/examples/openid_connect_demo/README.md deleted file mode 100644 index 7b0d100fd9..0000000000 --- a/examples/openid_connect_demo/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# OpenID Connect example to show how to authenticate an user - -The environment variables in [`.cargo/config.toml`](./.cargo/config.toml) must be set in order for this example to work. - -Once they are set, you can run `dx serve --platform web` or `dx serve --platform desktop`. - -### Environment variables summary - -- `DIOXUS_FRONT_ISSUER_URL`: The openid-connect's issuer url -- `DIOXUS_FRONT_CLIENT_ID`: The openid-connect's client id -- `DIOXUS_FRONT_CLIENT_SECRET`: The openid-connect's client secret -- `DIOXUS_FRONT_URL`: The url the frontend is supposed to be running on, it could be for example `http://localhost:8080` diff --git a/examples/openid_connect_demo/src/constants.rs b/examples/openid_connect_demo/src/constants.rs deleted file mode 100644 index 0a0b4950a6..0000000000 --- a/examples/openid_connect_demo/src/constants.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub const DIOXUS_FRONT_AUTH_TOKEN: &str = "auth_token"; -pub const DIOXUS_FRONT_AUTH_REQUEST: &str = "auth_request"; diff --git a/examples/openid_connect_demo/src/main.rs b/examples/openid_connect_demo/src/main.rs deleted file mode 100644 index 8487758595..0000000000 --- a/examples/openid_connect_demo/src/main.rs +++ /dev/null @@ -1,36 +0,0 @@ -#![allow(non_snake_case)] -use dioxus::prelude::*; -use dioxus_logger::tracing::Level; -use router::Route; - -use crate::oidc::ClientState; -use crate::storage::{use_auth_request_provider, use_auth_token_provider}; - -pub(crate) mod constants; -pub(crate) mod model; -pub(crate) mod oidc; -pub(crate) mod props; -pub(crate) mod router; -pub(crate) mod storage; -pub(crate) mod views; - -pub static CLIENT: GlobalSignal = Signal::global(ClientState::default); - -pub static DIOXUS_FRONT_ISSUER_URL: &str = env!("DIOXUS_FRONT_ISSUER_URL"); -pub static DIOXUS_FRONT_CLIENT_ID: &str = env!("DIOXUS_FRONT_CLIENT_ID"); -pub static DIOXUS_FRONT_CLIENT_SECRET: &str = env!("DIOXUS_FRONT_CLIENT_SECRET"); -pub static DIOXUS_FRONT_URL: &str = env!("DIOXUS_FRONT_URL"); - -fn App() -> Element { - use_auth_request_provider(); - use_auth_token_provider(); - rsx! { Router:: {} } -} - -fn main() { - dioxus_logger::init(Level::DEBUG).expect("failed to init logger"); - dioxus_sdk::set_dir!(); - console_error_panic_hook::set_once(); - log::info!("starting app"); - launch(App); -} diff --git a/examples/openid_connect_demo/src/model/mod.rs b/examples/openid_connect_demo/src/model/mod.rs deleted file mode 100644 index 68a79f5c0c..0000000000 --- a/examples/openid_connect_demo/src/model/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub(crate) mod user; diff --git a/examples/openid_connect_demo/src/model/user.rs b/examples/openid_connect_demo/src/model/user.rs deleted file mode 100644 index b1005fdd24..0000000000 --- a/examples/openid_connect_demo/src/model/user.rs +++ /dev/null @@ -1,7 +0,0 @@ -use uuid::Uuid; - -#[derive(PartialEq)] -pub struct User { - pub id: Uuid, - pub name: String, -} diff --git a/examples/openid_connect_demo/src/oidc.rs b/examples/openid_connect_demo/src/oidc.rs deleted file mode 100644 index 2c20c4e49b..0000000000 --- a/examples/openid_connect_demo/src/oidc.rs +++ /dev/null @@ -1,127 +0,0 @@ -use anyhow::Result; -use openidconnect::{ - core::{CoreClient, CoreIdToken, CoreResponseType, CoreTokenResponse}, - reqwest::async_http_client, - url::Url, - AuthenticationFlow, AuthorizationCode, ClaimsVerificationError, ClientId, ClientSecret, - CsrfToken, IssuerUrl, LogoutRequest, Nonce, ProviderMetadataWithLogout, RedirectUrl, - RefreshToken, -}; -use serde::{Deserialize, Serialize}; - -use crate::{props::client::ClientProps, DIOXUS_FRONT_CLIENT_ID}; - -#[derive(Clone, Debug, Default)] -pub struct ClientState { - pub oidc_client: Option, -} - -/// State that holds the nonce and authorization url and the nonce generated to log in an user -#[derive(Clone, PartialEq, Deserialize, Serialize, Default)] -pub struct AuthRequestState { - pub auth_request: Option, -} - -#[derive(Clone, PartialEq, Deserialize, Serialize)] -pub struct AuthRequest { - pub nonce: Nonce, - pub authorize_url: String, -} - -/// State the tokens returned once the user is authenticated -#[derive(Debug, Deserialize, Serialize, Default, Clone)] -pub struct AuthTokenState { - /// Token used to identify the user - pub id_token: Option, - /// Token used to refresh the tokens if they expire - pub refresh_token: Option, -} - -impl PartialEq for AuthTokenState { - fn eq(&self, other: &Self) -> bool { - self.id_token == other.id_token - && self.refresh_token.as_ref().map(|t| t.secret().clone()) - == other.refresh_token.as_ref().map(|t| t.secret().clone()) - } -} - -pub fn email( - client: CoreClient, - id_token: CoreIdToken, - nonce: Nonce, -) -> Result { - match id_token.claims(&client.id_token_verifier(), &nonce) { - Ok(claims) => Ok(claims.clone().email().unwrap().to_string()), - Err(error) => Err(error), - } -} - -pub fn authorize_url(client: CoreClient) -> AuthRequest { - let (authorize_url, _csrf_state, nonce) = client - .authorize_url( - AuthenticationFlow::::AuthorizationCode, - CsrfToken::new_random, - Nonce::new_random, - ) - .add_scope(openidconnect::Scope::new("email".to_string())) - .add_scope(openidconnect::Scope::new("profile".to_string())) - .url(); - AuthRequest { - authorize_url: authorize_url.to_string(), - nonce, - } -} - -pub async fn init_provider_metadata() -> Result { - let issuer_url = IssuerUrl::new(crate::DIOXUS_FRONT_ISSUER_URL.to_string())?; - Ok(ProviderMetadataWithLogout::discover_async(issuer_url, async_http_client).await?) -} - -pub async fn init_oidc_client() -> Result<(ClientId, CoreClient)> { - let client_id = ClientId::new(crate::DIOXUS_FRONT_CLIENT_ID.to_string()); - let provider_metadata = init_provider_metadata().await?; - let client_secret = Some(ClientSecret::new( - crate::DIOXUS_FRONT_CLIENT_SECRET.to_string(), - )); - let redirect_url = RedirectUrl::new(format!("{}/login", crate::DIOXUS_FRONT_URL))?; - - Ok(( - client_id.clone(), - CoreClient::from_provider_metadata(provider_metadata, client_id, client_secret) - .set_redirect_uri(redirect_url), - )) -} - -///TODO: Add pkce_pacifier -pub async fn token_response(oidc_client: CoreClient, code: String) -> Result { - // let (pkce_challenge, pkce_verifier) = PkceCodeChallenge::new_random_sha256(); - Ok(oidc_client - .exchange_code(AuthorizationCode::new(code.clone())) - // .set_pkce_verifier(pkce_verifier) - .request_async(async_http_client) - .await?) -} - -pub async fn exchange_refresh_token( - oidc_client: CoreClient, - refresh_token: RefreshToken, -) -> Result { - Ok(oidc_client - .exchange_refresh_token(&refresh_token) - .request_async(async_http_client) - .await?) -} - -pub async fn log_out_url(id_token_hint: CoreIdToken) -> Result { - let provider_metadata = init_provider_metadata().await?; - let end_session_url = provider_metadata - .additional_metadata() - .clone() - .end_session_endpoint - .unwrap(); - let logout_request: LogoutRequest = LogoutRequest::from(end_session_url); - Ok(logout_request - .set_client_id(ClientId::new(DIOXUS_FRONT_CLIENT_ID.to_string())) - .set_id_token_hint(&id_token_hint) - .http_get_url()) -} diff --git a/examples/openid_connect_demo/src/props/client.rs b/examples/openid_connect_demo/src/props/client.rs deleted file mode 100644 index 2e02111e16..0000000000 --- a/examples/openid_connect_demo/src/props/client.rs +++ /dev/null @@ -1,20 +0,0 @@ -use dioxus::prelude::*; -use openidconnect::{core::CoreClient, ClientId}; - -#[derive(Props, Clone, Debug)] -pub struct ClientProps { - pub client: CoreClient, - pub client_id: ClientId, -} - -impl PartialEq for ClientProps { - fn eq(&self, other: &Self) -> bool { - self.client_id == other.client_id - } -} - -impl ClientProps { - pub fn new(client_id: ClientId, client: CoreClient) -> Self { - ClientProps { client_id, client } - } -} diff --git a/examples/openid_connect_demo/src/props/mod.rs b/examples/openid_connect_demo/src/props/mod.rs deleted file mode 100644 index 1d33131531..0000000000 --- a/examples/openid_connect_demo/src/props/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub(crate) mod client; diff --git a/examples/openid_connect_demo/src/router.rs b/examples/openid_connect_demo/src/router.rs deleted file mode 100644 index 8f4b1b36a6..0000000000 --- a/examples/openid_connect_demo/src/router.rs +++ /dev/null @@ -1,16 +0,0 @@ -use crate::views::{header::AuthHeader, home::Home, login::Login, not_found::NotFound}; -use dioxus::prelude::*; - -#[derive(Routable, Clone)] -pub enum Route { - #[layout(AuthHeader)] - #[route("/")] - Home {}, - - // https://dioxuslabs.com/learn/0.4/router/reference/routes#query-segments - #[route("/login?:query_string")] - Login { query_string: String }, - #[end_layout] - #[route("/:..route")] - NotFound { route: Vec }, -} diff --git a/examples/openid_connect_demo/src/storage.rs b/examples/openid_connect_demo/src/storage.rs deleted file mode 100644 index c34819b965..0000000000 --- a/examples/openid_connect_demo/src/storage.rs +++ /dev/null @@ -1,35 +0,0 @@ -use dioxus::prelude::*; -use dioxus_sdk::storage::*; - -use crate::{ - constants::{DIOXUS_FRONT_AUTH_REQUEST, DIOXUS_FRONT_AUTH_TOKEN}, - oidc::{AuthRequestState, AuthTokenState}, -}; - -pub fn use_auth_token_provider() { - let stored_token = - use_storage::(DIOXUS_FRONT_AUTH_TOKEN.to_owned(), AuthTokenState::default); - - use_context_provider(move || stored_token); -} - -pub fn use_auth_token() -> Signal { - use_context() -} - -pub fn use_auth_request_provider() { - let stored_req = use_storage::( - DIOXUS_FRONT_AUTH_REQUEST.to_owned(), - AuthRequestState::default, - ); - - use_context_provider(move || stored_req); -} - -pub fn use_auth_request() -> Signal { - use_context() -} - -pub fn auth_request() -> Signal { - consume_context() -} diff --git a/examples/openid_connect_demo/src/views/header.rs b/examples/openid_connect_demo/src/views/header.rs deleted file mode 100644 index 274a0d22f0..0000000000 --- a/examples/openid_connect_demo/src/views/header.rs +++ /dev/null @@ -1,221 +0,0 @@ -use crate::storage::{auth_request, use_auth_request, use_auth_token}; -use crate::{ - oidc::{ - authorize_url, email, exchange_refresh_token, init_oidc_client, log_out_url, - AuthRequestState, AuthTokenState, ClientState, - }, - props::client::ClientProps, - router::Route, - CLIENT, -}; -use anyhow::Result; -use dioxus::prelude::*; -use dioxus::router::prelude::{Link, Outlet}; -use openidconnect::{url::Url, OAuth2TokenResponse, TokenResponse}; - -#[component] -pub fn LogOut() -> Element { - let mut auth_token = use_auth_token(); - let log_out_url_state = use_signal(|| None::>>); - match auth_token().id_token { - Some(id_token) => match &*log_out_url_state.read() { - Some(log_out_url_result) => match log_out_url_result { - Some(uri) => match uri { - Ok(uri) => { - rsx! { - Link { - onclick: move |_| { - auth_token.take(); - }, - to: uri.to_string(), - "Log out" - } - } - } - Err(error) => { - rsx! { div { "Failed to load disconnection url: {error:?}" } } - } - }, - None => { - rsx! { div { "Loading... Please wait" } } - } - }, - None => { - let logout_url_task = move || { - spawn({ - let mut log_out_url_state = log_out_url_state.to_owned(); - async move { - let logout_url = log_out_url(id_token).await; - let logout_url_option = Some(logout_url); - log_out_url_state.set(Some(logout_url_option)); - } - }) - }; - logout_url_task(); - rsx! { div { "Loading log out url... Please wait" } } - } - }, - None => { - rsx! {{}} - } - } -} - -#[component] -pub fn RefreshToken(props: ClientProps) -> Element { - let mut auth_token = use_auth_token(); - match auth_token().refresh_token { - Some(refresh_token) => { - rsx! { div { - onmounted: { - move |_| { - let client = props.client.clone(); - let refresh_token = refresh_token.clone(); - async move { - let exchange_refresh_token = - exchange_refresh_token(client, refresh_token).await; - match exchange_refresh_token { - Ok(response_token) => { - auth_token.set(AuthTokenState { - id_token: response_token.id_token().cloned(), - refresh_token: response_token.refresh_token().cloned(), - }); - } - Err(_error) => { - auth_token.take(); - auth_request().take(); - } - } - } - } - }, - "Refreshing session, please wait" - } } - } - None => { - rsx! { div { "Id token expired and no refresh token found" } } - } - } -} - -#[component] -pub fn LoadClient() -> Element { - let init_client_future = use_resource(move || async move { init_oidc_client().await }); - match &*init_client_future.read_unchecked() { - Some(Ok((client_id, client))) => rsx! { - div { - onmounted: { - let client_id = client_id.clone(); - let client = client.clone(); - move |_| { - *CLIENT.write() = ClientState { - oidc_client: Some(ClientProps::new(client_id.clone(), client.clone())), - }; - } - }, - "Client successfully loaded" - } - Outlet:: {} - }, - Some(Err(error)) => { - log::info! {"Failed to load client: {:?}", error}; - rsx! { - div { "Failed to load client: {error:?}" } - Outlet:: {} - } - } - None => { - rsx! { - div { - div { "Loading client, please wait" } - Outlet:: {} - } - } - } - } -} - -#[component] -pub fn AuthHeader() -> Element { - let client = CLIENT.read().oidc_client.clone(); - let mut auth_request = use_auth_request(); - let auth_token = use_auth_token(); - match (client, auth_request(), auth_token()) { - // We have everything we need to attempt to authenticate the user - (Some(client_props), current_auth_request, current_auth_token) => { - match current_auth_request.auth_request { - Some(new_auth_request) => { - match current_auth_token.id_token { - Some(id_token) => { - match email( - client_props.client.clone(), - id_token.clone(), - new_auth_request.nonce.clone(), - ) { - Ok(email) => { - rsx! { - div { - div { {email} } - LogOut {} - Outlet:: {} - } - } - } - // Id token failed to be decoded - Err(error) => match error { - // Id token failed to be decoded because it expired, we refresh it - openidconnect::ClaimsVerificationError::Expired(_message) => { - log::info!("Token expired"); - rsx! { - div { - RefreshToken { client_id: client_props.client_id, client: client_props.client } - Outlet:: {} - } - } - } - // Other issue with token decoding - _ => { - log::info!("Other issue with token"); - rsx! { - div { - div { "{error}" } - Outlet:: {} - } - } - } - }, - } - } - // User is not logged in - None => { - rsx! { - div { - Link { to: new_auth_request.authorize_url.clone(), "Log in" } - Outlet:: {} - } - } - } - } - } - None => { - rsx! { div { - onmounted: { - let client = client_props.client; - move |_| { - let new_auth_request = authorize_url(client.clone()); - auth_request.set(AuthRequestState { - auth_request: Some(new_auth_request), - }); - } - }, - "Loading nonce" - } } - } - } - } - // Client is not initialized yet, we need it for everything - (None, _, _) => { - rsx! { LoadClient {} } - } - } -} diff --git a/examples/openid_connect_demo/src/views/home.rs b/examples/openid_connect_demo/src/views/home.rs deleted file mode 100644 index d91b5c2037..0000000000 --- a/examples/openid_connect_demo/src/views/home.rs +++ /dev/null @@ -1,5 +0,0 @@ -use dioxus::prelude::*; - -pub fn Home() -> Element { - rsx! { div { "Hello world" } } -} diff --git a/examples/openid_connect_demo/src/views/login.rs b/examples/openid_connect_demo/src/views/login.rs deleted file mode 100644 index e3a4e0025b..0000000000 --- a/examples/openid_connect_demo/src/views/login.rs +++ /dev/null @@ -1,86 +0,0 @@ -use crate::{ - oidc::{token_response, AuthTokenState}, - router::Route, - storage::{auth_request, use_auth_token}, - CLIENT, -}; -use dioxus::prelude::*; -use dioxus::router::prelude::Link; -use openidconnect::{OAuth2TokenResponse, TokenResponse}; - -#[component] -pub fn Login(query_string: String) -> Element { - let client = CLIENT.read().oidc_client.clone(); - let mut auth_token = use_auth_token(); - let current_auth_token = auth_token(); - match client { - Some(client_props) => { - match ( - current_auth_token.id_token, - current_auth_token.refresh_token, - ) { - (Some(_id_token), Some(_refresh_token)) => { - rsx! { - div { "Sign in successful" } - Link { to: Route::Home {}, "Go back home" } - } - } - // If the refresh token is set but not the id_token, there was an error, we just go back home and reset their value - (None, Some(_)) | (Some(_), None) => { - rsx! { - div { "Error while attempting to log in" } - Link { - to: Route::Home {}, - onclick: move |_| { - auth_token.take(); - auth_request().take(); - }, - "Go back home" - } - } - } - (None, None) => { - let mut query_pairs = form_urlencoded::parse(query_string.as_bytes()); - let code_pair = query_pairs.find(|(key, _value)| key == "code"); - match code_pair { - Some((_key, code)) => { - let code = code.to_string(); - rsx! { div { - onmounted: { - move |_| { - let auth_code = code.to_string(); - let client_props = client_props.clone(); - async move { - let token_response_result = - token_response(client_props.client, auth_code).await; - match token_response_result { - Ok(token_response) => { - let id_token = token_response.id_token().unwrap(); - auth_token.set(AuthTokenState { - id_token: Some(id_token.clone()), - refresh_token: token_response - .refresh_token() - .cloned(), - }); - } - Err(error) => { - log::warn! {"{error}"}; - } - } - } - } - } - }} - } - None => { - rsx! { div { "No code provided" } } - } - } - } - } - } - _ => { - rsx! {{}} - } - } -} diff --git a/examples/openid_connect_demo/src/views/mod.rs b/examples/openid_connect_demo/src/views/mod.rs deleted file mode 100644 index 1b8e08a585..0000000000 --- a/examples/openid_connect_demo/src/views/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub(crate) mod header; -pub(crate) mod home; -pub(crate) mod login; -pub(crate) mod not_found; diff --git a/examples/openid_connect_demo/src/views/not_found.rs b/examples/openid_connect_demo/src/views/not_found.rs deleted file mode 100644 index 833c610674..0000000000 --- a/examples/openid_connect_demo/src/views/not_found.rs +++ /dev/null @@ -1,10 +0,0 @@ -use dioxus::prelude::*; - -#[component] -pub fn NotFound(route: Vec) -> Element { - rsx! { - div{ - {route.join("")} - } - } -} diff --git a/examples/optional_props.rs b/examples/optional_props.rs index ad54ed33cd..334852bea1 100644 --- a/examples/optional_props.rs +++ b/examples/optional_props.rs @@ -6,7 +6,7 @@ use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/overlay.rs b/examples/overlay.rs index e4e2e80592..59a9619d6a 100644 --- a/examples/overlay.rs +++ b/examples/overlay.rs @@ -11,7 +11,9 @@ use dioxus::desktop::{ use dioxus::prelude::*; fn main() { - LaunchBuilder::desktop().with_cfg(make_config()).launch(app); + dioxus::launch::builder() + .with_cfg(make_config()) + .launch(app); } fn app() -> Element { @@ -20,9 +22,9 @@ fn app() -> Element { _ = use_global_shortcut("cmd+g", move || show_overlay.toggle()); rsx! { - head::Link { + document::Link { rel: "stylesheet", - href: asset!("./examples/assets/overlay.css"), + href: asset!("/examples/assets/overlay.css"), } if show_overlay() { div { diff --git a/examples/popup.rs b/examples/popup.rs index 131d536edb..e708b9cd9f 100644 --- a/examples/popup.rs +++ b/examples/popup.rs @@ -5,7 +5,7 @@ use dioxus::prelude::*; use std::rc::Rc; fn main() { - launch_desktop(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/query_segment_search.rs b/examples/query_segment_search.rs index 018f475e45..4366cc0118 100644 --- a/examples/query_segment_search.rs +++ b/examples/query_segment_search.rs @@ -2,7 +2,7 @@ //! //! The enum router makes it easy to use your route as state in your app. This example shows how to use the router to encode search text into the url and decode it back into a string. //! -//! Run this example on desktop with +//! Run this example on desktop with //! ```sh //! dx serve --example query_segment_search //! ``` @@ -14,7 +14,7 @@ use dioxus::prelude::*; fn main() { - launch(|| { + dioxus::launch(|| { rsx! { Router:: {} } @@ -29,7 +29,7 @@ enum Route { // The each query segment must implement and Display. // You can use multiple query segments separated by `&`s. - #[route("/search?:query&:word_count")] + #[route("/search?:query&:word_count")] Search { query: String, word_count: usize, diff --git a/examples/read_size.rs b/examples/read_size.rs index 1965597cfa..dbde9353c7 100644 --- a/examples/read_size.rs +++ b/examples/read_size.rs @@ -9,7 +9,7 @@ use std::rc::Rc; use dioxus::{html::geometry::euclid::Rect, prelude::*}; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { @@ -28,7 +28,7 @@ fn app() -> Element { }; rsx!( - head::Link { rel: "stylesheet", href: asset!("./examples/assets/read_size.css") } + document::Stylesheet { href: asset!("/examples/assets/read_size.css") } div { width: "50%", height: "50%", diff --git a/examples/readme.rs b/examples/readme.rs index 8f412a749d..1c36c65c00 100644 --- a/examples/readme.rs +++ b/examples/readme.rs @@ -8,7 +8,7 @@ use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/reducer.rs b/examples/reducer.rs index 7c434e5858..32a15368e8 100644 --- a/examples/reducer.rs +++ b/examples/reducer.rs @@ -7,17 +7,17 @@ use dioxus::prelude::*; -const STYLE: &str = asset!("./examples/assets/radio.css"); +const STYLE: Asset = asset!("/examples/assets/radio.css"); fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { let mut state = use_signal(|| PlayerState { is_playing: false }); rsx!( - head::Link { rel: "stylesheet", href: STYLE } + document::Stylesheet { href: STYLE } h1 {"Select an option"} // Add some cute animations if the radio is playing! diff --git a/examples/resize.rs b/examples/resize.rs index eace774a0b..52b55bfb8c 100644 --- a/examples/resize.rs +++ b/examples/resize.rs @@ -8,14 +8,14 @@ use dioxus::prelude::*; use dioxus_elements::geometry::euclid::Size2D; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { let mut dimensions = use_signal(Size2D::zero); rsx!( - head::Link { rel: "stylesheet", href: asset!("./examples/assets/read_size.css") } + document::Stylesheet { href: asset!("/examples/assets/read_size.css") } div { width: "50%", height: "50%", diff --git a/examples/router.rs b/examples/router.rs index 13197f57e2..377c595418 100644 --- a/examples/router.rs +++ b/examples/router.rs @@ -8,12 +8,12 @@ use dioxus::prelude::*; -const STYLE: &str = asset!("./examples/assets/router.css"); +const STYLE: Asset = asset!("/examples/assets/router.css"); fn main() { - launch(|| { + dioxus::launch(|| { rsx! { - head::Link { rel: "stylesheet", href: STYLE } + document::Stylesheet { href: STYLE } Router:: {} } }); diff --git a/examples/router_resource.rs b/examples/router_resource.rs index de69f0e40b..6898585f3b 100644 --- a/examples/router_resource.rs +++ b/examples/router_resource.rs @@ -15,7 +15,7 @@ enum Route { } fn main() { - launch(App); + dioxus::launch(App); } #[component] diff --git a/examples/rsx_usage.rs b/examples/rsx_usage.rs index 3bffe8c700..b8ae72fd68 100644 --- a/examples/rsx_usage.rs +++ b/examples/rsx_usage.rs @@ -39,7 +39,7 @@ //! - Allow top-level fragments fn main() { - launch(app) + dioxus::launch(app) } use core::{fmt, str::FromStr}; @@ -228,6 +228,9 @@ fn app() -> Element { // Or we can shell out to a helper function {format_dollars(10, 50)} + + // some text? + {Some("hello world!")} } } } diff --git a/examples/scroll_to_top.rs b/examples/scroll_to_top.rs index 9f4f85955e..1cd91e4947 100644 --- a/examples/scroll_to_top.rs +++ b/examples/scroll_to_top.rs @@ -8,7 +8,7 @@ use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/shortcut.rs b/examples/shortcut.rs index d0192b9782..a6626c27b7 100644 --- a/examples/shortcut.rs +++ b/examples/shortcut.rs @@ -9,7 +9,7 @@ use dioxus::desktop::use_global_shortcut; use dioxus::prelude::*; fn main() { - launch_desktop(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/shorthand.rs b/examples/shorthand.rs index 33905d5710..bd2047a8e6 100644 --- a/examples/shorthand.rs +++ b/examples/shorthand.rs @@ -3,7 +3,7 @@ use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/signals.rs b/examples/signals.rs index 26db9685c1..6b881587bc 100644 --- a/examples/signals.rs +++ b/examples/signals.rs @@ -10,7 +10,7 @@ use async_std::task::sleep; use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/simple_list.rs b/examples/simple_list.rs index 3c1b9d71a1..32e1d3eb85 100644 --- a/examples/simple_list.rs +++ b/examples/simple_list.rs @@ -5,7 +5,7 @@ use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/simple_router.rs b/examples/simple_router.rs index e674f2f30e..0ec22a001e 100644 --- a/examples/simple_router.rs +++ b/examples/simple_router.rs @@ -5,7 +5,7 @@ use dioxus::prelude::*; fn main() { // Launch the router, using our `Route` component as the generic type // This will automatically boot the app to "/" unless otherwise specified - launch(|| rsx! { Router:: {} }); + dioxus::launch(|| rsx! { Router:: {} }); } /// By default, the Routable derive will use the name of the variant as the route diff --git a/examples/streams.rs b/examples/streams.rs index 6155cb35f9..a71b5b9c18 100644 --- a/examples/streams.rs +++ b/examples/streams.rs @@ -5,7 +5,7 @@ use dioxus::prelude::*; use futures_util::{future, stream, Stream, StreamExt}; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/suspense.rs b/examples/suspense.rs index 014bb3276a..14cd2573e9 100644 --- a/examples/suspense.rs +++ b/examples/suspense.rs @@ -15,7 +15,7 @@ use dioxus::desktop::{Config, LogicalSize, WindowBuilder}; use dioxus::prelude::*; fn main() { - LaunchBuilder::new() + dioxus::builder() .with_cfg(desktop! { Config::new().with_window( WindowBuilder::new() diff --git a/examples/svg.rs b/examples/svg.rs index d4c23d1ddb..b462f30288 100644 --- a/examples/svg.rs +++ b/examples/svg.rs @@ -10,7 +10,7 @@ use dioxus::prelude::*; use rand::{thread_rng, Rng}; fn main() { - launch(|| { + dioxus::launch(|| { rsx! { div { user_select: "none", webkit_user_select: "none", margin_left: "10%", margin_right: "10%", h1 { "Click die to generate a new value" } diff --git a/examples/tailwind/.gitignore b/examples/tailwind/.gitignore deleted file mode 100644 index 1521c8b765..0000000000 --- a/examples/tailwind/.gitignore +++ /dev/null @@ -1 +0,0 @@ -dist diff --git a/examples/tailwind/Cargo.toml b/examples/tailwind/Cargo.toml deleted file mode 100644 index 6660b87397..0000000000 --- a/examples/tailwind/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "dioxus-tailwind" -version = "0.0.0" -authors = [] -edition = "2021" -description = "A tailwindcss example using Dioxus" -license = "MIT OR Apache-2.0" -repository = "https://github.com/DioxusLabs/dioxus/" -homepage = "https://dioxuslabs.com" -documentation = "https://dioxuslabs.com" -publish = false - -[dependencies] -manganis = { workspace = true } - -[target.'cfg(not(target_arch = "wasm32"))'.dependencies] -dioxus = { path = "../../packages/dioxus", features = ["desktop"] } - -[target.'cfg(target_arch = "wasm32")'.dependencies] -dioxus = { path = "../../packages/dioxus", features = ["web"] } diff --git a/examples/tailwind/Dioxus.toml b/examples/tailwind/Dioxus.toml deleted file mode 100644 index af7fcf3b8f..0000000000 --- a/examples/tailwind/Dioxus.toml +++ /dev/null @@ -1,27 +0,0 @@ -[application] - -# App (Project) Name -name = "Tailwind CSS + Dioxus" - -# Dioxus App Default Platform -# desktop, web, mobile, ssr -default_platform = "web" - -# `build` & `serve` dist path -out_dir = "dist" - -# resource (public) file folder -asset_dir = "public" - -[web.app] - -# HTML title tag content -title = "dioxus | ⛺" - -[web.watcher] - -# when watcher trigger, regenerate the `index.html` -reload_html = true - -# which files or dirs will be watcher monitoring -watch_path = ["src", "public"] diff --git a/examples/tailwind/README.md b/examples/tailwind/README.md deleted file mode 100644 index 6dca4b775e..0000000000 --- a/examples/tailwind/README.md +++ /dev/null @@ -1,7 +0,0 @@ -Example: Basic Tailwind usage - -This example shows how an app might be styled with TailwindCSS. - -## Running - -Our [Tailwind](https://dioxuslabs.com/learn/0.5/cookbook/tailwind) guide explains how to setup and run Dioxus-Tailwind projects. \ No newline at end of file diff --git a/examples/tailwind/input.css b/examples/tailwind/input.css deleted file mode 100644 index bd6213e1df..0000000000 --- a/examples/tailwind/input.css +++ /dev/null @@ -1,3 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; \ No newline at end of file diff --git a/examples/tailwind/public/tailwind.css b/examples/tailwind/public/tailwind.css deleted file mode 100644 index 65b95316e1..0000000000 --- a/examples/tailwind/public/tailwind.css +++ /dev/null @@ -1,833 +0,0 @@ -/* -! tailwindcss v3.2.7 | MIT License | https://tailwindcss.com -*/ - -/* -1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) -2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) -*/ - -*, -::before, -::after { - box-sizing: border-box; - /* 1 */ - border-width: 0; - /* 2 */ - border-style: solid; - /* 2 */ - border-color: #e5e7eb; - /* 2 */ -} - -::before, -::after { - --tw-content: ''; -} - -/* -1. Use a consistent sensible line-height in all browsers. -2. Prevent adjustments of font size after orientation changes in iOS. -3. Use a more readable tab size. -4. Use the user's configured `sans` font-family by default. -5. Use the user's configured `sans` font-feature-settings by default. -*/ - -html { - line-height: 1.5; - /* 1 */ - -webkit-text-size-adjust: 100%; - /* 2 */ - -moz-tab-size: 4; - /* 3 */ - -o-tab-size: 4; - tab-size: 4; - /* 3 */ - font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; - /* 4 */ - font-feature-settings: normal; - /* 5 */ -} - -/* -1. Remove the margin in all browsers. -2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. -*/ - -body { - margin: 0; - /* 1 */ - line-height: inherit; - /* 2 */ -} - -/* -1. Add the correct height in Firefox. -2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) -3. Ensure horizontal rules are visible by default. -*/ - -hr { - height: 0; - /* 1 */ - color: inherit; - /* 2 */ - border-top-width: 1px; - /* 3 */ -} - -/* -Add the correct text decoration in Chrome, Edge, and Safari. -*/ - -abbr:where([title]) { - -webkit-text-decoration: underline dotted; - text-decoration: underline dotted; -} - -/* -Remove the default font size and weight for headings. -*/ - -h1, -h2, -h3, -h4, -h5, -h6 { - font-size: inherit; - font-weight: inherit; -} - -/* -Reset links to optimize for opt-in styling instead of opt-out. -*/ - -a { - color: inherit; - text-decoration: inherit; -} - -/* -Add the correct font weight in Edge and Safari. -*/ - -b, -strong { - font-weight: bolder; -} - -/* -1. Use the user's configured `mono` font family by default. -2. Correct the odd `em` font sizing in all browsers. -*/ - -code, -kbd, -samp, -pre { - font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; - /* 1 */ - font-size: 1em; - /* 2 */ -} - -/* -Add the correct font size in all browsers. -*/ - -small { - font-size: 80%; -} - -/* -Prevent `sub` and `sup` elements from affecting the line height in all browsers. -*/ - -sub, -sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; -} - -sub { - bottom: -0.25em; -} - -sup { - top: -0.5em; -} - -/* -1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) -2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) -3. Remove gaps between table borders by default. -*/ - -table { - text-indent: 0; - /* 1 */ - border-color: inherit; - /* 2 */ - border-collapse: collapse; - /* 3 */ -} - -/* -1. Change the font styles in all browsers. -2. Remove the margin in Firefox and Safari. -3. Remove default padding in all browsers. -*/ - -button, -input, -optgroup, -select, -textarea { - font-family: inherit; - /* 1 */ - font-size: 100%; - /* 1 */ - font-weight: inherit; - /* 1 */ - line-height: inherit; - /* 1 */ - color: inherit; - /* 1 */ - margin: 0; - /* 2 */ - padding: 0; - /* 3 */ -} - -/* -Remove the inheritance of text transform in Edge and Firefox. -*/ - -button, -select { - text-transform: none; -} - -/* -1. Correct the inability to style clickable types in iOS and Safari. -2. Remove default button styles. -*/ - -button, -[type='button'], -[type='reset'], -[type='submit'] { - -webkit-appearance: button; - /* 1 */ - background-color: transparent; - /* 2 */ - background-image: none; - /* 2 */ -} - -/* -Use the modern Firefox focus style for all focusable elements. -*/ - -:-moz-focusring { - outline: auto; -} - -/* -Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) -*/ - -:-moz-ui-invalid { - box-shadow: none; -} - -/* -Add the correct vertical alignment in Chrome and Firefox. -*/ - -progress { - vertical-align: baseline; -} - -/* -Correct the cursor style of increment and decrement buttons in Safari. -*/ - -::-webkit-inner-spin-button, -::-webkit-outer-spin-button { - height: auto; -} - -/* -1. Correct the odd appearance in Chrome and Safari. -2. Correct the outline style in Safari. -*/ - -[type='search'] { - -webkit-appearance: textfield; - /* 1 */ - outline-offset: -2px; - /* 2 */ -} - -/* -Remove the inner padding in Chrome and Safari on macOS. -*/ - -::-webkit-search-decoration { - -webkit-appearance: none; -} - -/* -1. Correct the inability to style clickable types in iOS and Safari. -2. Change font properties to `inherit` in Safari. -*/ - -::-webkit-file-upload-button { - -webkit-appearance: button; - /* 1 */ - font: inherit; - /* 2 */ -} - -/* -Add the correct display in Chrome and Safari. -*/ - -summary { - display: list-item; -} - -/* -Removes the default spacing and border for appropriate elements. -*/ - -blockquote, -dl, -dd, -h1, -h2, -h3, -h4, -h5, -h6, -hr, -figure, -p, -pre { - margin: 0; -} - -fieldset { - margin: 0; - padding: 0; -} - -legend { - padding: 0; -} - -ol, -ul, -menu { - list-style: none; - margin: 0; - padding: 0; -} - -/* -Prevent resizing textareas horizontally by default. -*/ - -textarea { - resize: vertical; -} - -/* -1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) -2. Set the default placeholder color to the user's configured gray 400 color. -*/ - -input::-moz-placeholder, textarea::-moz-placeholder { - opacity: 1; - /* 1 */ - color: #9ca3af; - /* 2 */ -} - -input::placeholder, -textarea::placeholder { - opacity: 1; - /* 1 */ - color: #9ca3af; - /* 2 */ -} - -/* -Set the default cursor for buttons. -*/ - -button, -[role="button"] { - cursor: pointer; -} - -/* -Make sure disabled buttons don't get the pointer cursor. -*/ - -:disabled { - cursor: default; -} - -/* -1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) -2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) - This can trigger a poorly considered lint error in some tools but is included by design. -*/ - -img, -svg, -video, -canvas, -audio, -iframe, -embed, -object { - display: block; - /* 1 */ - vertical-align: middle; - /* 2 */ -} - -/* -Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) -*/ - -img, -video { - max-width: 100%; - height: auto; -} - -/* Make elements with the HTML hidden attribute stay hidden by default */ - -[hidden] { - display: none; -} - -*, ::before, ::after { - --tw-border-spacing-x: 0; - --tw-border-spacing-y: 0; - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-rotate: 0; - --tw-skew-x: 0; - --tw-skew-y: 0; - --tw-scale-x: 1; - --tw-scale-y: 1; - --tw-pan-x: ; - --tw-pan-y: ; - --tw-pinch-zoom: ; - --tw-scroll-snap-strictness: proximity; - --tw-ordinal: ; - --tw-slashed-zero: ; - --tw-numeric-figure: ; - --tw-numeric-spacing: ; - --tw-numeric-fraction: ; - --tw-ring-inset: ; - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-color: rgb(59 130 246 / 0.5); - --tw-ring-offset-shadow: 0 0 #0000; - --tw-ring-shadow: 0 0 #0000; - --tw-shadow: 0 0 #0000; - --tw-shadow-colored: 0 0 #0000; - --tw-blur: ; - --tw-brightness: ; - --tw-contrast: ; - --tw-grayscale: ; - --tw-hue-rotate: ; - --tw-invert: ; - --tw-saturate: ; - --tw-sepia: ; - --tw-drop-shadow: ; - --tw-backdrop-blur: ; - --tw-backdrop-brightness: ; - --tw-backdrop-contrast: ; - --tw-backdrop-grayscale: ; - --tw-backdrop-hue-rotate: ; - --tw-backdrop-invert: ; - --tw-backdrop-opacity: ; - --tw-backdrop-saturate: ; - --tw-backdrop-sepia: ; -} - -::backdrop { - --tw-border-spacing-x: 0; - --tw-border-spacing-y: 0; - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-rotate: 0; - --tw-skew-x: 0; - --tw-skew-y: 0; - --tw-scale-x: 1; - --tw-scale-y: 1; - --tw-pan-x: ; - --tw-pan-y: ; - --tw-pinch-zoom: ; - --tw-scroll-snap-strictness: proximity; - --tw-ordinal: ; - --tw-slashed-zero: ; - --tw-numeric-figure: ; - --tw-numeric-spacing: ; - --tw-numeric-fraction: ; - --tw-ring-inset: ; - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-color: rgb(59 130 246 / 0.5); - --tw-ring-offset-shadow: 0 0 #0000; - --tw-ring-shadow: 0 0 #0000; - --tw-shadow: 0 0 #0000; - --tw-shadow-colored: 0 0 #0000; - --tw-blur: ; - --tw-brightness: ; - --tw-contrast: ; - --tw-grayscale: ; - --tw-hue-rotate: ; - --tw-invert: ; - --tw-saturate: ; - --tw-sepia: ; - --tw-drop-shadow: ; - --tw-backdrop-blur: ; - --tw-backdrop-brightness: ; - --tw-backdrop-contrast: ; - --tw-backdrop-grayscale: ; - --tw-backdrop-hue-rotate: ; - --tw-backdrop-invert: ; - --tw-backdrop-opacity: ; - --tw-backdrop-saturate: ; - --tw-backdrop-sepia: ; -} - -.container { - width: 100%; -} - -@media (min-width: 640px) { - .container { - max-width: 640px; - } -} - -@media (min-width: 768px) { - .container { - max-width: 768px; - } -} - -@media (min-width: 1024px) { - .container { - max-width: 1024px; - } -} - -@media (min-width: 1280px) { - .container { - max-width: 1280px; - } -} - -@media (min-width: 1536px) { - .container { - max-width: 1536px; - } -} - -.mx-auto { - margin-left: auto; - margin-right: auto; -} - -.mb-16 { - margin-bottom: 4rem; -} - -.mb-4 { - margin-bottom: 1rem; -} - -.mb-8 { - margin-bottom: 2rem; -} - -.ml-1 { - margin-left: 0.25rem; -} - -.ml-3 { - margin-left: 0.75rem; -} - -.ml-4 { - margin-left: 1rem; -} - -.mr-5 { - margin-right: 1.25rem; -} - -.mt-4 { - margin-top: 1rem; -} - -.flex { - display: flex; -} - -.inline-flex { - display: inline-flex; -} - -.hidden { - display: none; -} - -.h-10 { - height: 2.5rem; -} - -.h-4 { - height: 1rem; -} - -.w-10 { - width: 2.5rem; -} - -.w-4 { - width: 1rem; -} - -.w-5\/6 { - width: 83.333333%; -} - -.flex-col { - flex-direction: column; -} - -.flex-wrap { - flex-wrap: wrap; -} - -.items-center { - align-items: center; -} - -.justify-center { - justify-content: center; -} - -.rounded { - border-radius: 0.25rem; -} - -.rounded-full { - border-radius: 9999px; -} - -.border-0 { - border-width: 0px; -} - -.bg-gray-800 { - --tw-bg-opacity: 1; - background-color: rgb(31 41 55 / var(--tw-bg-opacity)); -} - -.bg-gray-900 { - --tw-bg-opacity: 1; - background-color: rgb(17 24 39 / var(--tw-bg-opacity)); -} - -.bg-indigo-500 { - --tw-bg-opacity: 1; - background-color: rgb(99 102 241 / var(--tw-bg-opacity)); -} - -.object-cover { - -o-object-fit: cover; - object-fit: cover; -} - -.object-center { - -o-object-position: center; - object-position: center; -} - -.p-2 { - padding: 0.5rem; -} - -.p-5 { - padding: 1.25rem; -} - -.px-3 { - padding-left: 0.75rem; - padding-right: 0.75rem; -} - -.px-5 { - padding-left: 1.25rem; - padding-right: 1.25rem; -} - -.px-6 { - padding-left: 1.5rem; - padding-right: 1.5rem; -} - -.py-1 { - padding-top: 0.25rem; - padding-bottom: 0.25rem; -} - -.py-2 { - padding-top: 0.5rem; - padding-bottom: 0.5rem; -} - -.py-24 { - padding-top: 6rem; - padding-bottom: 6rem; -} - -.text-center { - text-align: center; -} - -.text-3xl { - font-size: 1.875rem; - line-height: 2.25rem; -} - -.text-base { - font-size: 1rem; - line-height: 1.5rem; -} - -.text-lg { - font-size: 1.125rem; - line-height: 1.75rem; -} - -.text-xl { - font-size: 1.25rem; - line-height: 1.75rem; -} - -.font-medium { - font-weight: 500; -} - -.leading-relaxed { - line-height: 1.625; -} - -.text-gray-400 { - --tw-text-opacity: 1; - color: rgb(156 163 175 / var(--tw-text-opacity)); -} - -.text-white { - --tw-text-opacity: 1; - color: rgb(255 255 255 / var(--tw-text-opacity)); -} - -.hover\:bg-gray-700:hover { - --tw-bg-opacity: 1; - background-color: rgb(55 65 81 / var(--tw-bg-opacity)); -} - -.hover\:bg-indigo-600:hover { - --tw-bg-opacity: 1; - background-color: rgb(79 70 229 / var(--tw-bg-opacity)); -} - -.hover\:text-white:hover { - --tw-text-opacity: 1; - color: rgb(255 255 255 / var(--tw-text-opacity)); -} - -.focus\:outline-none:focus { - outline: 2px solid transparent; - outline-offset: 2px; -} - -@media (min-width: 640px) { - .sm\:text-4xl { - font-size: 2.25rem; - line-height: 2.5rem; - } -} - -@media (min-width: 768px) { - .md\:mb-0 { - margin-bottom: 0px; - } - - .md\:ml-auto { - margin-left: auto; - } - - .md\:mt-0 { - margin-top: 0px; - } - - .md\:w-1\/2 { - width: 50%; - } - - .md\:flex-row { - flex-direction: row; - } - - .md\:items-start { - align-items: flex-start; - } - - .md\:pr-16 { - padding-right: 4rem; - } - - .md\:text-left { - text-align: left; - } -} - -@media (min-width: 1024px) { - .lg\:inline-block { - display: inline-block; - } - - .lg\:w-full { - width: 100%; - } - - .lg\:max-w-lg { - max-width: 32rem; - } - - .lg\:flex-grow { - flex-grow: 1; - } - - .lg\:pr-24 { - padding-right: 6rem; - } -} \ No newline at end of file diff --git a/examples/tailwind/src/main.rs b/examples/tailwind/src/main.rs deleted file mode 100644 index 70ce484c38..0000000000 --- a/examples/tailwind/src/main.rs +++ /dev/null @@ -1,103 +0,0 @@ -#![allow(non_snake_case)] - -use dioxus::prelude::*; - -const _STYLE: &str = asset!("public/tailwind.css"); - -fn main() { - launch(app); -} - -pub fn app() -> Element { - let grey_background = true; - rsx!( - div { - header { - class: "text-gray-400 body-font", - // you can use optional attributes to optionally apply a tailwind class - class: if grey_background { - "bg-gray-900" - }, - div { class: "container mx-auto flex flex-wrap p-5 flex-col md:flex-row items-center", - a { class: "flex title-font font-medium items-center text-white mb-4 md:mb-0", - StacksIcon {} - span { class: "ml-3 text-xl", "Hello Dioxus!" } - } - nav { class: "md:ml-auto flex flex-wrap items-center text-base justify-center", - a { class: "mr-5 hover:text-white", "First Link" } - a { class: "mr-5 hover:text-white", "Second Link" } - a { class: "mr-5 hover:text-white", "Third Link" } - a { class: "mr-5 hover:text-white", "Fourth Link" } - } - button { class: "inline-flex items-center bg-gray-800 border-0 py-1 px-3 focus:outline-none hover:bg-gray-700 rounded text-base mt-4 md:mt-0", - "Button" - RightArrowIcon {} - } - } - } - - section { class: "text-gray-400 bg-gray-900 body-font", - div { class: "container mx-auto flex px-5 py-24 md:flex-row flex-col items-center", - div { class: "lg:flex-grow md:w-1/2 lg:pr-24 md:pr-16 flex flex-col md:items-start md:text-left mb-16 md:mb-0 items-center text-center", - h1 { class: "title-font sm:text-4xl text-3xl mb-4 font-medium text-white", - br { class: "hidden lg:inline-block" } - "Dioxus Sneak Peek" - } - p { class: "mb-8 leading-relaxed", - - "Dioxus is a new UI framework that makes it easy and simple to write cross-platform apps using web - technologies! It is functional, fast, and portable. Dioxus can run on the web, on the desktop, and - on mobile and embedded platforms." - } - div { class: "flex justify-center", - button { class: "inline-flex text-white bg-indigo-500 border-0 py-2 px-6 focus:outline-none hover:bg-indigo-600 rounded text-lg", - "Learn more" - } - button { class: "ml-4 inline-flex text-gray-400 bg-gray-800 border-0 py-2 px-6 focus:outline-none hover:bg-gray-700 hover:text-white rounded text-lg", - "Build an app" - } - } - } - div { class: "lg:max-w-lg lg:w-full md:w-1/2 w-5/6", - img { - class: "object-cover object-center rounded", - src: "https://i.imgur.com/oK6BLtw.png", - referrerpolicy: "no-referrer", - alt: "hero" - } - } - } - } - } - ) -} - -pub fn StacksIcon() -> Element { - rsx!( - svg { - fill: "none", - stroke: "currentColor", - stroke_linecap: "round", - stroke_linejoin: "round", - stroke_width: "2", - class: "w-10 h-10 text-white p-2 bg-indigo-500 rounded-full", - view_box: "0 0 24 24", - path { d: "M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5" } - } - ) -} - -pub fn RightArrowIcon() -> Element { - rsx!( - svg { - fill: "none", - stroke: "currentColor", - stroke_linecap: "round", - stroke_linejoin: "round", - stroke_width: "2", - class: "w-4 h-4 ml-1", - view_box: "0 0 24 24", - path { d: "M5 12h14M12 5l7 7-7 7" } - } - ) -} diff --git a/examples/tailwind/tailwind.config.js b/examples/tailwind/tailwind.config.js deleted file mode 100644 index 2a69d5803a..0000000000 --- a/examples/tailwind/tailwind.config.js +++ /dev/null @@ -1,9 +0,0 @@ -/** @type {import('tailwindcss').Config} */ -module.exports = { - mode: "all", - content: ["./src/**/*.{rs,html,css}", "./dist/**/*.html"], - theme: { - extend: {}, - }, - plugins: [], -}; diff --git a/examples/title.rs b/examples/title.rs index ebe258963c..f836a7f3d6 100644 --- a/examples/title.rs +++ b/examples/title.rs @@ -3,8 +3,7 @@ use dioxus::prelude::*; fn main() { - tracing_subscriber::fmt::init(); - launch(app); + dioxus::launch(app); } fn app() -> Element { @@ -14,7 +13,7 @@ fn app() -> Element { div { // You can set the title of the page with the Title component // In web applications, this sets the title in the head. On desktop, it sets the window title - Title { "My Application (Count {count})" } + document::Title { "My Application (Count {count})" } button { onclick: move |_| count += 1, "Up high!" } button { onclick: move |_| count -= 1, "Down low!" } } diff --git a/examples/todomvc.rs b/examples/todomvc.rs index ea2e528a12..a95fda0e2d 100644 --- a/examples/todomvc.rs +++ b/examples/todomvc.rs @@ -3,10 +3,10 @@ use dioxus::prelude::*; use std::collections::HashMap; -const STYLE: &str = asset!("./examples/assets/todomvc.css"); +const STYLE: Asset = asset!("/examples/assets/todomvc.css"); fn main() { - launch(app); + dioxus::launch(app); } #[derive(PartialEq, Eq, Clone, Copy)] @@ -63,7 +63,7 @@ fn app() -> Element { }; rsx! { - head::Link { rel: "stylesheet", href: STYLE } + document::Stylesheet { href: STYLE } section { class: "todoapp", TodoHeader { todos } section { class: "main", diff --git a/examples/weather_app.rs b/examples/weather_app.rs index 395fd81f19..7b56bbbe54 100644 --- a/examples/weather_app.rs +++ b/examples/weather_app.rs @@ -4,7 +4,7 @@ use dioxus::prelude::*; use serde::{Deserialize, Serialize}; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { @@ -19,7 +19,7 @@ fn app() -> Element { let current_weather = use_resource(move || async move { get_weather(&country()).await }); rsx! { - head::Link { rel: "stylesheet", href: "https://unpkg.com/tailwindcss@^2.0/dist/tailwind.min.css" } + document::Stylesheet { href: "https://unpkg.com/tailwindcss@^2.0/dist/tailwind.min.css" } div { class: "mx-auto p-4 bg-gray-100 h-screen flex justify-center", div { class: "flex items-center justify-center flex-row", div { class: "flex items-start justify-center flex-row", diff --git a/examples/web_component.rs b/examples/web_component.rs index df0515397b..a5f831912b 100644 --- a/examples/web_component.rs +++ b/examples/web_component.rs @@ -7,7 +7,7 @@ use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/window_event.rs b/examples/window_event.rs index cb91d2a5b2..ca126f6a1d 100644 --- a/examples/window_event.rs +++ b/examples/window_event.rs @@ -13,7 +13,7 @@ use dioxus::desktop::{window, Config, WindowBuilder}; use dioxus::prelude::*; fn main() { - LaunchBuilder::desktop() + dioxus::launch::builder() .with_cfg( Config::new().with_window( WindowBuilder::new() @@ -26,7 +26,7 @@ fn main() { fn app() -> Element { rsx!( - head::Link { href: "https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css", rel: "stylesheet" } + document::Link { href: "https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css", rel: "stylesheet" } Header {} div { class: "container mx-auto", div { class: "grid grid-cols-5", diff --git a/examples/window_focus.rs b/examples/window_focus.rs index 447544077b..1213b3e994 100644 --- a/examples/window_focus.rs +++ b/examples/window_focus.rs @@ -12,7 +12,7 @@ use dioxus::desktop::{Config, WindowCloseBehaviour}; use dioxus::prelude::*; fn main() { - LaunchBuilder::desktop() + dioxus::launch::builder() .with_cfg(Config::new().with_close_behaviour(WindowCloseBehaviour::CloseWindow)) .launch(app) } diff --git a/examples/window_zoom.rs b/examples/window_zoom.rs index 8b10d503b0..35d25f4fde 100644 --- a/examples/window_zoom.rs +++ b/examples/window_zoom.rs @@ -5,7 +5,7 @@ use dioxus::prelude::*; fn main() { - launch_desktop(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/xss_safety.rs b/examples/xss_safety.rs index cb8d414dd7..904a42b07b 100644 --- a/examples/xss_safety.rs +++ b/examples/xss_safety.rs @@ -5,7 +5,7 @@ use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { From a130b8130b9046701cf90d876a7212fbfebe9381 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Sun, 15 Sep 2024 18:11:05 -0700 Subject: [PATCH 109/139] revert changes to examples --- examples/PWA-example/Cargo.toml | 17 + examples/PWA-example/Dioxus.toml | 27 + examples/PWA-example/LICENSE | 21 + examples/PWA-example/README.md | 45 + examples/PWA-example/index.html | 30 + examples/PWA-example/public/favicon.ico | Bin 0 -> 23104 bytes examples/PWA-example/public/logo_192.png | Bin 0 -> 11198 bytes examples/PWA-example/public/logo_512.png | Bin 0 -> 48151 bytes examples/PWA-example/public/manifest.json | 34 + examples/PWA-example/public/sw.js | 198 + examples/PWA-example/src/main.rs | 21 + examples/all_events.rs | 6 +- examples/assets/purecss.css | 12 - examples/backgrounded_futures.rs | 2 +- examples/calculator.rs | 6 +- examples/calculator_mutable.rs | 4 +- examples/clock.rs | 10 +- examples/control_focus.rs | 9 +- examples/counters.rs | 6 +- examples/crm.rs | 11 +- examples/custom_assets.rs | 32 +- examples/custom_html.rs | 2 +- examples/custom_menu.rs | 2 +- examples/disabled.rs | 2 +- examples/dog_app.rs | 2 +- examples/dynamic_asset.rs | 6 +- examples/errors.rs | 36 +- examples/eval.rs | 29 +- examples/file_explorer.rs | 8 +- examples/file_upload.rs | 6 +- examples/flat_router.rs | 6 +- examples/form.rs | 2 +- examples/future.rs | 2 +- examples/generic_component.rs | 2 +- examples/global.rs | 6 +- examples/hash_fragment_state.rs | 4 +- examples/hello_world.rs | 2 +- examples/hydration.rs | 2 +- examples/image_generator_openai.rs | 4 +- examples/link.rs | 6 +- examples/login_form.rs | 2 +- examples/memo_chain.rs | 2 +- examples/meta.rs | 13 +- examples/mobile_demo/.gitignore | 10 + examples/mobile_demo/Cargo.lock | 4007 +++++++++++ examples/mobile_demo/Cargo.toml | 51 + examples/mobile_demo/README.md | 11 + examples/mobile_demo/mobile.toml | 8 + examples/mobile_demo/src/index.html | 12 + examples/mobile_demo/src/lib.rs | 90 + examples/multiwindow.rs | 2 +- examples/nested_listeners.rs | 2 +- .../openid_connect_demo/.cargo/config.toml | 5 + examples/openid_connect_demo/.gitignore | 3 + examples/openid_connect_demo/Cargo.lock | 6372 +++++++++++++++++ examples/openid_connect_demo/Cargo.toml | 36 + examples/openid_connect_demo/Dioxus.toml | 35 + examples/openid_connect_demo/README.md | 12 + examples/openid_connect_demo/src/constants.rs | 2 + examples/openid_connect_demo/src/main.rs | 36 + examples/openid_connect_demo/src/model/mod.rs | 1 + .../openid_connect_demo/src/model/user.rs | 7 + examples/openid_connect_demo/src/oidc.rs | 127 + .../openid_connect_demo/src/props/client.rs | 20 + examples/openid_connect_demo/src/props/mod.rs | 1 + examples/openid_connect_demo/src/router.rs | 16 + examples/openid_connect_demo/src/storage.rs | 35 + .../openid_connect_demo/src/views/header.rs | 221 + .../openid_connect_demo/src/views/home.rs | 5 + .../openid_connect_demo/src/views/login.rs | 86 + examples/openid_connect_demo/src/views/mod.rs | 4 + .../src/views/not_found.rs | 10 + examples/optional_props.rs | 2 +- examples/overlay.rs | 8 +- examples/popup.rs | 2 +- examples/query_segment_search.rs | 6 +- examples/read_size.rs | 4 +- examples/readme.rs | 2 +- examples/reducer.rs | 6 +- examples/resize.rs | 4 +- examples/router.rs | 6 +- examples/router_resource.rs | 2 +- examples/rsx_usage.rs | 5 +- examples/scroll_to_top.rs | 2 +- examples/shortcut.rs | 2 +- examples/shorthand.rs | 2 +- examples/signals.rs | 2 +- examples/simple_list.rs | 2 +- examples/simple_router.rs | 2 +- examples/streams.rs | 2 +- examples/suspense.rs | 2 +- examples/svg.rs | 2 +- examples/tailwind/.gitignore | 1 + examples/tailwind/Cargo.toml | 20 + examples/tailwind/Dioxus.toml | 27 + examples/tailwind/README.md | 7 + examples/tailwind/input.css | 3 + examples/tailwind/public/tailwind.css | 833 +++ examples/tailwind/src/main.rs | 103 + examples/tailwind/tailwind.config.js | 9 + examples/title.rs | 5 +- examples/todomvc.rs | 6 +- examples/weather_app.rs | 4 +- examples/web_component.rs | 2 +- examples/window_event.rs | 4 +- examples/window_focus.rs | 2 +- examples/window_zoom.rs | 2 +- examples/xss_safety.rs | 2 +- 108 files changed, 12795 insertions(+), 164 deletions(-) create mode 100644 examples/PWA-example/Cargo.toml create mode 100644 examples/PWA-example/Dioxus.toml create mode 100644 examples/PWA-example/LICENSE create mode 100644 examples/PWA-example/README.md create mode 100644 examples/PWA-example/index.html create mode 100644 examples/PWA-example/public/favicon.ico create mode 100644 examples/PWA-example/public/logo_192.png create mode 100644 examples/PWA-example/public/logo_512.png create mode 100644 examples/PWA-example/public/manifest.json create mode 100644 examples/PWA-example/public/sw.js create mode 100644 examples/PWA-example/src/main.rs delete mode 100644 examples/assets/purecss.css create mode 100644 examples/mobile_demo/.gitignore create mode 100644 examples/mobile_demo/Cargo.lock create mode 100644 examples/mobile_demo/Cargo.toml create mode 100644 examples/mobile_demo/README.md create mode 100644 examples/mobile_demo/mobile.toml create mode 100644 examples/mobile_demo/src/index.html create mode 100644 examples/mobile_demo/src/lib.rs create mode 100644 examples/openid_connect_demo/.cargo/config.toml create mode 100644 examples/openid_connect_demo/.gitignore create mode 100644 examples/openid_connect_demo/Cargo.lock create mode 100644 examples/openid_connect_demo/Cargo.toml create mode 100644 examples/openid_connect_demo/Dioxus.toml create mode 100644 examples/openid_connect_demo/README.md create mode 100644 examples/openid_connect_demo/src/constants.rs create mode 100644 examples/openid_connect_demo/src/main.rs create mode 100644 examples/openid_connect_demo/src/model/mod.rs create mode 100644 examples/openid_connect_demo/src/model/user.rs create mode 100644 examples/openid_connect_demo/src/oidc.rs create mode 100644 examples/openid_connect_demo/src/props/client.rs create mode 100644 examples/openid_connect_demo/src/props/mod.rs create mode 100644 examples/openid_connect_demo/src/router.rs create mode 100644 examples/openid_connect_demo/src/storage.rs create mode 100644 examples/openid_connect_demo/src/views/header.rs create mode 100644 examples/openid_connect_demo/src/views/home.rs create mode 100644 examples/openid_connect_demo/src/views/login.rs create mode 100644 examples/openid_connect_demo/src/views/mod.rs create mode 100644 examples/openid_connect_demo/src/views/not_found.rs create mode 100644 examples/tailwind/.gitignore create mode 100644 examples/tailwind/Cargo.toml create mode 100644 examples/tailwind/Dioxus.toml create mode 100644 examples/tailwind/README.md create mode 100644 examples/tailwind/input.css create mode 100644 examples/tailwind/public/tailwind.css create mode 100644 examples/tailwind/src/main.rs create mode 100644 examples/tailwind/tailwind.config.js diff --git a/examples/PWA-example/Cargo.toml b/examples/PWA-example/Cargo.toml new file mode 100644 index 0000000000..bee204f20a --- /dev/null +++ b/examples/PWA-example/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "dioxus-pwa-example" +version = "0.1.0" +authors = ["Antonio Curavalea "] +edition = "2021" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +dioxus = { workspace = true, features = ["web"] } + +log = "0.4.6" + +# WebAssembly Debug +wasm-logger = "0.2.0" +console_error_panic_hook = "0.1.7" diff --git a/examples/PWA-example/Dioxus.toml b/examples/PWA-example/Dioxus.toml new file mode 100644 index 0000000000..a831a28ff7 --- /dev/null +++ b/examples/PWA-example/Dioxus.toml @@ -0,0 +1,27 @@ +[application] + +# App (Project) Name +name = "dioxus-pwa-example" + +# Dioxus App Default Platform +# desktop, web, mobile, ssr +default_platform = "web" + +# `build` & `serve` dist path +out_dir = "dist" + +# resource (public) file folder +asset_dir = "public" + +[web.app] + +# HTML title tag content +title = "dioxus | ⛺" + +[web.watcher] + +# when watcher trigger, regenerate the `index.html` +reload_html = true + +# which files or dirs will be watcher monitoring +watch_path = ["src", "public"] diff --git a/examples/PWA-example/LICENSE b/examples/PWA-example/LICENSE new file mode 100644 index 0000000000..bcdd828e9c --- /dev/null +++ b/examples/PWA-example/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Dioxus + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/examples/PWA-example/README.md b/examples/PWA-example/README.md new file mode 100644 index 0000000000..d501df2126 --- /dev/null +++ b/examples/PWA-example/README.md @@ -0,0 +1,45 @@ +# Dioxus PWA example + +This is a basic example of a progressive web app (PWA) using Dioxus and Dioxus CLI. +Currently PWA functionality requires the use of a service worker and manifest file, so this isn't 100% Rust yet. + +It is also very much usable as a template for your projects, if you're aiming to create a PWA. + +## Try the example + +Make sure you have Dioxus CLI installed (if you're unsure, run `cargo install dioxus-cli --locked`). + +You can run `dx serve` in this directory to start the web server locally, or run +`dx build --release` to build the project so you can deploy it on a separate web-server. + +## Project Structure + +``` +├── Cargo.toml +├── Dioxus.toml +├── index.html // Custom HTML is needed for this, to load the SW and manifest. +├── LICENSE +├── public +│ ├── favicon.ico +│ ├── logo_192.png +│ ├── logo_512.png +│ ├── manifest.json // The manifest file - edit this as you need to. +│ └── sw.js // The service worker - you must edit this for actual projects. +├── README.md +└── src + └── main.rs +``` + +## Resources + +If you're just getting started with PWAs, here are some useful resources: + +- [PWABuilder docs](https://docs.pwabuilder.com/#/) +- [MDN article on PWAs](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps) + +For service worker scripting (in JavaScript): + +- [Service worker guide from PWABuilder](https://docs.pwabuilder.com/#/home/sw-intro) +- [Service worker examples, also from PWABuilder](https://github.com/pwa-builder/pwabuilder-serviceworkers) + +If you want to stay as close to 100% Rust as possible, you can try using [wasi-worker](https://github.com/dunnock/wasi-worker) to replace the JS service worker file. The JSON manifest will still be required though. diff --git a/examples/PWA-example/index.html b/examples/PWA-example/index.html new file mode 100644 index 0000000000..44bfa59cc4 --- /dev/null +++ b/examples/PWA-example/index.html @@ -0,0 +1,30 @@ + + + + {app_title} + + + + + + {style_include} + + +
      + + {script_include} + + \ No newline at end of file diff --git a/examples/PWA-example/public/favicon.ico b/examples/PWA-example/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..b11015bdbacdb3b99e03555edb85d78467b37c7d GIT binary patch literal 23104 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 W+`s-{ME)24jHW23DqAIO8vH-~t)grI literal 0 HcmV?d00001 diff --git a/examples/PWA-example/public/logo_192.png b/examples/PWA-example/public/logo_192.png new file mode 100644 index 0000000000000000000000000000000000000000..112eb5ee49ca4e69d742a99269d1ae08a970dd7a GIT binary patch literal 11198 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 literal 0 HcmV?d00001 diff --git a/examples/PWA-example/public/logo_512.png b/examples/PWA-example/public/logo_512.png new file mode 100644 index 0000000000000000000000000000000000000000..43129772a892a5ca82dfdc8bb4d9027cc830ddf5 GIT binary patch literal 48151 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 Unavailable', { + status: 503, + statusText: 'Service Unavailable', + headers: new Headers({ + 'Content-Type': 'text/html' + }) + }); + } + }) + ); +}); + +/* The activate event fires after a service worker has been successfully installed. + It is most useful when phasing out an older version of a service worker, as at + this point you know that the new worker was installed correctly. In this example, + we delete old caches that don't match the version in the worker we just finished + installing. +*/ +self.addEventListener("activate", function (event) { + /* Just like with the install event, event.waitUntil blocks activate on a promise. + Activation will fail unless the promise is fulfilled. + */ + //console.log('WORKER: activate event in progress.'); + + event.waitUntil( + caches + /* This method returns a promise which will resolve to an array of available + cache keys. + */ + .keys() + .then(function (keys) { + // We return a promise that settles when all outdated caches are deleted. + return Promise.all( + keys + .filter(function (key) { + // Filter by keys that don't start with the latest version prefix. + return !key.startsWith(version); + }) + .map(function (key) { + /* Return a promise that's fulfilled + when each outdated cache is deleted. + */ + return caches.delete(key); + }) + ); + }) + .then(function () { + //console.log('WORKER: activate completed.'); + }) + ); +}); diff --git a/examples/PWA-example/src/main.rs b/examples/PWA-example/src/main.rs new file mode 100644 index 0000000000..3e4d5b2361 --- /dev/null +++ b/examples/PWA-example/src/main.rs @@ -0,0 +1,21 @@ +use dioxus::prelude::*; + +fn main() { + // init debug tool for WebAssembly + wasm_logger::init(wasm_logger::Config::default()); + console_error_panic_hook::set_once(); + + launch(app); +} + +fn app() -> Element { + rsx! ( + div { style: "text-align: center;", + h1 { "🌗 Dioxus 🚀" } + h3 { "Frontend that scales." } + p { + "Dioxus is a portable, performant, and ergonomic framework for building cross-platform user interfaces in Rust." + } + } + ) +} diff --git a/examples/all_events.rs b/examples/all_events.rs index 4e4ca3d6d5..2a04ee2bb3 100644 --- a/examples/all_events.rs +++ b/examples/all_events.rs @@ -6,8 +6,10 @@ use dioxus::prelude::*; use std::{collections::VecDeque, fmt::Debug, rc::Rc}; +const STYLE: &str = asset!("./examples/assets/events.css"); + fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { @@ -24,7 +26,7 @@ fn app() -> Element { }; rsx! { - document::Stylesheet { href: asset!("/examples/assets/events.css") } + head::Link { rel: "stylesheet", href: STYLE } div { id: "container", // focusing is necessary to catch keyboard events div { id: "receiver", tabindex: 0, diff --git a/examples/assets/purecss.css b/examples/assets/purecss.css deleted file mode 100644 index dd301cc364..0000000000 --- a/examples/assets/purecss.css +++ /dev/null @@ -1,12 +0,0 @@ -/*! -Pure v2.0.6 -Copyright 2013 Yahoo! -Licensed under the BSD License. -https://github.com/pure-css/pure/blob/master/LICENSE -*/ -/*! -normalize.css v | MIT License | git.io/normalize -Copyright (c) Nicolas Gallagher and Jonathan Neal -*/ -/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ -html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{-webkit-box-sizing:content-box;box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{-webkit-box-sizing:border-box;box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{-webkit-box-sizing:border-box;box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}html{font-family:sans-serif}.hidden,[hidden]{display:none!important}.pure-img{max-width:100%;height:auto;display:block}.pure-g{letter-spacing:-.31em;text-rendering:optimizespeed;font-family:FreeSans,Arimo,"Droid Sans",Helvetica,Arial,sans-serif;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-line-pack:start;align-content:flex-start}@media all and (-ms-high-contrast:none),(-ms-high-contrast:active){table .pure-g{display:block}}.opera-only :-o-prefocus,.pure-g{word-spacing:-0.43em}.pure-u{display:inline-block;letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-g [class*=pure-u]{font-family:sans-serif}.pure-u-1,.pure-u-1-1,.pure-u-1-12,.pure-u-1-2,.pure-u-1-24,.pure-u-1-3,.pure-u-1-4,.pure-u-1-5,.pure-u-1-6,.pure-u-1-8,.pure-u-10-24,.pure-u-11-12,.pure-u-11-24,.pure-u-12-24,.pure-u-13-24,.pure-u-14-24,.pure-u-15-24,.pure-u-16-24,.pure-u-17-24,.pure-u-18-24,.pure-u-19-24,.pure-u-2-24,.pure-u-2-3,.pure-u-2-5,.pure-u-20-24,.pure-u-21-24,.pure-u-22-24,.pure-u-23-24,.pure-u-24-24,.pure-u-3-24,.pure-u-3-4,.pure-u-3-5,.pure-u-3-8,.pure-u-4-24,.pure-u-4-5,.pure-u-5-12,.pure-u-5-24,.pure-u-5-5,.pure-u-5-6,.pure-u-5-8,.pure-u-6-24,.pure-u-7-12,.pure-u-7-24,.pure-u-7-8,.pure-u-8-24,.pure-u-9-24{display:inline-block;letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-u-1-24{width:4.1667%}.pure-u-1-12,.pure-u-2-24{width:8.3333%}.pure-u-1-8,.pure-u-3-24{width:12.5%}.pure-u-1-6,.pure-u-4-24{width:16.6667%}.pure-u-1-5{width:20%}.pure-u-5-24{width:20.8333%}.pure-u-1-4,.pure-u-6-24{width:25%}.pure-u-7-24{width:29.1667%}.pure-u-1-3,.pure-u-8-24{width:33.3333%}.pure-u-3-8,.pure-u-9-24{width:37.5%}.pure-u-2-5{width:40%}.pure-u-10-24,.pure-u-5-12{width:41.6667%}.pure-u-11-24{width:45.8333%}.pure-u-1-2,.pure-u-12-24{width:50%}.pure-u-13-24{width:54.1667%}.pure-u-14-24,.pure-u-7-12{width:58.3333%}.pure-u-3-5{width:60%}.pure-u-15-24,.pure-u-5-8{width:62.5%}.pure-u-16-24,.pure-u-2-3{width:66.6667%}.pure-u-17-24{width:70.8333%}.pure-u-18-24,.pure-u-3-4{width:75%}.pure-u-19-24{width:79.1667%}.pure-u-4-5{width:80%}.pure-u-20-24,.pure-u-5-6{width:83.3333%}.pure-u-21-24,.pure-u-7-8{width:87.5%}.pure-u-11-12,.pure-u-22-24{width:91.6667%}.pure-u-23-24{width:95.8333%}.pure-u-1,.pure-u-1-1,.pure-u-24-24,.pure-u-5-5{width:100%}.pure-button{display:inline-block;line-height:normal;white-space:nowrap;vertical-align:middle;text-align:center;cursor:pointer;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-box-sizing:border-box;box-sizing:border-box}.pure-button::-moz-focus-inner{padding:0;border:0}.pure-button-group{letter-spacing:-.31em;text-rendering:optimizespeed}.opera-only :-o-prefocus,.pure-button-group{word-spacing:-0.43em}.pure-button-group .pure-button{letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-button{font-family:inherit;font-size:100%;padding:.5em 1em;color:rgba(0,0,0,.8);border:none transparent;background-color:#e6e6e6;text-decoration:none;border-radius:2px}.pure-button-hover,.pure-button:focus,.pure-button:hover{background-image:-webkit-gradient(linear,left top,left bottom,from(transparent),color-stop(40%,rgba(0,0,0,.05)),to(rgba(0,0,0,.1)));background-image:linear-gradient(transparent,rgba(0,0,0,.05) 40%,rgba(0,0,0,.1))}.pure-button:focus{outline:0}.pure-button-active,.pure-button:active{-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.15) inset,0 0 6px rgba(0,0,0,.2) inset;box-shadow:0 0 0 1px rgba(0,0,0,.15) inset,0 0 6px rgba(0,0,0,.2) inset;border-color:#000}.pure-button-disabled,.pure-button-disabled:active,.pure-button-disabled:focus,.pure-button-disabled:hover,.pure-button[disabled]{border:none;background-image:none;opacity:.4;cursor:not-allowed;-webkit-box-shadow:none;box-shadow:none;pointer-events:none}.pure-button-hidden{display:none}.pure-button-primary,.pure-button-selected,a.pure-button-primary,a.pure-button-selected{background-color:#0078e7;color:#fff}.pure-button-group .pure-button{margin:0;border-radius:0;border-right:1px solid rgba(0,0,0,.2)}.pure-button-group .pure-button:first-child{border-top-left-radius:2px;border-bottom-left-radius:2px}.pure-button-group .pure-button:last-child{border-top-right-radius:2px;border-bottom-right-radius:2px;border-right:none}.pure-form input[type=color],.pure-form input[type=date],.pure-form input[type=datetime-local],.pure-form input[type=datetime],.pure-form input[type=email],.pure-form input[type=month],.pure-form input[type=number],.pure-form input[type=password],.pure-form input[type=search],.pure-form input[type=tel],.pure-form input[type=text],.pure-form input[type=time],.pure-form input[type=url],.pure-form input[type=week],.pure-form select,.pure-form textarea{padding:.5em .6em;display:inline-block;border:1px solid #ccc;-webkit-box-shadow:inset 0 1px 3px #ddd;box-shadow:inset 0 1px 3px #ddd;border-radius:4px;vertical-align:middle;-webkit-box-sizing:border-box;box-sizing:border-box}.pure-form input:not([type]){padding:.5em .6em;display:inline-block;border:1px solid #ccc;-webkit-box-shadow:inset 0 1px 3px #ddd;box-shadow:inset 0 1px 3px #ddd;border-radius:4px;-webkit-box-sizing:border-box;box-sizing:border-box}.pure-form input[type=color]{padding:.2em .5em}.pure-form input[type=color]:focus,.pure-form input[type=date]:focus,.pure-form input[type=datetime-local]:focus,.pure-form input[type=datetime]:focus,.pure-form input[type=email]:focus,.pure-form input[type=month]:focus,.pure-form input[type=number]:focus,.pure-form input[type=password]:focus,.pure-form input[type=search]:focus,.pure-form input[type=tel]:focus,.pure-form input[type=text]:focus,.pure-form input[type=time]:focus,.pure-form input[type=url]:focus,.pure-form input[type=week]:focus,.pure-form select:focus,.pure-form textarea:focus{outline:0;border-color:#129fea}.pure-form input:not([type]):focus{outline:0;border-color:#129fea}.pure-form input[type=checkbox]:focus,.pure-form input[type=file]:focus,.pure-form input[type=radio]:focus{outline:thin solid #129FEA;outline:1px auto #129FEA}.pure-form .pure-checkbox,.pure-form .pure-radio{margin:.5em 0;display:block}.pure-form input[type=color][disabled],.pure-form input[type=date][disabled],.pure-form input[type=datetime-local][disabled],.pure-form input[type=datetime][disabled],.pure-form input[type=email][disabled],.pure-form input[type=month][disabled],.pure-form input[type=number][disabled],.pure-form input[type=password][disabled],.pure-form input[type=search][disabled],.pure-form input[type=tel][disabled],.pure-form input[type=text][disabled],.pure-form input[type=time][disabled],.pure-form input[type=url][disabled],.pure-form input[type=week][disabled],.pure-form select[disabled],.pure-form textarea[disabled]{cursor:not-allowed;background-color:#eaeded;color:#cad2d3}.pure-form input:not([type])[disabled]{cursor:not-allowed;background-color:#eaeded;color:#cad2d3}.pure-form input[readonly],.pure-form select[readonly],.pure-form textarea[readonly]{background-color:#eee;color:#777;border-color:#ccc}.pure-form input:focus:invalid,.pure-form select:focus:invalid,.pure-form textarea:focus:invalid{color:#b94a48;border-color:#e9322d}.pure-form input[type=checkbox]:focus:invalid:focus,.pure-form input[type=file]:focus:invalid:focus,.pure-form input[type=radio]:focus:invalid:focus{outline-color:#e9322d}.pure-form select{height:2.25em;border:1px solid #ccc;background-color:#fff}.pure-form select[multiple]{height:auto}.pure-form label{margin:.5em 0 .2em}.pure-form fieldset{margin:0;padding:.35em 0 .75em;border:0}.pure-form legend{display:block;width:100%;padding:.3em 0;margin-bottom:.3em;color:#333;border-bottom:1px solid #e5e5e5}.pure-form-stacked input[type=color],.pure-form-stacked input[type=date],.pure-form-stacked input[type=datetime-local],.pure-form-stacked input[type=datetime],.pure-form-stacked input[type=email],.pure-form-stacked input[type=file],.pure-form-stacked input[type=month],.pure-form-stacked input[type=number],.pure-form-stacked input[type=password],.pure-form-stacked input[type=search],.pure-form-stacked input[type=tel],.pure-form-stacked input[type=text],.pure-form-stacked input[type=time],.pure-form-stacked input[type=url],.pure-form-stacked input[type=week],.pure-form-stacked label,.pure-form-stacked select,.pure-form-stacked textarea{display:block;margin:.25em 0}.pure-form-stacked input:not([type]){display:block;margin:.25em 0}.pure-form-aligned input,.pure-form-aligned select,.pure-form-aligned textarea,.pure-form-message-inline{display:inline-block;vertical-align:middle}.pure-form-aligned textarea{vertical-align:top}.pure-form-aligned .pure-control-group{margin-bottom:.5em}.pure-form-aligned .pure-control-group label{text-align:right;display:inline-block;vertical-align:middle;width:10em;margin:0 1em 0 0}.pure-form-aligned .pure-controls{margin:1.5em 0 0 11em}.pure-form .pure-input-rounded,.pure-form input.pure-input-rounded{border-radius:2em;padding:.5em 1em}.pure-form .pure-group fieldset{margin-bottom:10px}.pure-form .pure-group input,.pure-form .pure-group textarea{display:block;padding:10px;margin:0 0 -1px;border-radius:0;position:relative;top:-1px}.pure-form .pure-group input:focus,.pure-form .pure-group textarea:focus{z-index:3}.pure-form .pure-group input:first-child,.pure-form .pure-group textarea:first-child{top:1px;border-radius:4px 4px 0 0;margin:0}.pure-form .pure-group input:first-child:last-child,.pure-form .pure-group textarea:first-child:last-child{top:1px;border-radius:4px;margin:0}.pure-form .pure-group input:last-child,.pure-form .pure-group textarea:last-child{top:-2px;border-radius:0 0 4px 4px;margin:0}.pure-form .pure-group button{margin:.35em 0}.pure-form .pure-input-1{width:100%}.pure-form .pure-input-3-4{width:75%}.pure-form .pure-input-2-3{width:66%}.pure-form .pure-input-1-2{width:50%}.pure-form .pure-input-1-3{width:33%}.pure-form .pure-input-1-4{width:25%}.pure-form-message-inline{display:inline-block;padding-left:.3em;color:#666;vertical-align:middle;font-size:.875em}.pure-form-message{display:block;color:#666;font-size:.875em}@media only screen and (max-width :480px){.pure-form button[type=submit]{margin:.7em 0 0}.pure-form input:not([type]),.pure-form input[type=color],.pure-form input[type=date],.pure-form input[type=datetime-local],.pure-form input[type=datetime],.pure-form input[type=email],.pure-form input[type=month],.pure-form input[type=number],.pure-form input[type=password],.pure-form input[type=search],.pure-form input[type=tel],.pure-form input[type=text],.pure-form input[type=time],.pure-form input[type=url],.pure-form input[type=week],.pure-form label{margin-bottom:.3em;display:block}.pure-group input:not([type]),.pure-group input[type=color],.pure-group input[type=date],.pure-group input[type=datetime-local],.pure-group input[type=datetime],.pure-group input[type=email],.pure-group input[type=month],.pure-group input[type=number],.pure-group input[type=password],.pure-group input[type=search],.pure-group input[type=tel],.pure-group input[type=text],.pure-group input[type=time],.pure-group input[type=url],.pure-group input[type=week]{margin-bottom:0}.pure-form-aligned .pure-control-group label{margin-bottom:.3em;text-align:left;display:block;width:100%}.pure-form-aligned .pure-controls{margin:1.5em 0 0 0}.pure-form-message,.pure-form-message-inline{display:block;font-size:.75em;padding:.2em 0 .8em}}.pure-menu{-webkit-box-sizing:border-box;box-sizing:border-box}.pure-menu-fixed{position:fixed;left:0;top:0;z-index:3}.pure-menu-item,.pure-menu-list{position:relative}.pure-menu-list{list-style:none;margin:0;padding:0}.pure-menu-item{padding:0;margin:0;height:100%}.pure-menu-heading,.pure-menu-link{display:block;text-decoration:none;white-space:nowrap}.pure-menu-horizontal{width:100%;white-space:nowrap}.pure-menu-horizontal .pure-menu-list{display:inline-block}.pure-menu-horizontal .pure-menu-heading,.pure-menu-horizontal .pure-menu-item,.pure-menu-horizontal .pure-menu-separator{display:inline-block;vertical-align:middle}.pure-menu-item .pure-menu-item{display:block}.pure-menu-children{display:none;position:absolute;left:100%;top:0;margin:0;padding:0;z-index:3}.pure-menu-horizontal .pure-menu-children{left:0;top:auto;width:inherit}.pure-menu-active>.pure-menu-children,.pure-menu-allow-hover:hover>.pure-menu-children{display:block;position:absolute}.pure-menu-has-children>.pure-menu-link:after{padding-left:.5em;content:"\25B8";font-size:small}.pure-menu-horizontal .pure-menu-has-children>.pure-menu-link:after{content:"\25BE"}.pure-menu-scrollable{overflow-y:scroll;overflow-x:hidden}.pure-menu-scrollable .pure-menu-list{display:block}.pure-menu-horizontal.pure-menu-scrollable .pure-menu-list{display:inline-block}.pure-menu-horizontal.pure-menu-scrollable{white-space:nowrap;overflow-y:hidden;overflow-x:auto;padding:.5em 0}.pure-menu-horizontal .pure-menu-children .pure-menu-separator,.pure-menu-separator{background-color:#ccc;height:1px;margin:.3em 0}.pure-menu-horizontal .pure-menu-separator{width:1px;height:1.3em;margin:0 .3em}.pure-menu-horizontal .pure-menu-children .pure-menu-separator{display:block;width:auto}.pure-menu-heading{text-transform:uppercase;color:#565d64}.pure-menu-link{color:#777}.pure-menu-children{background-color:#fff}.pure-menu-heading,.pure-menu-link{padding:.5em 1em}.pure-menu-disabled{opacity:.5}.pure-menu-disabled .pure-menu-link:hover{background-color:transparent;cursor:default}.pure-menu-active>.pure-menu-link,.pure-menu-link:focus,.pure-menu-link:hover{background-color:#eee}.pure-menu-selected>.pure-menu-link,.pure-menu-selected>.pure-menu-link:visited{color:#000}.pure-table{border-collapse:collapse;border-spacing:0;empty-cells:show;border:1px solid #cbcbcb}.pure-table caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.pure-table td,.pure-table th{border-left:1px solid #cbcbcb;border-width:0 0 0 1px;font-size:inherit;margin:0;overflow:visible;padding:.5em 1em}.pure-table thead{background-color:#e0e0e0;color:#000;text-align:left;vertical-align:bottom}.pure-table td{background-color:transparent}.pure-table-odd td{background-color:#f2f2f2}.pure-table-striped tr:nth-child(2n-1) td{background-color:#f2f2f2}.pure-table-bordered td{border-bottom:1px solid #cbcbcb}.pure-table-bordered tbody>tr:last-child>td{border-bottom-width:0}.pure-table-horizontal td,.pure-table-horizontal th{border-width:0 0 1px 0;border-bottom:1px solid #cbcbcb}.pure-table-horizontal tbody>tr:last-child>td{border-bottom-width:0} diff --git a/examples/backgrounded_futures.rs b/examples/backgrounded_futures.rs index 4fabef09f7..4cafad439e 100644 --- a/examples/backgrounded_futures.rs +++ b/examples/backgrounded_futures.rs @@ -11,7 +11,7 @@ use async_std::task::sleep; use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/calculator.rs b/examples/calculator.rs index b373923f6b..9bf2a04618 100644 --- a/examples/calculator.rs +++ b/examples/calculator.rs @@ -12,10 +12,10 @@ use dioxus::events::*; use dioxus::html::input_data::keyboard_types::Key; use dioxus::prelude::*; -const STYLE: Asset = asset!("/examples/assets/calculator.css"); +const STYLE: &str = asset!("./examples/assets/calculator.css"); fn main() { - dioxus::launch::builder() + LaunchBuilder::desktop() .with_cfg(desktop!({ use dioxus::desktop::{Config, LogicalSize, WindowBuilder}; Config::new().with_window( @@ -54,7 +54,7 @@ fn app() -> Element { }; rsx! { - document::Stylesheet { href: STYLE } + head::Link { rel: "stylesheet", href: STYLE } div { id: "wrapper", div { class: "app", div { class: "calculator", tabindex: "0", onkeydown: handle_key_down_event, diff --git a/examples/calculator_mutable.rs b/examples/calculator_mutable.rs index 9d1ec05fbc..2f933ebbea 100644 --- a/examples/calculator_mutable.rs +++ b/examples/calculator_mutable.rs @@ -13,7 +13,7 @@ use dioxus::html::MouseEvent; use dioxus::prelude::*; fn main() { - dioxus::launch::builder() + LaunchBuilder::desktop() .with_cfg( Config::new().with_window( WindowBuilder::new() @@ -29,7 +29,7 @@ fn app() -> Element { let mut state = use_signal(Calculator::new); rsx! { - document::Stylesheet { href: asset!("/examples/assets/calculator.css") } + head::Link { rel: "stylesheet", href: asset!("./examples/assets/calculator.css") } div { id: "wrapper", div { class: "app", div { diff --git a/examples/clock.rs b/examples/clock.rs index d344311fae..dc168a5ec2 100644 --- a/examples/clock.rs +++ b/examples/clock.rs @@ -5,18 +5,18 @@ use async_std::task::sleep; use dioxus::prelude::*; use web_time::Instant; -const STYLE: Asset = asset!("/examples/assets/clock.css"); +const STYLE: &str = asset!("./examples/assets/clock.css"); fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { let mut millis = use_signal(|| 0); use_future(move || async move { - // Save our initial time - let start = std::time::Instant::now(); + // Save our initial timea + let start = Instant::now(); loop { sleep(std::time::Duration::from_millis(27)).await; @@ -36,7 +36,7 @@ fn app() -> Element { ); rsx! { - document::Stylesheet { href: STYLE } + head::Link { rel: "stylesheet", href: STYLE } div { id: "app", div { id: "title", "Carpe diem 🎉" } div { id: "clock-display", "{time}" } diff --git a/examples/control_focus.rs b/examples/control_focus.rs index 6a383fa662..c69293c56d 100644 --- a/examples/control_focus.rs +++ b/examples/control_focus.rs @@ -5,8 +5,13 @@ use std::rc::Rc; +use async_std::task::sleep; +use dioxus::prelude::*; + +const STYLE: &str = asset!("./examples/assets/roulette.css"); + fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { @@ -35,7 +40,7 @@ fn app() -> Element { }); rsx! { - document::Stylesheet { href: asset!("/examples/assets/roulette.css") } + head::Link { rel: "stylesheet", href: STYLE } h1 { "Input Roulette" } button { onclick: move |_| running.toggle(), "Toggle roulette" } div { id: "roulette-grid", diff --git a/examples/counters.rs b/examples/counters.rs index 63013a1cc5..f2d8c4203c 100644 --- a/examples/counters.rs +++ b/examples/counters.rs @@ -2,8 +2,10 @@ use dioxus::prelude::*; +const STYLE: &str = asset!("./examples/assets/counter.css"); + fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { @@ -14,7 +16,7 @@ fn app() -> Element { let sum = use_memo(move || counters.read().iter().copied().sum::()); rsx! { - document::Stylesheet { href: asset!("/examples/assets/counter.css") } + head::Link { rel: "stylesheet", href: STYLE } div { id: "controls", button { onclick: move |_| counters.write().push(0), "Add counter" } diff --git a/examples/crm.rs b/examples/crm.rs index ca83f58d40..21f07466cd 100644 --- a/examples/crm.rs +++ b/examples/crm.rs @@ -12,7 +12,7 @@ use dioxus::prelude::*; fn main() { - dioxus::builder() + LaunchBuilder::new() .with_cfg(desktop!({ use dioxus::desktop::{LogicalSize, WindowBuilder}; dioxus::desktop::Config::default() @@ -20,8 +20,13 @@ fn main() { })) .launch(|| { rsx! { - document::Stylesheet { href: asset!("/examples/assets/crm.css") } - document::Link { href: asset!("/examples/assets/purecss.css") } + head::Link { + rel: "stylesheet", + href: asset!("https://unpkg.com/purecss@2.0.6/build/pure-min.css"), + integrity: "sha384-Uu6IeWbM+gzNVXJcM9XV3SohHtmWE+3VGi496jvgX1jyvDTXfdK+rfZc8C1Aehk5", + crossorigin: "anonymous" + } + head::Link { rel: "stylesheet", href: asset!("./examples/assets/crm.css") } h1 { "Dioxus CRM Example" } Router:: {} } diff --git a/examples/custom_assets.rs b/examples/custom_assets.rs index 8a56530110..6c927d21a4 100644 --- a/examples/custom_assets.rs +++ b/examples/custom_assets.rs @@ -1,37 +1,27 @@ //! A simple example on how to use assets loading from the filesystem. //! -//! Dioxus provides an asset!() macro which properly handles asset loading and bundling for you. -//! For bundling, asset!() must be paired with a tool that handles mangansis-link sections. The dioxus-cli -//! handles this for you, but this means you can't just simply `cargo build --release` to build and -//! distribute your app. +//! If the feature "collect-assets" is enabled, the assets will be collected via the dioxus CLI and embedded into the +//! final bundle. This lets you do various useful things like minify, compress, and optimize your assets. //! -//! You can run this example with `cargo run --example assets` or `dx serve --example assets`. -//! When manganis is not active, the asset!() macro will fallback to the path of the asset on -//! your filesystem. +//! We can still use assets without the CLI middleware, but generally larger apps will benefit from it. + use dioxus::prelude::*; -/// asset!() will mark this asset as a dependency of the app without actually including it in the -/// generated code. This is better than include_str!() or include_bytes!() since it works -/// for web apps as well as native and mobile apps. -/// -/// When used with web apps, manganis will detect the import of the image, optimize it, and put it -/// in the output dist folder in the right location, ensuring no two images have the same name. -static IMAGE: ImageAsset = asset!("/examples/assets/logo.png".image().format(ImageType::Avif)); +#[cfg(not(feature = "collect-assets"))] +static ASSET_PATH: &str = "examples/assets/logo.png"; + +#[cfg(feature = "collect-assets")] +static ASSET_PATH: &str = asset!("examples/assets/logo.png".format(ImageType::Avif)); fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { rsx! { div { h1 { "This should show an image:" } - img { src: IMAGE } - - // temporarily keep support for these too - img { src: "/Users/jonkelley/Development/dioxus/examples/assets/logo.png" } - img { src: "/examples/assets/logo.png" } - img { src: "examples/assets/logo.png" } + img { src: ASSET_PATH.to_string() } } } } diff --git a/examples/custom_html.rs b/examples/custom_html.rs index f1ea4b91ad..dbbe334f05 100644 --- a/examples/custom_html.rs +++ b/examples/custom_html.rs @@ -4,7 +4,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch::builder() + LaunchBuilder::new() .with_cfg( dioxus::desktop::Config::new().with_custom_index( r#" diff --git a/examples/custom_menu.rs b/examples/custom_menu.rs index da1085bfd5..f78f33c884 100644 --- a/examples/custom_menu.rs +++ b/examples/custom_menu.rs @@ -28,7 +28,7 @@ fn main() { let config = dioxus::desktop::Config::new().with_menu(menu); // Launch the app with the custom menu - dioxus::launch::builder().with_cfg(config).launch(app) + LaunchBuilder::new().with_cfg(config).launch(app) } fn app() -> Element { diff --git a/examples/disabled.rs b/examples/disabled.rs index b48b1a912e..6aaa70b9d4 100644 --- a/examples/disabled.rs +++ b/examples/disabled.rs @@ -5,7 +5,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/dog_app.rs b/examples/dog_app.rs index 09bf5f0a96..1c53c8731f 100644 --- a/examples/dog_app.rs +++ b/examples/dog_app.rs @@ -11,7 +11,7 @@ use dioxus::prelude::*; use std::collections::HashMap; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/dynamic_asset.rs b/examples/dynamic_asset.rs index b741ba57af..14a33c4809 100644 --- a/examples/dynamic_asset.rs +++ b/examples/dynamic_asset.rs @@ -7,8 +7,10 @@ use dioxus::desktop::{use_asset_handler, wry::http::Response}; use dioxus::prelude::*; +const STYLE: &str = asset!("./examples/assets/custom_assets.css"); + fn main() { - dioxus::launch(app); + launch_desktop(app); } fn app() -> Element { @@ -22,7 +24,7 @@ fn app() -> Element { }); rsx! { - document::Stylesheet { href: asset!("/examples/assets/custom_assets.css") } + head::Link { rel: "stylesheet", href: STYLE } h1 { "Dynamic Assets" } img { src: "/logos/logo.png" } } diff --git a/examples/errors.rs b/examples/errors.rs index fe16598e95..4727cb5e03 100644 --- a/examples/errors.rs +++ b/examples/errors.rs @@ -11,11 +11,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(|| { - rsx! { - Router:: {} - } - }); + launch(|| rsx! { Router:: {} }); } /// You can use an ErrorBoundary to catch errors in children and display a warning @@ -38,8 +34,11 @@ fn ParseNumber() -> Element { h1 { "Error handler demo" } button { onclick: move |_| { + // You can return a result from an event handler which lets you easily quit rendering early if something fails let data: i32 = "0.5".parse()?; + println!("parsed {data}"); + Ok(()) }, "Click to throw an error" @@ -59,7 +58,10 @@ fn Show() -> Element { if let Some(error) = error.show() { {error} } else { - pre { color: "red", "{error}" } + pre { + color: "red", + "{error}" + } } } } @@ -86,10 +88,15 @@ fn ParseNumberWithShow() -> Element { border_width: "2px", border_radius: "5px", p { "Failed to parse data" } - Link { to: Route::Home {}, "Go back to the homepage" } + Link { + to: Route::Home {}, + "Go back to the homepage" + } } })?; + println!("parsed {data}"); + Ok(()) }, "Click to throw an error" @@ -132,13 +139,22 @@ fn Home() -> Element { rsx! { ul { li { - Link { to: Route::Simple {}, "Simple errors" } + Link { + to: Route::Simple {}, + "Simple errors" + } } li { - Link { to: Route::Panic {}, "Capture panics" } + Link { + to: Route::Panic {}, + "Capture panics" + } } li { - Link { to: Route::Show {}, "Show errors" } + Link { + to: Route::Show {}, + "Show errors" + } } } } diff --git a/examples/eval.rs b/examples/eval.rs index 68d26c8b09..9cfd15e941 100644 --- a/examples/eval.rs +++ b/examples/eval.rs @@ -7,7 +7,7 @@ use async_std::task::sleep; use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { @@ -19,24 +19,29 @@ fn app() -> Element { // The `eval` is available in the prelude - and simply takes a block of JS. // Dioxus' eval is interesting since it allows sending messages to and from the JS code using the `await dioxus.recv()` // builtin function. This allows you to create a two-way communication channel between Rust and JS. - let mut eval = document::eval( + let mut eval = eval( r#" - return "hi from JS!"; + dioxus.send("Hi from JS!"); + let msg = await dioxus.recv(); + console.log(msg); + return "hi from JS!"; "#, ); - // This will print "Hi from JS!" and "Hi from Rust!". - let res = eval.await; + // Send a message to the JS code. + eval.send("Hi from Rust!".into()).unwrap(); + + // Our line on the JS side will log the message and then return "hello world". + let res = eval.recv().await.unwrap(); - println!("hello from js! {:?}", res); + // This will print "Hi from JS!" and "Hi from Rust!". + println!("{:?}", eval.await); res }); - todo!() - // future.read_unchecked().as_ref().map(|f| match f { - // Some(Ok(v)) => rsx!( p { "{v:?}" } ), - // Some(Err(e)) => rsx!( p { "{v:?}" } ), - // None => rsx!( p { "waiting.." } ), - // }) + match future.value().as_ref() { + Some(v) => rsx!( p { "{v}" } ), + _ => rsx!( p { "waiting.." } ), + } } diff --git a/examples/file_explorer.rs b/examples/file_explorer.rs index c5d5f31756..1ac571d2e8 100644 --- a/examples/file_explorer.rs +++ b/examples/file_explorer.rs @@ -9,7 +9,7 @@ use dioxus::desktop::{Config, WindowBuilder}; use dioxus::prelude::*; fn main() { - dioxus::launch::builder() + LaunchBuilder::desktop() .with_cfg(Config::new().with_window(WindowBuilder::new().with_resizable(true))) .launch(app) } @@ -18,12 +18,12 @@ fn app() -> Element { let mut files = use_signal(Files::new); rsx! { - document::Link { + head::Link { rel: "stylesheet", - href: asset!("/examples/assets/fileexplorer.css") + href: asset!("./examples/assets/fileexplorer.css") } div { - document::Link { href: "https://fonts.googleapis.com/icon?family=Material+Icons", rel: "stylesheet" } + head::Link { href: "https://fonts.googleapis.com/icon?family=Material+Icons", rel: "stylesheet" } header { i { class: "material-icons icon-menu", "menu" } h1 { "Files: " {files.read().current()} } diff --git a/examples/file_upload.rs b/examples/file_upload.rs index ffc1858238..9fb74072bb 100644 --- a/examples/file_upload.rs +++ b/examples/file_upload.rs @@ -8,10 +8,10 @@ use std::sync::Arc; use dioxus::prelude::*; use dioxus::{html::HasFileData, prelude::dioxus_elements::FileEngine}; -const STYLE: Asset = asset!("/examples/assets/file_upload.css"); +const STYLE: &str = asset!("./examples/assets/file_upload.css"); fn main() { - dioxus::launch(app); + launch(app); } struct UploadedFile { @@ -43,7 +43,7 @@ fn app() -> Element { }; rsx! { - document::Stylesheet { href: STYLE } + head::Link { rel: "stylesheet", href: STYLE } h1 { "File Upload Example" } p { "Drop a .txt, .rs, or .js file here to read it" } diff --git a/examples/flat_router.rs b/examples/flat_router.rs index d537ee4c1b..79a9d2a3f2 100644 --- a/examples/flat_router.rs +++ b/examples/flat_router.rs @@ -9,12 +9,12 @@ use dioxus::prelude::*; -const STYLE: Asset = asset!("/examples/assets/flat_router.css"); +const STYLE: &str = asset!("./examples/assets/flat_router.css"); fn main() { - dioxus::launch(|| { + launch(|| { rsx! { - document::Stylesheet { href: STYLE } + head::Link { rel: "stylesheet", href: STYLE } Router:: {} } }) diff --git a/examples/form.rs b/examples/form.rs index ff6154b9d4..91b09ecd34 100644 --- a/examples/form.rs +++ b/examples/form.rs @@ -7,7 +7,7 @@ use dioxus::prelude::*; use std::collections::HashMap; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/future.rs b/examples/future.rs index e32386fe83..f4ddc10468 100644 --- a/examples/future.rs +++ b/examples/future.rs @@ -7,7 +7,7 @@ use async_std::task::sleep; use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/generic_component.rs b/examples/generic_component.rs index 520f257787..6af34e542b 100644 --- a/examples/generic_component.rs +++ b/examples/generic_component.rs @@ -7,7 +7,7 @@ use dioxus::prelude::*; use std::fmt::Display; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/global.rs b/examples/global.rs index 693b912e93..0a08485b02 100644 --- a/examples/global.rs +++ b/examples/global.rs @@ -7,18 +7,18 @@ use dioxus::prelude::*; -const STYLE: Asset = asset!("/examples/assets/counter.css"); +const STYLE: &str = asset!("./examples/assets/counter.css"); static COUNT: GlobalSignal = Signal::global(|| 0); static DOUBLED_COUNT: GlobalMemo = Memo::global(|| COUNT() * 2); fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { rsx! { - document::Stylesheet { href: STYLE } + head::Link { rel: "stylesheet", href: STYLE } Increment {} Decrement {} Reset {} diff --git a/examples/hash_fragment_state.rs b/examples/hash_fragment_state.rs index bc0ec2cc88..ed77c8c46e 100644 --- a/examples/hash_fragment_state.rs +++ b/examples/hash_fragment_state.rs @@ -2,7 +2,7 @@ //! //! You can set up two way data binding between the url hash and signals. //! -//! Run this example on desktop with +//! Run this example on desktop with //! ```sh //! dx serve --example hash_fragment_state --features=ciborium,base64 //! ``` @@ -19,7 +19,7 @@ use dioxus::prelude::*; use serde::{Deserialize, Serialize}; fn main() { - dioxus::launch(|| { + launch(|| { rsx! { Router:: {} } diff --git a/examples/hello_world.rs b/examples/hello_world.rs index 20c9af934a..ee33c22309 100644 --- a/examples/hello_world.rs +++ b/examples/hello_world.rs @@ -12,7 +12,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/hydration.rs b/examples/hydration.rs index cffacc86aa..1e7de2706f 100644 --- a/examples/hydration.rs +++ b/examples/hydration.rs @@ -13,7 +13,7 @@ use dioxus::desktop::Config; use dioxus::prelude::*; fn main() { - dioxus::launch::builder() + LaunchBuilder::desktop() .with_cfg(Config::new().with_prerendered({ // We build the dom a first time, then pre-render it to HTML let pre_rendered_dom = VirtualDom::prebuilt(app); diff --git a/examples/image_generator_openai.rs b/examples/image_generator_openai.rs index 67c743c427..474c46de90 100644 --- a/examples/image_generator_openai.rs +++ b/examples/image_generator_openai.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; use serde_json::{json, Error}; fn main() { - dioxus::launch(app) + launch(app) } fn app() -> Element { @@ -36,7 +36,7 @@ fn app() -> Element { }); rsx! { - document::Stylesheet { href: "https://unpkg.com/bulma@0.9.0/css/bulma.min.css" } + head::Link { rel: "stylesheet", href: "https://unpkg.com/bulma@0.9.0/css/bulma.min.css" } div { class: "container", div { class: "columns", div { class: "column", diff --git a/examples/link.rs b/examples/link.rs index 7d1f4be609..ff29b65626 100644 --- a/examples/link.rs +++ b/examples/link.rs @@ -8,15 +8,15 @@ use dioxus::prelude::*; -const STYLE: Asset = asset!("/examples/assets/links.css"); +const STYLE: &str = asset!("./examples/assets/links.css"); fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { rsx! ( - document::Stylesheet { href: STYLE } + head::Link { rel: "stylesheet", href: STYLE } Router:: {} ) } diff --git a/examples/login_form.rs b/examples/login_form.rs index 617723466c..39e874cc99 100644 --- a/examples/login_form.rs +++ b/examples/login_form.rs @@ -9,7 +9,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/memo_chain.rs b/examples/memo_chain.rs index ab57089397..205fe87693 100644 --- a/examples/memo_chain.rs +++ b/examples/memo_chain.rs @@ -6,7 +6,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/meta.rs b/examples/meta.rs index 9d8e1a4ae0..fae916258f 100644 --- a/examples/meta.rs +++ b/examples/meta.rs @@ -3,7 +3,8 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + tracing_subscriber::fmt::init(); + launch(app); } fn app() -> Element { @@ -11,23 +12,23 @@ fn app() -> Element { // You can use the Meta component to render a meta tag into the head of the page // Meta tags are useful to provide information about the page to search engines and social media sites // This example sets up meta tags for the open graph protocol for social media previews - document::Meta { + Meta { property: "og:title", content: "My Site", } - document::Meta { + Meta { property: "og:type", content: "website", } - document::Meta { + Meta { property: "og:url", content: "https://www.example.com", } - document::Meta { + Meta { property: "og:image", content: "https://example.com/image.jpg", } - document::Meta { + Meta { name: "description", content: "My Site is a site", } diff --git a/examples/mobile_demo/.gitignore b/examples/mobile_demo/.gitignore new file mode 100644 index 0000000000..e1e084c4bb --- /dev/null +++ b/examples/mobile_demo/.gitignore @@ -0,0 +1,10 @@ +# Rust +target/ +**/*.rs.bk + +# cargo-mobile2 +.cargo/ +/gen + +# macOS +.DS_Store diff --git a/examples/mobile_demo/Cargo.lock b/examples/mobile_demo/Cargo.lock new file mode 100644 index 0000000000..7f50292263 --- /dev/null +++ b/examples/mobile_demo/Cargo.lock @@ -0,0 +1,4007 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + +[[package]] +name = "android_log-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85965b6739a430150bdd138e2374a98af0c3ee0d030b3bb7fc3bddff58d0102e" + +[[package]] +name = "android_logger" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ec2333c185d826313162cee39d3fcc6a84ba08114a839bebf53b961e7e75773" +dependencies = [ + "android_log-sys", + "env_logger 0.7.1", + "lazy_static", + "log", +] + +[[package]] +name = "anyhow" +version = "1.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" + +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + +[[package]] +name = "async-lock" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa24f727524730b077666307f2734b4a1a1c57acb79193127dcc8914d5242dd7" +dependencies = [ + "event-listener", +] + +[[package]] +name = "async-task" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae" + +[[package]] +name = "async-trait" +version = "0.1.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "atk" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4af014b17dd80e8af9fa689b2d4a211ddba6eb583c1622f35d0cb543f6b17e4" +dependencies = [ + "atk-sys", + "glib", + "libc", +] + +[[package]] +name = "atk-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "251e0b7d90e33e0ba930891a505a9a35ece37b2dd37a14f3ffc306c13b980009" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "atomic-waker" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +dependencies = [ + "serde", +] + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "blocking" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77231a1c8f801696fc0123ec6150ce92cffb8e164a02afb9c8ddee0e9b65ad65" +dependencies = [ + "async-channel", + "async-lock", + "async-task", + "atomic-waker", + "fastrand", + "futures-lite", + "log", +] + +[[package]] +name = "bumpalo" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" + +[[package]] +name = "bytemuck" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cairo-rs" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" +dependencies = [ + "bitflags 2.4.2", + "cairo-sys-rs", + "glib", + "libc", + "once_cell", + "thiserror", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfb" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" +dependencies = [ + "byteorder", + "fnv", + "uuid", +] + +[[package]] +name = "cfg-expr" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b40ccee03b5175c18cde8f37e7d2a33bcef6f8ec8f7cc0d81090d1bb380949c9" +dependencies = [ + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "cocoa" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" +dependencies = [ + "bitflags 1.3.2", + "block", + "cocoa-foundation", + "core-foundation", + "core-graphics", + "foreign-types 0.5.0", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "931d3837c286f56e3c58423ce4eba12d08db2374461a785c86f672b08b5650d6" +dependencies = [ + "bitflags 1.3.2", + "block", + "core-foundation", + "core-graphics-types", + "foreign-types 0.3.2", + "libc", + "objc", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "combine" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "concurrent-queue" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "const_format" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a214c7af3d04997541b18d432afaff4c455e79e2029079647e72fc2bd27673" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "constcat" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd7e35aee659887cbfb97aaf227ac12cad1a9d7c71e55ff3376839ed4e282d08" + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + +[[package]] +name = "core-graphics" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "970a29baf4110c26fedbc7f82107d42c23f7e88e404c4577ed73fe99ff85a212" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types 0.5.0", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bb142d41022986c1d8ff29103a1411c8a3dfad3552f87a4f8dc50d61d4f4e33" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[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 = "cssparser" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa 0.4.8", + "matches", + "phf 0.8.0", + "proc-macro2", + "quote", + "smallvec", + "syn 1.0.109", +] + +[[package]] +name = "cssparser-macros" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" +dependencies = [ + "quote", + "syn 2.0.52", +] + +[[package]] +name = "darling" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "darling_macro" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown 0.14.0", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case 0.4.0", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dioxus" +version = "0.5.0-alpha.0" +dependencies = [ + "dioxus-config-macro", + "dioxus-core", + "dioxus-core-macro", + "dioxus-desktop", + "dioxus-fullstack", + "dioxus-hooks", + "dioxus-hot-reload", + "dioxus-html", + "dioxus-mobile", + "dioxus-signals", +] + +[[package]] +name = "dioxus-cli-config" +version = "0.5.0-alpha.0" +dependencies = [ + "once_cell", + "serde", + "serde_json", + "tracing", +] + +[[package]] +name = "dioxus-config-macro" +version = "0.5.0-alpha.0" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "dioxus-core" +version = "0.5.0-alpha.0" +dependencies = [ + "futures-channel", + "futures-util", + "longest-increasing-subsequence", + "rustc-hash", + "serde", + "slab", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "dioxus-core-macro" +version = "0.5.0-alpha.0" +dependencies = [ + "constcat", + "convert_case 0.6.0", + "dioxus-rsx", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "dioxus-debug-cell" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ea539174bb236e0e7dc9c12b19b88eae3cb574dedbd0252a2d43ea7e6de13e2" + +[[package]] +name = "dioxus-desktop" +version = "0.5.0-alpha.0" +dependencies = [ + "async-trait", + "core-foundation", + "dioxus-cli-config", + "dioxus-core", + "dioxus-hooks", + "dioxus-hot-reload", + "dioxus-html", + "dioxus-interpreter-js", + "dunce", + "futures-channel", + "futures-util", + "generational-box", + "global-hotkey", + "infer", + "muda", + "objc", + "objc_id", + "rfd", + "rustc-hash", + "serde", + "serde_json", + "slab", + "tao", + "thiserror", + "tokio", + "tracing", + "urlencoding", + "webbrowser", + "wry 0.37.0", +] + +[[package]] +name = "dioxus-fullstack" +version = "0.5.0-alpha.0" +dependencies = [ + "async-trait", + "base64", + "bytes", + "ciborium", + "dioxus-hot-reload", + "dioxus-lib", + "dioxus-mobile", + "dioxus_server_macro", + "futures-util", + "once_cell", + "serde", + "serde_json", + "server_fn", + "tracing", +] + +[[package]] +name = "dioxus-hooks" +version = "0.5.0-alpha.0" +dependencies = [ + "dioxus-core", + "dioxus-debug-cell", + "dioxus-signals", + "futures-channel", + "futures-util", + "generational-box", + "slab", + "thiserror", + "tracing", +] + +[[package]] +name = "dioxus-hot-reload" +version = "0.5.0-alpha.0" +dependencies = [ + "dioxus-core", + "dioxus-html", + "dioxus-rsx", + "interprocess", + "serde", + "serde_json", +] + +[[package]] +name = "dioxus-html" +version = "0.5.0-alpha.0" +dependencies = [ + "async-trait", + "dioxus-core", + "dioxus-html-internal-macro", + "enumset", + "euclid", + "futures-channel", + "generational-box", + "keyboard-types", + "serde", + "serde-value", + "serde_json", + "serde_repr", + "tokio", + "web-sys", +] + +[[package]] +name = "dioxus-html-internal-macro" +version = "0.5.0-alpha.0" +dependencies = [ + "convert_case 0.6.0", + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "dioxus-interpreter-js" +version = "0.5.0-alpha.0" +dependencies = [ + "dioxus-core", + "dioxus-html", + "md5", + "sledgehammer_bindgen", + "sledgehammer_utils", +] + +[[package]] +name = "dioxus-lib" +version = "0.5.0-alpha.0" +dependencies = [ + "dioxus-core", + "dioxus-core-macro", + "dioxus-hooks", + "dioxus-html", + "dioxus-rsx", + "dioxus-signals", +] + +[[package]] +name = "dioxus-mobile" +version = "0.5.0-alpha.0" +dependencies = [ + "dioxus-desktop", +] + +[[package]] +name = "dioxus-rsx" +version = "0.5.0-alpha.0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", + "tracing", +] + +[[package]] +name = "dioxus-signals" +version = "0.5.0-alpha.0" +dependencies = [ + "dioxus-core", + "futures-channel", + "futures-util", + "generational-box", + "once_cell", + "parking_lot", + "rustc-hash", + "tracing", +] + +[[package]] +name = "dioxus_server_macro" +version = "0.5.0-alpha.0" +dependencies = [ + "convert_case 0.6.0", + "proc-macro2", + "quote", + "server_fn_macro", + "syn 2.0.52", +] + +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + +[[package]] +name = "dlopen2" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1297103d2bbaea85724fcee6294c2d50b1081f9ad47d0f6f6f61eda65315a6" +dependencies = [ + "dlopen2_derive", + "libc", + "once_cell", + "winapi", +] + +[[package]] +name = "dlopen2_derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "dtoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" + +[[package]] +name = "dtoa-short" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbaceec3c6e4211c79e7b1800fb9680527106beb2f9c51904a3210c03a448c74" +dependencies = [ + "dtoa", +] + +[[package]] +name = "dunce" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" + +[[package]] +name = "enumset" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e875f1719c16de097dee81ed675e2d9bb63096823ed3f0ca827b7dea3028bbbb" +dependencies = [ + "enumset_derive", +] + +[[package]] +name = "enumset_derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08b6c6ab82d70f08844964ba10c7babb716de2ecaeab9be5717918a5177d3af" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "env_logger" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "euclid" +version = "0.22.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f253bc5c813ca05792837a0ff4b3a580336b224512d48f7eda1d7dd9210787" +dependencies = [ + "num-traits", + "serde", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "fdeflate" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "field-offset" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" +dependencies = [ + "memoffset", + "rustc_version", +] + +[[package]] +name = "flate2" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "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.52", +] + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" +dependencies = [ + "mac", + "new_debug_unreachable", +] + +[[package]] +name = "futures" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-executor" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" + +[[package]] +name = "futures-lite" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + +[[package]] +name = "futures-macro" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" + +[[package]] +name = "futures-util" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "gdk" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5ba081bdef3b75ebcdbfc953699ed2d7417d6bd853347a42a37d76406a33646" +dependencies = [ + "cairo-rs", + "gdk-pixbuf", + "gdk-sys", + "gio", + "glib", + "libc", + "pango", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" +dependencies = [ + "gdk-pixbuf-sys", + "gio", + "glib", + "libc", + "once_cell", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gdk-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31ff856cb3386dae1703a920f803abafcc580e9b5f711ca62ed1620c25b51ff2" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkwayland-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a90fbf5c033c65d93792192a49a8efb5bb1e640c419682a58bb96f5ae77f3d4a" +dependencies = [ + "gdk-sys", + "glib-sys", + "gobject-sys", + "libc", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkx11" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2ea8a4909d530f79921290389cbd7c34cb9d623bfe970eaae65ca5f9cd9cce" +dependencies = [ + "gdk", + "gdkx11-sys", + "gio", + "glib", + "libc", + "x11", +] + +[[package]] +name = "gdkx11-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fee8f00f4ee46cad2939b8990f5c70c94ff882c3028f3cc5abf950fa4ab53043" +dependencies = [ + "gdk-sys", + "glib-sys", + "libc", + "system-deps", + "x11", +] + +[[package]] +name = "generational-box" +version = "0.5.0-alpha.0" +dependencies = [ + "parking_lot", +] + +[[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 = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "gimli" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" + +[[package]] +name = "gio" +version = "0.18.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys", + "glib", + "libc", + "once_cell", + "pin-project-lite", + "smallvec", + "thiserror", +] + +[[package]] +name = "gio-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", + "winapi", +] + +[[package]] +name = "glib" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" +dependencies = [ + "bitflags 2.4.2", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "memchr", + "once_cell", + "smallvec", + "thiserror", +] + +[[package]] +name = "glib-macros" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" +dependencies = [ + "heck", + "proc-macro-crate 2.0.2", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "glib-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" +dependencies = [ + "libc", + "system-deps", +] + +[[package]] +name = "global-hotkey" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0d37e95d3937251ee2019709389bb793c1237f16d45fc0fe7b2464b5f97c68" +dependencies = [ + "crossbeam-channel", + "keyboard-types", + "once_cell", + "thiserror", + "windows-sys 0.52.0", + "x11-dl", +] + +[[package]] +name = "gloo-net" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43aaa242d1239a8822c15c645f02166398da4f8b5c4bae795c1f5b44e9eee173" +dependencies = [ + "futures-channel", + "futures-core", + "futures-sink", + "gloo-utils", + "http 0.2.9", + "js-sys", + "pin-project", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-utils" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gobject-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gtk" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93c4f5e0e20b60e10631a5f06da7fe3dda744b05ad0ea71fee2f47adf865890c" +dependencies = [ + "atk", + "cairo-rs", + "field-offset", + "futures-channel", + "gdk", + "gdk-pixbuf", + "gio", + "glib", + "gtk-sys", + "gtk3-macros", + "libc", + "pango", + "pkg-config", +] + +[[package]] +name = "gtk-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771437bf1de2c1c0b496c11505bdf748e26066bbe942dfc8f614c9460f6d7722" +dependencies = [ + "atk-sys", + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "system-deps", +] + +[[package]] +name = "gtk3-macros" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6063efb63db582968fb7df72e1ae68aa6360dcfb0a75143f34fc7d616bad75e" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "half" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5eceaaeec696539ddaf7b333340f1af35a5aa87ae3e4f3ead0532f72affab2e" +dependencies = [ + "cfg-if", + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" + +[[package]] +name = "home" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "html5ever" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" +dependencies = [ + "log", + "mac", + "markup5ever", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "http" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes", + "fnv", + "itoa 1.0.9", +] + +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa 1.0.9", +] + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "image" +version = "0.24.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527909aa81e20ac3a44803521443a765550f09b5130c2c2fa1ea59c2f8f50a3a" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "num-rational", + "num-traits", +] + +[[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", +] + +[[package]] +name = "indexmap" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +dependencies = [ + "equivalent", + "hashbrown 0.14.0", +] + +[[package]] +name = "infer" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6c16b11a665b26aeeb9b1d7f954cdeb034be38dd00adab4f2ae921a8fee804" +dependencies = [ + "cfb", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "interprocess" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81f2533f3be42fffe3b5e63b71aeca416c1c3bc33e4e27be018521e76b1f38fb" +dependencies = [ + "blocking", + "cfg-if", + "futures-core", + "futures-io", + "intmap", + "libc", + "once_cell", + "rustc_version", + "spinning", + "thiserror", + "to_method", + "winapi", +] + +[[package]] +name = "intmap" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae52f28f45ac2bc96edb7714de995cffc174a395fb0abf5bff453587c980d7b9" + +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "javascriptcore-rs" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca5671e9ffce8ffba57afc24070e906da7fc4b1ba66f2cabebf61bf2ea257fcc" +dependencies = [ + "bitflags 1.3.2", + "glib", + "javascriptcore-rs-sys", +] + +[[package]] +name = "javascriptcore-rs-sys" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1be78d14ffa4b75b66df31840478fef72b51f8c2465d4ca7c194da9f7a5124" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "jni" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec" +dependencies = [ + "cesu8", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", +] + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror", + "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 = "js-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "keyboard-types" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" +dependencies = [ + "bitflags 2.4.2", + "serde", + "unicode-segmentation", +] + +[[package]] +name = "kuchikiki" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e4755b7b995046f510a7520c42b2fed58b77bd94d5a87a8eb43d2fd126da8" +dependencies = [ + "cssparser", + "html5ever", + "indexmap 1.9.3", + "matches", + "selectors", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "libxdo" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00333b8756a3d28e78def82067a377de7fa61b24909000aeaa2b446a948d14db" +dependencies = [ + "libxdo-sys", +] + +[[package]] +name = "libxdo-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db23b9e7e2b7831bbd8aac0bbeeeb7b68cbebc162b227e7052e8e55829a09212" +dependencies = [ + "libc", + "x11", +] + +[[package]] +name = "lock_api" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" + +[[package]] +name = "longest-increasing-subsequence" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3bd0dd2cd90571056fdb71f6275fada10131182f84899f4b2a916e565d81d86" + +[[package]] +name = "lru" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" +dependencies = [ + "hashbrown 0.14.0", +] + +[[package]] +name = "mac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "markup5ever" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" +dependencies = [ + "log", + "phf 0.10.1", + "phf_codegen 0.10.0", + "string_cache", + "string_cache_codegen", + "tendril", +] + +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + +[[package]] +name = "md5" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", + "simd-adler32", +] + +[[package]] +name = "mobile-demo" +version = "0.1.0" +dependencies = [ + "android_logger", + "anyhow", + "core-foundation", + "dioxus", + "env_logger 0.9.3", + "jni 0.19.0", + "log", + "paste", + "wry 0.35.2", +] + +[[package]] +name = "muda" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c47e7625990fc1af2226ea4f34fb2412b03c12639fcb91868581eb3a6893453" +dependencies = [ + "cocoa", + "crossbeam-channel", + "gtk", + "keyboard-types", + "libxdo", + "objc", + "once_cell", + "png", + "thiserror", + "windows-sys 0.52.0", +] + +[[package]] +name = "ndk" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" +dependencies = [ + "bitflags 1.3.2", + "jni-sys", + "ndk-sys", + "num_enum", + "raw-window-handle 0.5.2", + "thiserror", +] + +[[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.4.1+23.1.7779620" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cf2aae958bd232cac5069850591667ad422d263686d75b52a065f9badeee5a3" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi 0.3.2", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", + "objc_exception", +] + +[[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_exception" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" +dependencies = [ + "cc", +] + +[[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.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "ordered-float" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7940cf2ca942593318d07fcf2596cdca60a85c9e7fab408a5e21a4f9dcd40d87" +dependencies = [ + "num-traits", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "pango" +version = "0.18.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" +dependencies = [ + "gio", + "glib", + "libc", + "once_cell", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "parking" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.48.1", +] + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "percent-encoding" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" + +[[package]] +name = "phf" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" +dependencies = [ + "phf_macros", + "phf_shared 0.8.0", + "proc-macro-hack", +] + +[[package]] +name = "phf" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" +dependencies = [ + "phf_shared 0.10.0", +] + +[[package]] +name = "phf_codegen" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" +dependencies = [ + "phf_generator 0.8.0", + "phf_shared 0.8.0", +] + +[[package]] +name = "phf_codegen" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", +] + +[[package]] +name = "phf_generator" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" +dependencies = [ + "phf_shared 0.8.0", + "rand 0.7.3", +] + +[[package]] +name = "phf_generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +dependencies = [ + "phf_shared 0.10.0", + "rand 0.8.5", +] + +[[package]] +name = "phf_macros" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" +dependencies = [ + "phf_generator 0.8.0", + "phf_shared 0.8.0", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "phf_shared" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" +dependencies = [ + "siphasher", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + +[[package]] +name = "png" +version = "0.17.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59871cc5b6cce7eaccca5a802b4173377a1c2ba90654246789a8fa2334426d11" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "prettyplease" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" +dependencies = [ + "proc-macro2", + "syn 2.0.52", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.14", +] + +[[package]] +name = "proc-macro-crate" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" +dependencies = [ + "toml_datetime", + "toml_edit 0.20.2", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + +[[package]] +name = "proc-macro2" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", + "rand_pcg", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[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 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.10", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "raw-window-handle" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" + +[[package]] +name = "raw-window-handle" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42a9830a0e1b9fb145ebb365b8bc4ccd75f290f98c0247deafbbe2c75cefb544" + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "regex" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7b6d6190b7594385f61bd3911cd1be99dfddcfc365a4160cc2ab5bff4aed294" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" + +[[package]] +name = "rfd" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c9e7b57df6e8472152674607f6cc68aa14a748a3157a857a94f516e11aeacc2" +dependencies = [ + "block", + "dispatch", + "glib-sys", + "gobject-sys", + "gtk-sys", + "js-sys", + "log", + "objc", + "objc-foundation", + "objc_id", + "raw-window-handle 0.5.2", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[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 = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "selectors" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe" +dependencies = [ + "bitflags 1.3.2", + "cssparser", + "derive_more", + "fxhash", + "log", + "matches", + "phf 0.8.0", + "phf_codegen 0.8.0", + "precomputed-hash", + "servo_arc", + "smallvec", + "thin-slice", +] + +[[package]] +name = "semver" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" + +[[package]] +name = "send_wrapper" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" +dependencies = [ + "futures-core", +] + +[[package]] +name = "serde" +version = "1.0.180" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea67f183f058fe88a4e3ec6e2788e003840893b91bac4559cabedd00863b3ed" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float", + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.180" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24e744d7782b686ab3b73267ef05697159cc0e5abbed3f47f9933165e5219036" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "serde_json" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" +dependencies = [ + "itoa 1.0.9", + "ryu", + "serde", +] + +[[package]] +name = "serde_qs" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0431a35568651e363364210c91983c1da5eb29404d9f0928b67d4ebcfa7d330c" +dependencies = [ + "percent-encoding", + "serde", + "thiserror", +] + +[[package]] +name = "serde_repr" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "serde_spanned" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" +dependencies = [ + "serde", +] + +[[package]] +name = "server_fn" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2955da1dc5fcd970c182ebf1089af6c5f19051e1f286a21f7b96490a49b7a531" +dependencies = [ + "bytes", + "const_format", + "dashmap", + "futures", + "gloo-net", + "http 1.1.0", + "js-sys", + "once_cell", + "send_wrapper", + "serde", + "serde_json", + "serde_qs", + "server_fn_macro_default", + "thiserror", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "xxhash-rust", +] + +[[package]] +name = "server_fn_macro" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adfdd051ef905fdb3da20942b0c52d536158d7489a724e14cc2fd47323e7ca91" +dependencies = [ + "const_format", + "convert_case 0.6.0", + "proc-macro2", + "quote", + "syn 2.0.52", + "xxhash-rust", +] + +[[package]] +name = "server_fn_macro_default" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "060af1def72353a779fcc184c53e1965d3055a38b9e827f2259b2bff2d9c371e" +dependencies = [ + "server_fn_macro", + "syn 2.0.52", +] + +[[package]] +name = "servo_arc" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432" +dependencies = [ + "nodrop", + "stable_deref_trait", +] + +[[package]] +name = "sha2" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "siphasher" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" + +[[package]] +name = "slab" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +dependencies = [ + "autocfg", +] + +[[package]] +name = "sledgehammer_bindgen" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcfaf791ff02f48f3518ce825d32cf419c13a43c1d8b1232f74ac89f339c46d2" +dependencies = [ + "sledgehammer_bindgen_macro", +] + +[[package]] +name = "sledgehammer_bindgen_macro" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdd941cc539bd3dc694edaf9d0c4e1221d02baa67c6b45ec04fad1024d9e8139" +dependencies = [ + "quote", + "syn 2.0.52", +] + +[[package]] +name = "sledgehammer_utils" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f20798defa0e9d4eff9ca451c7f84774c7378a9c3b5a40112cfa2b3eadb97ae2" +dependencies = [ + "lru", + "once_cell", + "rustc-hash", +] + +[[package]] +name = "smallvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" + +[[package]] +name = "soup3" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "471f924a40f31251afc77450e781cb26d55c0b650842efafc9c6cbd2f7cc4f9f" +dependencies = [ + "futures-channel", + "gio", + "glib", + "libc", + "soup3-sys", +] + +[[package]] +name = "soup3-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebe8950a680a12f24f15ebe1bf70db7af98ad242d9db43596ad3108aab86c27" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "spinning" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d4f0e86297cad2658d92a707320d87bf4e6ae1050287f51d19b67ef3f153a7b" +dependencies = [ + "lock_api", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "string_cache" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" +dependencies = [ + "new_debug_unreachable", + "once_cell", + "parking_lot", + "phf_shared 0.10.0", + "precomputed-hash", + "serde", +] + +[[package]] +name = "string_cache_codegen" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", + "proc-macro2", + "quote", +] + +[[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.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "system-deps" +version = "6.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30c2de8a4d8f4b823d634affc9cd2a74ec98c53a756f317e529a48046cbf71f3" +dependencies = [ + "cfg-expr", + "heck", + "pkg-config", + "toml", + "version-compare", +] + +[[package]] +name = "tao" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccba570365293ca309d60f30fdac2c5271b732dc762e6154e59c85d2c762a0a1" +dependencies = [ + "bitflags 1.3.2", + "cocoa", + "core-foundation", + "core-graphics", + "crossbeam-channel", + "dispatch", + "dlopen2", + "gdkwayland-sys", + "gdkx11-sys", + "gtk", + "image", + "instant", + "jni 0.21.1", + "lazy_static", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "objc", + "once_cell", + "parking_lot", + "png", + "raw-window-handle 0.5.2", + "raw-window-handle 0.6.0", + "scopeguard", + "tao-macros", + "unicode-segmentation", + "url", + "windows", + "windows-implement", + "windows-version", + "x11-dl", +] + +[[package]] +name = "tao-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b27a4bcc5eb524658234589bdffc7e7bfb996dbae6ce9393bfd39cb4159b445" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "target-lexicon" +version = "0.12.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a" + +[[package]] +name = "tendril" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" +dependencies = [ + "futf", + "mac", + "utf-8", +] + +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thin-slice" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" + +[[package]] +name = "thiserror" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "to_method" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c4ceeeca15c8384bbc3e011dbd8fccb7f068a440b752b7d9b32ceb0ca0e2e8" + +[[package]] +name = "tokio" +version = "1.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" +dependencies = [ + "autocfg", + "backtrace", + "bytes", + "num_cpus", + "pin-project-lite", + "tokio-macros", +] + +[[package]] +name = "tokio-macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "toml" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.19.14", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" +dependencies = [ + "indexmap 2.0.0", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "toml_edit" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +dependencies = [ + "indexmap 2.0.0", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "nu-ansi-term", + "sharded-slab", + "smallvec", + "thread_local", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unicode-bidi" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + +[[package]] +name = "unicode-ident" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "url" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[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 = "uuid" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version-compare" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "waker-fn" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" + +[[package]] +name = "walkdir" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[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.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.52", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" + +[[package]] +name = "wasm-streams" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webbrowser" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd222aa310eb7532e3fd427a5d7db7e44bc0b0cf1c1e21139c345325511a85b6" +dependencies = [ + "core-foundation", + "home", + "jni 0.21.1", + "log", + "ndk-context", + "objc", + "raw-window-handle 0.5.2", + "url", + "web-sys", +] + +[[package]] +name = "webkit2gtk" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76b1bc1e54c581da1e9f179d0b38512ba358fb1af2d634a1affe42e37172361a" +dependencies = [ + "bitflags 1.3.2", + "cairo-rs", + "gdk", + "gdk-sys", + "gio", + "gio-sys", + "glib", + "glib-sys", + "gobject-sys", + "gtk", + "gtk-sys", + "javascriptcore-rs", + "libc", + "once_cell", + "soup3", + "webkit2gtk-sys", +] + +[[package]] +name = "webkit2gtk-sys" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62daa38afc514d1f8f12b8693d30d5993ff77ced33ce30cd04deebc267a6d57c" +dependencies = [ + "bitflags 1.3.2", + "cairo-sys-rs", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "gtk-sys", + "javascriptcore-rs-sys", + "libc", + "pkg-config", + "soup3-sys", + "system-deps", +] + +[[package]] +name = "webview2-com" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0ae9c7e420783826cf769d2c06ac9ba462f450eca5893bb8c6c6529a4e5dd33" +dependencies = [ + "webview2-com-macros", + "webview2-com-sys", + "windows", + "windows-core", + "windows-implement", + "windows-interface", +] + +[[package]] +name = "webview2-com-macros" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac1345798ecd8122468840bcdf1b95e5dc6d2206c5e4b0eafa078d061f59c9bc" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "webview2-com-sys" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6ad85fceee6c42fa3d61239eba5a11401bf38407a849ed5ea1b407df08cca72" +dependencies = [ + "thiserror", + "windows", + "windows-core", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[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.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core", + "windows-implement", + "windows-interface", + "windows-targets 0.52.4", +] + +[[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.4", +] + +[[package]] +name = "windows-implement" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12168c33176773b86799be25e2a2ba07c7aab9968b37541f1094dbd7a60c8946" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "windows-interface" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d8dc32e0095a7eeccebd0e3f09e9509365ecb3fc6ac4d6f5f14a3f6392942d1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[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.1", +] + +[[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.4", +] + +[[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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + +[[package]] +name = "windows-targets" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +dependencies = [ + "windows_aarch64_gnullvm 0.52.4", + "windows_aarch64_msvc 0.52.4", + "windows_i686_gnu 0.52.4", + "windows_i686_msvc 0.52.4", + "windows_x86_64_gnu 0.52.4", + "windows_x86_64_gnullvm 0.52.4", + "windows_x86_64_msvc 0.52.4", +] + +[[package]] +name = "windows-version" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75aa004c988e080ad34aff5739c39d0312f4684699d6d71fc8a198d057b8b9b4" +dependencies = [ + "windows-targets 0.52.4", +] + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" + +[[package]] +name = "winnow" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bd122eb777186e60c3fdf765a58ac76e41c582f1f535fbf3314434c6b58f3f7" +dependencies = [ + "memchr", +] + +[[package]] +name = "wry" +version = "0.35.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3016c47c9b6f7029a9da7cd48af8352327226bba0e955f3c92e2966651365a9" +dependencies = [ + "base64", + "block", + "cfg_aliases", + "cocoa", + "core-graphics", + "crossbeam-channel", + "dunce", + "gdkx11", + "gtk", + "html5ever", + "http 0.2.9", + "javascriptcore-rs", + "jni 0.21.1", + "kuchikiki", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "objc", + "objc_id", + "once_cell", + "raw-window-handle 0.5.2", + "serde", + "serde_json", + "sha2", + "soup3", + "tao-macros", + "thiserror", + "url", + "webkit2gtk", + "webkit2gtk-sys", + "webview2-com", + "windows", + "windows-implement", + "windows-version", + "x11-dl", +] + +[[package]] +name = "wry" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b717040ba9771fd88eb428c6ea6b555f8e734ff8534f02c13e8f10d97f5935e" +dependencies = [ + "base64", + "block", + "cfg_aliases", + "cocoa", + "core-graphics", + "crossbeam-channel", + "dunce", + "gdkx11", + "gtk", + "html5ever", + "http 0.2.9", + "javascriptcore-rs", + "jni 0.21.1", + "kuchikiki", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "objc", + "objc_id", + "once_cell", + "percent-encoding", + "raw-window-handle 0.6.0", + "serde", + "serde_json", + "sha2", + "soup3", + "tao-macros", + "thiserror", + "webkit2gtk", + "webkit2gtk-sys", + "webview2-com", + "windows", + "windows-implement", + "windows-version", + "x11-dl", +] + +[[package]] +name = "x11" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "xxhash-rust" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927da81e25be1e1a2901d59b81b37dd2efd1fc9c9345a55007f09bf5a2d3ee03" + +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] diff --git a/examples/mobile_demo/Cargo.toml b/examples/mobile_demo/Cargo.toml new file mode 100644 index 0000000000..4d4155750b --- /dev/null +++ b/examples/mobile_demo/Cargo.toml @@ -0,0 +1,51 @@ +[package] +name = "mobile-demo" +version = "0.1.0" +authors = ["Jonathan Kelley "] +edition = "2021" + +[lib] +crate-type = ["staticlib", "cdylib", "rlib"] + +[[bin]] +name = "mobile-demo-desktop" +path = "gen/bin/desktop.rs" + +[package.metadata.cargo-android] +app-activity-name = "com.example.mobile_demo.MainActivity" +app-dependencies = [ + "androidx.webkit:webkit:1.6.1", + "androidx.appcompat:appcompat:1.6.1", + "com.google.android.material:material:1.8.0", +] +project-dependencies = ["org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21"] +app-plugins = ["org.jetbrains.kotlin.android"] +app-permissions = ["android.permission.INTERNET"] +app-theme-parent = "Theme.MaterialComponents.DayNight.DarkActionBar" +vulkan-validation = false + +[package.metadata.cargo-android.env-vars] +WRY_ANDROID_PACKAGE = "com.example.mobile_demo" +WRY_ANDROID_LIBRARY = "mobile_demo" +WRY_ANDROID_KOTLIN_FILES_OUT_DIR = "/app/src/main/kotlin/com/example/mobile_demo" + +[package.metadata.cargo-apple.ios] +frameworks = ["WebKit"] + +[dependencies] +anyhow = "1.0.56" +log = "0.4.11" +wry = "0.35.0" +dioxus = { path = "../../packages/dioxus", features = ["mobile"]} + + +[target.'cfg(target_os = "android")'.dependencies] +android_logger = "0.9.0" +jni = "0.19.0" +paste = "1.0" + +[target.'cfg(not(target_os = "android"))'.dependencies] +env_logger = "0.9.0" + +[target.'cfg(target_os = "ios")'.dependencies] +core-foundation = "0.9.3" diff --git a/examples/mobile_demo/README.md b/examples/mobile_demo/README.md new file mode 100644 index 0000000000..4d5ea46ed0 --- /dev/null +++ b/examples/mobile_demo/README.md @@ -0,0 +1,11 @@ +# Dioxus Mobile demo + +## How this project was generated + +Right now, Dioxus supports mobile targets including iOS and Android. However, our tooling is not mature enough to include the build commands directly. + +This project was generated using [cargo-mobile2](https://github.com/tauri-apps/cargo-mobile2). We have yet to integrate this generation into the Dioxus-CLI. The open issue for this is [#1157](https://github.com/DioxusLabs/dioxus/issues/1157). + +## Running this project + +Because the tooling and ecosystem is still young, Dioxus mobile can be difficult to setup and run. We have [detailed guides](https://dioxuslabs.com/learn/0.5/getting_started) for creating, building, and running on both iOS and Android. diff --git a/examples/mobile_demo/mobile.toml b/examples/mobile_demo/mobile.toml new file mode 100644 index 0000000000..3b87772508 --- /dev/null +++ b/examples/mobile_demo/mobile.toml @@ -0,0 +1,8 @@ +[app] +name = "mobile-demo" +stylized-name = "Mobile Demo" +domain = "example.com" +template-pack = "wry" + +[apple] +development-team = "34U4FG9TJ8" diff --git a/examples/mobile_demo/src/index.html b/examples/mobile_demo/src/index.html new file mode 100644 index 0000000000..b0e6290e12 --- /dev/null +++ b/examples/mobile_demo/src/index.html @@ -0,0 +1,12 @@ + + + + Dioxus app + + + + +
      + + + diff --git a/examples/mobile_demo/src/lib.rs b/examples/mobile_demo/src/lib.rs new file mode 100644 index 0000000000..6fc142d9a3 --- /dev/null +++ b/examples/mobile_demo/src/lib.rs @@ -0,0 +1,90 @@ +use anyhow::Result; +use dioxus::mobile::Config; +use dioxus::prelude::*; + +#[cfg(target_os = "android")] +use dioxus::mobile::wry::android_binding; + +#[cfg(target_os = "android")] +fn init_logging() { + android_logger::init_once( + android_logger::Config::default() + .with_min_level(log::Level::Trace) + .with_tag("mobile-demo"), + ); +} + +#[cfg(not(target_os = "android"))] +fn init_logging() { + env_logger::init(); +} + +#[cfg(any(target_os = "android", target_os = "ios"))] +fn stop_unwind T, T>(f: F) -> T { + match std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)) { + Ok(t) => t, + Err(err) => { + eprintln!("attempt to unwind out of `rust` with err: {:?}", err); + std::process::abort() + } + } +} + +#[cfg(any(target_os = "android", target_os = "ios"))] +fn _start_app() { + stop_unwind(|| main().unwrap()); +} + +#[no_mangle] +#[inline(never)] +#[cfg(any(target_os = "android", target_os = "ios"))] +pub extern "C" fn start_app() { + #[cfg(target_os = "android")] + android_binding!(com_example, mobile_demo, _start_app); + #[cfg(target_os = "ios")] + _start_app() +} + +pub fn main() -> Result<()> { + init_logging(); + + // Right now we're going through dioxus-desktop but we'd like to go through dioxus-mobile + // That will seed the index.html with some fixes that prevent the page from scrolling/zooming etc + LaunchBuilder::mobile() + .with_cfg( + // Note that we have to disable the viewport goofiness of the browser. + // Dioxus_mobile should do this for us + Config::default().with_custom_index(include_str!("index.html").to_string()), + ) + .launch(app); + + Ok(()) +} + +fn app() -> Element { + let mut items = use_signal(|| vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); + + log::debug!("Hello from the app"); + + rsx! { + div { + h1 { "Hello, Mobile" } + div { + margin_left: "auto", + margin_right: "auto", + width: "200px", + padding: "10px", + border: "1px solid black", + button { + onclick: move |_| { + items.push(items.len()); + }, + "Add item" + } + for item in items.iter() { + div { "- {item}" } + } + } + } + } +} diff --git a/examples/multiwindow.rs b/examples/multiwindow.rs index d89a78ee6d..6858f65896 100644 --- a/examples/multiwindow.rs +++ b/examples/multiwindow.rs @@ -7,7 +7,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch_desktop(app); } fn app() -> Element { diff --git a/examples/nested_listeners.rs b/examples/nested_listeners.rs index c2316aed37..33cf3d4043 100644 --- a/examples/nested_listeners.rs +++ b/examples/nested_listeners.rs @@ -7,7 +7,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/openid_connect_demo/.cargo/config.toml b/examples/openid_connect_demo/.cargo/config.toml new file mode 100644 index 0000000000..fcb494816a --- /dev/null +++ b/examples/openid_connect_demo/.cargo/config.toml @@ -0,0 +1,5 @@ +[env] +DIOXUS_FRONT_ISSUER_URL = "TODO" +DIOXUS_FRONT_CLIENT_ID = "TODO" +DIOXUS_FRONT_CLIENT_SECRET = "TODO" +DIOXUS_FRONT_URL = "http://localhost:8080" diff --git a/examples/openid_connect_demo/.gitignore b/examples/openid_connect_demo/.gitignore new file mode 100644 index 0000000000..919c8ee550 --- /dev/null +++ b/examples/openid_connect_demo/.gitignore @@ -0,0 +1,3 @@ +/target +/dist +.env diff --git a/examples/openid_connect_demo/Cargo.lock b/examples/openid_connect_demo/Cargo.lock new file mode 100644 index 0000000000..b7d513c11e --- /dev/null +++ b/examples/openid_connect_demo/Cargo.lock @@ -0,0 +1,6372 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + +[[package]] +name = "anymap" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33954243bd79057c2de7338850b85983a44588021f8a5fee574a8888c6de4344" + +[[package]] +name = "anymap2" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c" + +[[package]] +name = "ashpd" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd884d7c72877a94102c3715f3b1cd09ff4fac28221add3e57cfbe25c236d093" +dependencies = [ + "async-fs", + "async-net", + "enumflags2", + "futures-channel", + "futures-util", + "rand 0.8.5", + "serde", + "serde_repr", + "url", + "zbus", +] + +[[package]] +name = "askama_escape" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341" + +[[package]] +name = "async-broadcast" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cd0e2e25ea8e5f7e9df04578dc6cf5c83577fd09b1a46aaf5c85e1c33f2a7e" +dependencies = [ + "event-listener", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-channel" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8828ec6e544c02b0d6691d21ed9f9218d0384a82542855073c2a3f58304aaf0" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "slab", +] + +[[package]] +name = "async-fs" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" +dependencies = [ + "async-lock", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-io" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d6baa8f0178795da0e71bc42c9e5d13261aac7ee549853162e66a241ba17964" +dependencies = [ + "async-lock", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "async-lock" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-net" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" +dependencies = [ + "async-io", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-process" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7eda79bbd84e29c2b308d1dc099d7de8dcc7035e48f4bf5dc4a531a44ff5e2a" +dependencies = [ + "async-channel", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "async-recursion" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "async-signal" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "329972aa325176e89114919f2a80fdae4f4c040f66a370b1a1159c6c0f94e7aa" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix", + "signal-hook-registry", + "slab", + "windows-sys 0.52.0", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "async-trait" +version = "0.1.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "atk" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4af014b17dd80e8af9fa689b2d4a211ddba6eb583c1622f35d0cb543f6b17e4" +dependencies = [ + "atk-sys", + "glib", + "libc", +] + +[[package]] +name = "atk-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "251e0b7d90e33e0ba930891a505a9a35ece37b2dd37a14f3ffc306c13b980009" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "atomic-polyfill" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" +dependencies = [ + "critical-section", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "axum" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" +dependencies = [ + "async-trait", + "axum-core", + "axum-macros", + "base64 0.21.7", + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", + "hyper 1.3.1", + "hyper-util", + "itoa 1.0.11", + "matchit", + "memchr", + "mime", + "multer", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sha1", + "sync_wrapper 1.0.1", + "tokio", + "tokio-tungstenite", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper 0.1.2", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00c055ee2d014ae5981ce1016374e8213682aa14d9bf40e48ab48b5f3ef20eaa" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "backtrace" +version = "0.3.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17c6a35df3749d2e8bb1b7b21a976d82b15548788d2735b9d82f329268f71a11" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +dependencies = [ + "serde", +] + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "blocking" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + +[[package]] +name = "bstr" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" + +[[package]] +name = "cairo-rs" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" +dependencies = [ + "bitflags 2.5.0", + "cairo-sys-rs", + "glib", + "libc", + "once_cell", + "thiserror", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "camino" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "cc" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfb" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" +dependencies = [ + "byteorder", + "fnv", + "uuid", +] + +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-targets 0.52.5", +] + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "cobs" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" + +[[package]] +name = "cocoa" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" +dependencies = [ + "bitflags 1.3.2", + "block", + "cocoa-foundation", + "core-foundation", + "core-graphics", + "foreign-types", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" +dependencies = [ + "bitflags 1.3.2", + "block", + "core-foundation", + "core-graphics-types", + "libc", + "objc", +] + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "const_format" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a214c7af3d04997541b18d432afaff4c455e79e2029079647e72fc2bd27673" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[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-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "core-graphics" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "critical-section" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" + +[[package]] +name = "crossbeam-channel" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +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.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array 0.14.7", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array 0.14.7", + "typenum", +] + +[[package]] +name = "cssparser" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa 0.4.8", + "matches", + "phf 0.8.0", + "proc-macro2", + "quote", + "smallvec", + "syn 1.0.109", +] + +[[package]] +name = "cssparser-macros" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" +dependencies = [ + "quote", + "syn 2.0.66", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "platforms", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "darling" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.66", +] + +[[package]] +name = "darling_macro" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "data-encoding" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" + +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", + "serde", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case 0.4.0", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "dioxus" +version = "0.5.2" +dependencies = [ + "dioxus-config-macro", + "dioxus-core", + "dioxus-core-macro", + "dioxus-desktop", + "dioxus-fullstack", + "dioxus-hooks", + "dioxus-hot-reload", + "dioxus-html", + "dioxus-liveview", + "dioxus-router", + "dioxus-signals", + "dioxus-ssr", + "dioxus-static-site-generation", + "dioxus-web", + "serde", +] + +[[package]] +name = "dioxus-cli-config" +version = "0.5.2" +dependencies = [ + "once_cell", + "serde", + "serde_json", + "tracing", +] + +[[package]] +name = "dioxus-config-macro" +version = "0.5.2" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "dioxus-core" +version = "0.5.2" +dependencies = [ + "futures-channel", + "futures-util", + "generational-box", + "longest-increasing-subsequence", + "rustc-hash", + "serde", + "slab", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "dioxus-core-macro" +version = "0.5.2" +dependencies = [ + "convert_case 0.6.0", + "dioxus-rsx", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "dioxus-debug-cell" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ea539174bb236e0e7dc9c12b19b88eae3cb574dedbd0252a2d43ea7e6de13e2" + +[[package]] +name = "dioxus-desktop" +version = "0.5.2" +dependencies = [ + "async-trait", + "cocoa", + "core-foundation", + "dioxus-cli-config", + "dioxus-core", + "dioxus-hooks", + "dioxus-hot-reload", + "dioxus-html", + "dioxus-interpreter-js", + "dunce", + "futures-channel", + "futures-util", + "generational-box", + "global-hotkey", + "infer", + "muda", + "objc", + "objc_id", + "rfd", + "rustc-hash", + "serde", + "serde_json", + "signal-hook", + "slab", + "tao", + "thiserror", + "tokio", + "tracing", + "urlencoding", + "webbrowser", + "wry", +] + +[[package]] +name = "dioxus-fullstack" +version = "0.5.2" +dependencies = [ + "anymap", + "async-trait", + "axum", + "base64 0.21.7", + "bytes", + "ciborium", + "dioxus-cli-config", + "dioxus-desktop", + "dioxus-hot-reload", + "dioxus-lib", + "dioxus-ssr", + "dioxus-web", + "dioxus_server_macro", + "futures-util", + "http 1.1.0", + "hyper 1.3.1", + "once_cell", + "pin-project", + "serde", + "serde_json", + "server_fn", + "thiserror", + "tokio", + "tokio-stream", + "tokio-util", + "tower", + "tower-http", + "tower-layer", + "tracing", + "tracing-futures", + "web-sys", +] + +[[package]] +name = "dioxus-hooks" +version = "0.5.2" +dependencies = [ + "dioxus-core", + "dioxus-debug-cell", + "dioxus-signals", + "futures-channel", + "futures-util", + "generational-box", + "slab", + "thiserror", + "tracing", +] + +[[package]] +name = "dioxus-hot-reload" +version = "0.5.2" +dependencies = [ + "axum", + "chrono", + "dioxus-core", + "dioxus-html", + "dioxus-rsx", + "execute", + "futures-util", + "ignore", + "interprocess-docfix", + "notify", + "once_cell", + "serde", + "serde_json", + "tokio", + "tokio-stream", + "tracing", +] + +[[package]] +name = "dioxus-html" +version = "0.5.2" +dependencies = [ + "async-trait", + "dioxus-core", + "dioxus-html-internal-macro", + "dioxus-rsx", + "enumset", + "euclid", + "futures-channel", + "generational-box", + "keyboard-types", + "serde", + "serde-value", + "serde_json", + "serde_repr", + "tokio", + "tracing", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "dioxus-html-internal-macro" +version = "0.5.2" +dependencies = [ + "convert_case 0.6.0", + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "dioxus-interpreter-js" +version = "0.5.2" +dependencies = [ + "dioxus-core", + "dioxus-html", + "js-sys", + "md5", + "sledgehammer_bindgen", + "sledgehammer_utils", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "dioxus-lib" +version = "0.5.2" +dependencies = [ + "dioxus-config-macro", + "dioxus-core", + "dioxus-core-macro", + "dioxus-hooks", + "dioxus-html", + "dioxus-rsx", + "dioxus-signals", +] + +[[package]] +name = "dioxus-liveview" +version = "0.5.2" +dependencies = [ + "axum", + "dioxus-cli-config", + "dioxus-core", + "dioxus-hot-reload", + "dioxus-html", + "dioxus-interpreter-js", + "futures-channel", + "futures-util", + "generational-box", + "rustc-hash", + "serde", + "serde_json", + "slab", + "thiserror", + "tokio", + "tokio-stream", + "tokio-util", + "tracing", +] + +[[package]] +name = "dioxus-logger" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe09dc9773dc1f1bb0d38529203d6555d08f67aadca5cf955ac3d1a9e69880" +dependencies = [ + "console_error_panic_hook", + "tracing", + "tracing-subscriber", + "tracing-wasm", +] + +[[package]] +name = "dioxus-router" +version = "0.5.2" +dependencies = [ + "dioxus-cli-config", + "dioxus-fullstack", + "dioxus-lib", + "dioxus-router-macro", + "dioxus-ssr", + "gloo", + "gloo-utils 0.1.7", + "http 1.1.0", + "js-sys", + "tokio", + "tracing", + "url", + "urlencoding", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "dioxus-router-macro" +version = "0.5.2" +dependencies = [ + "proc-macro2", + "quote", + "slab", + "syn 2.0.66", +] + +[[package]] +name = "dioxus-rsx" +version = "0.5.2" +dependencies = [ + "dioxus-core", + "internment", + "krates", + "proc-macro2", + "quote", + "syn 2.0.66", + "tracing", +] + +[[package]] +name = "dioxus-sdk" +version = "0.5.0" +source = "git+https://github.com/Dioxuslabs/sdk#d49812fbc9d4506bd3b1ec994f45ef4447f34c79" +dependencies = [ + "cfg-if", + "dioxus", + "dioxus-signals", + "directories", + "futures-util", + "js-sys", + "once_cell", + "postcard", + "rustc-hash", + "serde", + "tokio", + "tracing", + "uuid", + "wasm-bindgen", + "web-sys", + "yazi", +] + +[[package]] +name = "dioxus-signals" +version = "0.5.2" +dependencies = [ + "dioxus-core", + "futures-channel", + "futures-util", + "generational-box", + "once_cell", + "parking_lot", + "rustc-hash", + "serde", + "tracing", +] + +[[package]] +name = "dioxus-ssr" +version = "0.5.2" +dependencies = [ + "askama_escape", + "async-trait", + "chrono", + "dioxus-cli-config", + "dioxus-core", + "dioxus-html", + "generational-box", + "http 1.1.0", + "lru", + "rustc-hash", + "serde_json", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "dioxus-static-site-generation" +version = "0.5.2" +dependencies = [ + "axum", + "dioxus-cli-config", + "dioxus-fullstack", + "dioxus-hot-reload", + "dioxus-lib", + "dioxus-router", + "dioxus-ssr", + "dioxus-web", + "http 1.1.0", + "tokio", + "tower", + "tower-http", + "tracing", +] + +[[package]] +name = "dioxus-web" +version = "0.5.2" +dependencies = [ + "async-trait", + "console_error_panic_hook", + "dioxus-core", + "dioxus-html", + "dioxus-interpreter-js", + "futures-channel", + "futures-util", + "generational-box", + "js-sys", + "rustc-hash", + "serde", + "serde-wasm-bindgen", + "serde_json", + "tracing", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "dioxus_server_macro" +version = "0.5.2" +dependencies = [ + "convert_case 0.6.0", + "proc-macro2", + "quote", + "server_fn_macro", + "syn 2.0.66", +] + +[[package]] +name = "directories" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + +[[package]] +name = "dlopen2" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1297103d2bbaea85724fcee6294c2d50b1081f9ad47d0f6f6f61eda65315a6" +dependencies = [ + "dlopen2_derive", + "libc", + "once_cell", + "winapi", +] + +[[package]] +name = "dlopen2_derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "dtoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" + +[[package]] +name = "dtoa-short" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbaceec3c6e4211c79e7b1800fb9680527106beb2f9c51904a3210c03a448c74" +dependencies = [ + "dtoa", +] + +[[package]] +name = "dunce" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" + +[[package]] +name = "dyn-clone" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +dependencies = [ + "curve25519-dalek", + "ed25519", + "serde", + "sha2", + "subtle", + "zeroize", +] + +[[package]] +name = "either" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array 0.14.7", + "group", + "hkdf", + "pem-rfc7468", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" + +[[package]] +name = "encoding_rs" +version = "0.8.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +dependencies = [ + "cfg-if", +] + +[[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.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3278c9d5fb675e0a51dabcf4c0d355f692b064171535ba72361be1528a9d8e8d" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c785274071b1b420972453b306eeca06acf4633829db4223b58a2a8c5953bc4" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "enumset" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "226c0da7462c13fb57e5cc9e0dc8f0635e7d27f276a3a7fd30054647f669007d" +dependencies = [ + "enumset_derive", +] + +[[package]] +name = "enumset_derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08b6c6ab82d70f08844964ba10c7babb716de2ecaeab9be5717918a5177d3af" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[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.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "euclid" +version = "0.22.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0f0eb73b934648cd7a4a61f1b15391cd95dab0b4da6e2e66c2a072c144b4a20" +dependencies = [ + "num-traits", + "serde", +] + +[[package]] +name = "event-listener" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +dependencies = [ + "event-listener", + "pin-project-lite", +] + +[[package]] +name = "execute" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a82608ee96ce76aeab659e9b8d3c2b787bffd223199af88c674923d861ada10" +dependencies = [ + "execute-command-macro", + "execute-command-tokens", + "generic-array 1.0.0", +] + +[[package]] +name = "execute-command-macro" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90dec53d547564e911dc4ff3ecb726a64cf41a6fa01a2370ebc0d95175dd08bd" +dependencies = [ + "execute-command-macro-impl", +] + +[[package]] +name = "execute-command-macro-impl" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce8cd46a041ad005ab9c71263f9a0ff5b529eac0fe4cc9b4a20f4f0765d8cf4b" +dependencies = [ + "execute-command-tokens", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "execute-command-tokens" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69dc321eb6be977f44674620ca3aa21703cb20ffbe560e1ae97da08401ffbcad" + +[[package]] +name = "fastrand" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" + +[[package]] +name = "fdeflate" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "field-offset" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" +dependencies = [ + "memoffset", + "rustc_version", +] + +[[package]] +name = "filetime" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.4.1", + "windows-sys 0.52.0", +] + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "flate2" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[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 = "fsevent-sys" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" +dependencies = [ + "libc", +] + +[[package]] +name = "futf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" +dependencies = [ + "mac", + "new_debug_unreachable", +] + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-lite" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "gdk" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5ba081bdef3b75ebcdbfc953699ed2d7417d6bd853347a42a37d76406a33646" +dependencies = [ + "cairo-rs", + "gdk-pixbuf", + "gdk-sys", + "gio", + "glib", + "libc", + "pango", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" +dependencies = [ + "gdk-pixbuf-sys", + "gio", + "glib", + "libc", + "once_cell", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gdk-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31ff856cb3386dae1703a920f803abafcc580e9b5f711ca62ed1620c25b51ff2" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkwayland-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a90fbf5c033c65d93792192a49a8efb5bb1e640c419682a58bb96f5ae77f3d4a" +dependencies = [ + "gdk-sys", + "glib-sys", + "gobject-sys", + "libc", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkx11" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2ea8a4909d530f79921290389cbd7c34cb9d623bfe970eaae65ca5f9cd9cce" +dependencies = [ + "gdk", + "gdkx11-sys", + "gio", + "glib", + "libc", + "x11", +] + +[[package]] +name = "gdkx11-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fee8f00f4ee46cad2939b8990f5c70c94ff882c3028f3cc5abf950fa4ab53043" +dependencies = [ + "gdk-sys", + "glib-sys", + "libc", + "system-deps", + "x11", +] + +[[package]] +name = "generational-box" +version = "0.5.2" +dependencies = [ + "parking_lot", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "generic-array" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe739944a5406424e080edccb6add95685130b9f160d5407c639c7df0c5836b0" +dependencies = [ + "typenum", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "gimli" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" + +[[package]] +name = "gio" +version = "0.18.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys", + "glib", + "libc", + "once_cell", + "pin-project-lite", + "smallvec", + "thiserror", +] + +[[package]] +name = "gio-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", + "winapi", +] + +[[package]] +name = "glib" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" +dependencies = [ + "bitflags 2.5.0", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "memchr", + "once_cell", + "smallvec", + "thiserror", +] + +[[package]] +name = "glib-macros" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" +dependencies = [ + "heck 0.4.1", + "proc-macro-crate 2.0.2", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "glib-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" +dependencies = [ + "libc", + "system-deps", +] + +[[package]] +name = "global-hotkey" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89cb13e8c52c87e28a46eae3e5e65b8f0cd465c4c9e67b13d56c70412e792bc3" +dependencies = [ + "bitflags 2.5.0", + "cocoa", + "crossbeam-channel", + "keyboard-types", + "objc", + "once_cell", + "thiserror", + "windows-sys 0.52.0", + "x11-dl", +] + +[[package]] +name = "globset" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "gloo" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28999cda5ef6916ffd33fb4a7b87e1de633c47c0dc6d97905fee1cdaa142b94d" +dependencies = [ + "gloo-console", + "gloo-dialogs", + "gloo-events", + "gloo-file", + "gloo-history", + "gloo-net 0.3.1", + "gloo-render", + "gloo-storage", + "gloo-timers", + "gloo-utils 0.1.7", + "gloo-worker", +] + +[[package]] +name = "gloo-console" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b7ce3c05debe147233596904981848862b068862e9ec3e34be446077190d3f" +dependencies = [ + "gloo-utils 0.1.7", + "js-sys", + "serde", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-dialogs" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67062364ac72d27f08445a46cab428188e2e224ec9e37efdba48ae8c289002e6" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-events" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b107f8abed8105e4182de63845afcc7b69c098b7852a813ea7462a320992fc" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-file" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d5564e570a38b43d78bdc063374a0c3098c4f0d64005b12f9bbe87e869b6d7" +dependencies = [ + "gloo-events", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-history" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85725d90bf0ed47063b3930ef28e863658a7905989e9929a8708aab74a1d5e7f" +dependencies = [ + "gloo-events", + "gloo-utils 0.1.7", + "serde", + "serde-wasm-bindgen", + "serde_urlencoded", + "thiserror", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-net" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a66b4e3c7d9ed8d315fd6b97c8b1f74a7c6ecbbc2320e65ae7ed38b7068cc620" +dependencies = [ + "futures-channel", + "futures-core", + "futures-sink", + "gloo-utils 0.1.7", + "http 0.2.12", + "js-sys", + "pin-project", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-net" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43aaa242d1239a8822c15c645f02166398da4f8b5c4bae795c1f5b44e9eee173" +dependencies = [ + "futures-channel", + "futures-core", + "futures-sink", + "gloo-utils 0.2.0", + "http 0.2.12", + "js-sys", + "pin-project", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-render" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd9306aef67cfd4449823aadcd14e3958e0800aa2183955a309112a84ec7764" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-storage" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6ab60bf5dbfd6f0ed1f7843da31b41010515c745735c970e821945ca91e480" +dependencies = [ + "gloo-utils 0.1.7", + "js-sys", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-timers" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "gloo-utils" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037fcb07216cb3a30f7292bd0176b050b7b9a052ba830ef7d5d65f6dc64ba58e" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-utils" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-worker" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13471584da78061a28306d1359dd0178d8d6fc1c7c80e5e35d27260346e0516a" +dependencies = [ + "anymap2", + "bincode", + "gloo-console", + "gloo-utils 0.1.7", + "js-sys", + "serde", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gobject-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "gtk" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93c4f5e0e20b60e10631a5f06da7fe3dda744b05ad0ea71fee2f47adf865890c" +dependencies = [ + "atk", + "cairo-rs", + "field-offset", + "futures-channel", + "gdk", + "gdk-pixbuf", + "gio", + "glib", + "gtk-sys", + "gtk3-macros", + "libc", + "pango", + "pkg-config", +] + +[[package]] +name = "gtk-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771437bf1de2c1c0b496c11505bdf748e26066bbe942dfc8f614c9460f6d7722" +dependencies = [ + "atk-sys", + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "system-deps", +] + +[[package]] +name = "gtk3-macros" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6063efb63db582968fb7df72e1ae68aa6360dcfb0a75143f34fc7d616bad75e" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap 2.2.6", + "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", + "crunchy", +] + +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "heapless" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" +dependencies = [ + "atomic-polyfill", + "hash32", + "rustc_version", + "serde", + "spin 0.9.8", + "stable_deref_trait", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "html5ever" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" +dependencies = [ + "log", + "mac", + "markup5ever", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa 1.0.11", +] + +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa 1.0.11", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + +[[package]] +name = "http-body" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +dependencies = [ + "bytes", + "http 1.1.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" +dependencies = [ + "bytes", + "futures-core", + "http 1.1.0", + "http-body 1.0.0", + "pin-project-lite", +] + +[[package]] +name = "http-range-header" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08a397c49fec283e3d6211adbe480be95aae5f304cfb923e9970e08956d5168a" + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "0.14.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http 0.2.12", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa 1.0.11", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "httparse", + "httpdate", + "itoa 1.0.11", + "pin-project-lite", + "smallvec", + "tokio", +] + +[[package]] +name = "hyper-rustls" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +dependencies = [ + "futures-util", + "http 0.2.12", + "hyper 0.14.29", + "rustls", + "tokio", + "tokio-rustls", +] + +[[package]] +name = "hyper-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b875924a60b96e5d7b9ae7b066540b1dd1cbd90d1828f54c92e02a283351c56" +dependencies = [ + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "hyper 1.3.1", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +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 = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "ignore" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata", + "same-file", + "walkdir", + "winapi-util", +] + +[[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.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown 0.14.5", + "serde", +] + +[[package]] +name = "infer" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6c16b11a665b26aeeb9b1d7f954cdeb034be38dd00adab4f2ae921a8fee804" +dependencies = [ + "cfb", +] + +[[package]] +name = "inotify" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" +dependencies = [ + "bitflags 1.3.2", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "internment" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04e8e537b529b8674e97e9fb82c10ff168a290ac3867a0295f112061ffbca1ef" +dependencies = [ + "hashbrown 0.14.5", + "parking_lot", +] + +[[package]] +name = "interprocess-docfix" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b84ee245c606aeb0841649a9288e3eae8c61b853a8cd5c0e14450e96d53d28f" +dependencies = [ + "blocking", + "cfg-if", + "futures-core", + "futures-io", + "intmap", + "libc", + "once_cell", + "rustc_version", + "spinning", + "thiserror", + "to_method", + "winapi", +] + +[[package]] +name = "intmap" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae52f28f45ac2bc96edb7714de995cffc174a395fb0abf5bff453587c980d7b9" + +[[package]] +name = "inventory" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f958d3d68f4167080a18141e10381e7634563984a537f2a49a30fd8e53ac5767" + +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "javascriptcore-rs" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca5671e9ffce8ffba57afc24070e906da7fc4b1ba66f2cabebf61bf2ea257fcc" +dependencies = [ + "bitflags 1.3.2", + "glib", + "javascriptcore-rs-sys", +] + +[[package]] +name = "javascriptcore-rs-sys" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1be78d14ffa4b75b66df31840478fef72b51f8c2465d4ca7c194da9f7a5124" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror", + "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 = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "keyboard-types" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" +dependencies = [ + "bitflags 2.5.0", + "serde", + "unicode-segmentation", +] + +[[package]] +name = "kqueue" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +dependencies = [ + "bitflags 1.3.2", + "libc", +] + +[[package]] +name = "krates" +version = "0.16.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcb3baf2360eb25ad31f0ada3add63927ada6db457791979b82ac199f835cb9" +dependencies = [ + "cargo-platform", + "cargo_metadata", + "cfg-expr", + "petgraph", + "semver", +] + +[[package]] +name = "kuchikiki" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e4755b7b995046f510a7520c42b2fed58b77bd94d5a87a8eb43d2fd126da8" +dependencies = [ + "cssparser", + "html5ever", + "indexmap 1.9.3", + "matches", + "selectors", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin 0.5.2", +] + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.5.0", + "libc", +] + +[[package]] +name = "libxdo" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00333b8756a3d28e78def82067a377de7fa61b24909000aeaa2b446a948d14db" +dependencies = [ + "libxdo-sys", +] + +[[package]] +name = "libxdo-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db23b9e7e2b7831bbd8aac0bbeeeb7b68cbebc162b227e7052e8e55829a09212" +dependencies = [ + "libc", + "x11", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[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 = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "longest-increasing-subsequence" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3bd0dd2cd90571056fdb71f6275fada10131182f84899f4b2a916e565d81d86" + +[[package]] +name = "lru" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" +dependencies = [ + "hashbrown 0.14.5", +] + +[[package]] +name = "mac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "markup5ever" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" +dependencies = [ + "log", + "phf 0.10.1", + "phf_codegen 0.10.0", + "string_cache", + "string_cache_codegen", + "tendril", +] + +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + +[[package]] +name = "md5" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" + +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[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 = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "miniz_oxide" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" +dependencies = [ + "adler", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.48.0", +] + +[[package]] +name = "muda" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c47e7625990fc1af2226ea4f34fb2412b03c12639fcb91868581eb3a6893453" +dependencies = [ + "cocoa", + "crossbeam-channel", + "gtk", + "keyboard-types", + "libxdo", + "objc", + "once_cell", + "png", + "thiserror", + "windows-sys 0.52.0", +] + +[[package]] +name = "multer" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b" +dependencies = [ + "bytes", + "encoding_rs", + "futures-util", + "http 1.1.0", + "httparse", + "memchr", + "mime", + "spin 0.9.8", + "version_check", +] + +[[package]] +name = "ndk" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" +dependencies = [ + "bitflags 1.3.2", + "jni-sys", + "ndk-sys", + "num_enum", + "raw-window-handle 0.5.2", + "thiserror", +] + +[[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.4.1+23.1.7779620" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cf2aae958bd232cac5069850591667ad422d263686d75b52a065f9badeee5a3" +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.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags 2.5.0", + "cfg-if", + "libc", + "memoffset", +] + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + +[[package]] +name = "notify" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "729f63e1ca555a43fe3efa4f3efdf4801c479da85b432242a7b726f353c88486" +dependencies = [ + "bitflags 1.3.2", + "crossbeam-channel", + "filetime", + "fsevent-sys", + "inotify", + "kqueue", + "libc", + "mio", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand 0.8.5", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[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-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "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", + "libm", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "oauth2" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c38841cdd844847e3e7c8d29cef9dcfed8877f8f56f9071f77843ecf3baf937f" +dependencies = [ + "base64 0.13.1", + "chrono", + "getrandom 0.2.15", + "http 0.2.12", + "rand 0.8.5", + "reqwest", + "serde", + "serde_json", + "serde_path_to_error", + "sha2", + "thiserror", + "url", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", + "objc_exception", +] + +[[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_exception" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" +dependencies = [ + "cc", +] + +[[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.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "openid_auth_demo" +version = "0.1.0" +dependencies = [ + "anyhow", + "console_error_panic_hook", + "dioxus", + "dioxus-logger", + "dioxus-sdk", + "form_urlencoded", + "log", + "openidconnect", + "serde", + "uuid", +] + +[[package]] +name = "openidconnect" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f47e80a9cfae4462dd29c41e987edd228971d6565553fbc14b8a11e666d91590" +dependencies = [ + "base64 0.13.1", + "chrono", + "dyn-clone", + "ed25519-dalek", + "hmac", + "http 0.2.12", + "itertools", + "log", + "oauth2", + "p256", + "p384", + "rand 0.8.5", + "rsa", + "serde", + "serde-value", + "serde_derive", + "serde_json", + "serde_path_to_error", + "serde_plain", + "serde_with", + "sha2", + "subtle", + "thiserror", + "url", +] + +[[package]] +name = "ordered-float" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +dependencies = [ + "num-traits", +] + +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "p384" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "pango" +version = "0.18.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" +dependencies = [ + "gio", + "glib", + "libc", + "once_cell", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "parking" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" + +[[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", + "libc", + "redox_syscall 0.5.1", + "smallvec", + "windows-targets 0.52.5", +] + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "petgraph" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset", + "indexmap 2.2.6", +] + +[[package]] +name = "phf" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" +dependencies = [ + "phf_macros", + "phf_shared 0.8.0", + "proc-macro-hack", +] + +[[package]] +name = "phf" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" +dependencies = [ + "phf_shared 0.10.0", +] + +[[package]] +name = "phf_codegen" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" +dependencies = [ + "phf_generator 0.8.0", + "phf_shared 0.8.0", +] + +[[package]] +name = "phf_codegen" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", +] + +[[package]] +name = "phf_generator" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" +dependencies = [ + "phf_shared 0.8.0", + "rand 0.7.3", +] + +[[package]] +name = "phf_generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +dependencies = [ + "phf_shared 0.10.0", + "rand 0.8.5", +] + +[[package]] +name = "phf_macros" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" +dependencies = [ + "phf_generator 0.8.0", + "phf_shared 0.8.0", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "phf_shared" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" +dependencies = [ + "siphasher", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[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.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1d5c74c9876f070d3e8fd503d748c7d974c3e48da8f41350fa5222ef9b4391" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "platforms" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7" + +[[package]] +name = "png" +version = "0.17.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "polling" +version = "3.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6a007746f34ed64099e88783b0ae369eaa3da6392868ba262e2af9b8fbaea1" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "pollster" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2" + +[[package]] +name = "postcard" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a55c51ee6c0db07e68448e336cf8ea4131a620edefebf9893e759b2d793420f8" +dependencies = [ + "cobs", + "embedded-io", + "heapless", + "serde", +] + +[[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.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "prettyplease" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" +dependencies = [ + "proc-macro2", + "syn 2.0.66", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" +dependencies = [ + "toml_datetime", + "toml_edit 0.20.2", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + +[[package]] +name = "proc-macro2" +version = "1.0.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", + "rand_pcg", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[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 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.15", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "raw-window-handle" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" + +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +dependencies = [ + "bitflags 2.5.0", +] + +[[package]] +name = "redox_users" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +dependencies = [ + "getrandom 0.2.15", + "libredox", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" + +[[package]] +name = "reqwest" +version = "0.11.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +dependencies = [ + "base64 0.21.7", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.29", + "hyper-rustls", + "ipnet", + "js-sys", + "log", + "mime", + "mime_guess", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 0.1.2", + "system-configuration", + "tokio", + "tokio-rustls", + "tokio-util", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "webpki-roots", + "winreg", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "rfd" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a73a7337fc24366edfca76ec521f51877b114e42dab584008209cca6719251" +dependencies = [ + "ashpd", + "block", + "dispatch", + "js-sys", + "log", + "objc", + "objc-foundation", + "objc_id", + "pollster", + "raw-window-handle 0.6.2", + "urlencoding", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-sys 0.48.0", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.15", + "libc", + "spin 0.9.8", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rsa" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" +dependencies = [ + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core 0.6.4", + "signature", + "spki", + "subtle", + "zeroize", +] + +[[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_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags 2.5.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.21.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + +[[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 = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array 0.14.7", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "selectors" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe" +dependencies = [ + "bitflags 1.3.2", + "cssparser", + "derive_more", + "fxhash", + "log", + "matches", + "phf 0.8.0", + "phf_codegen 0.8.0", + "precomputed-hash", + "servo_arc", + "smallvec", + "thin-slice", +] + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +dependencies = [ + "serde", +] + +[[package]] +name = "send_wrapper" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" +dependencies = [ + "futures-core", +] + +[[package]] +name = "serde" +version = "1.0.203" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float", + "serde", +] + +[[package]] +name = "serde-wasm-bindgen" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3b143e2833c57ab9ad3ea280d21fd34e285a42837aeb0ee301f4f41890fa00e" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "serde_derive" +version = "1.0.203" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "serde_json" +version = "1.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +dependencies = [ + "itoa 1.0.11", + "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 1.0.11", + "serde", +] + +[[package]] +name = "serde_plain" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1fc6db65a611022b23a0dec6975d63fb80a302cb3388835ff02c097258d50" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_qs" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0431a35568651e363364210c91983c1da5eb29404d9f0928b67d4ebcfa7d330c" +dependencies = [ + "percent-encoding", + "serde", + "thiserror", +] + +[[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.66", +] + +[[package]] +name = "serde_spanned" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" +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 1.0.11", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "3.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ad483d2ab0149d5a5ebcd9972a3852711e0153d863bf5a5d0391d28883c4a20" +dependencies = [ + "base64 0.22.1", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.2.6", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65569b702f41443e8bc8bbb1c5779bd0450bbe723b56198980e80ec45780bce2" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "server_fn" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b06e6e5467a2cd93ce1accfdfd8b859404f0b3b2041131ffd774fabf666b8219" +dependencies = [ + "axum", + "bytes", + "const_format", + "dashmap", + "futures", + "gloo-net 0.5.0", + "http 1.1.0", + "http-body-util", + "hyper 1.3.1", + "inventory", + "js-sys", + "once_cell", + "reqwest", + "send_wrapper", + "serde", + "serde_json", + "serde_qs", + "server_fn_macro_default", + "thiserror", + "tower", + "tower-layer", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "xxhash-rust", +] + +[[package]] +name = "server_fn_macro" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09c216bb1c1ac890151397643c663c875a1836adf0b269be4e389cb1b48c173c" +dependencies = [ + "const_format", + "convert_case 0.6.0", + "proc-macro2", + "quote", + "syn 2.0.66", + "xxhash-rust", +] + +[[package]] +name = "server_fn_macro_default" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00783df297ec85ea605779f2fef9cbec98981dffe2e01e1a9845c102ee1f1ae6" +dependencies = [ + "server_fn_macro", + "syn 2.0.66", +] + +[[package]] +name = "servo_arc" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432" +dependencies = [ + "nodrop", + "stable_deref_trait", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[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 = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core 0.6.4", +] + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "sledgehammer_bindgen" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcfaf791ff02f48f3518ce825d32cf419c13a43c1d8b1232f74ac89f339c46d2" +dependencies = [ + "sledgehammer_bindgen_macro", + "wasm-bindgen", +] + +[[package]] +name = "sledgehammer_bindgen_macro" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdd941cc539bd3dc694edaf9d0c4e1221d02baa67c6b45ec04fad1024d9e8139" +dependencies = [ + "quote", + "syn 2.0.66", +] + +[[package]] +name = "sledgehammer_utils" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f20798defa0e9d4eff9ca451c7f84774c7378a9c3b5a40112cfa2b3eadb97ae2" +dependencies = [ + "lru", + "once_cell", + "rustc-hash", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "soup3" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "471f924a40f31251afc77450e781cb26d55c0b650842efafc9c6cbd2f7cc4f9f" +dependencies = [ + "futures-channel", + "gio", + "glib", + "libc", + "soup3-sys", +] + +[[package]] +name = "soup3-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebe8950a680a12f24f15ebe1bf70db7af98ad242d9db43596ad3108aab86c27" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spinning" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d4f0e86297cad2658d92a707320d87bf4e6ae1050287f51d19b67ef3f153a7b" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[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 = "string_cache" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" +dependencies = [ + "new_debug_unreachable", + "once_cell", + "parking_lot", + "phf_shared 0.10.0", + "precomputed-hash", + "serde", +] + +[[package]] +name = "string_cache_codegen" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", + "proc-macro2", + "quote", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[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.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "sync_wrapper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +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 0.5.0", + "pkg-config", + "toml", + "version-compare", +] + +[[package]] +name = "tao" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69ebbccb78deb5a36744c079eea2981b4a48ecbbe6b1b2ffbaa528bea3f5e5db" +dependencies = [ + "bitflags 1.3.2", + "cocoa", + "core-foundation", + "core-graphics", + "crossbeam-channel", + "dispatch", + "dlopen2", + "gdkwayland-sys", + "gdkx11-sys", + "gtk", + "instant", + "jni", + "lazy_static", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "objc", + "once_cell", + "parking_lot", + "raw-window-handle 0.5.2", + "raw-window-handle 0.6.2", + "scopeguard", + "tao-macros", + "unicode-segmentation", + "url", + "windows 0.54.0", + "windows-version", + "x11-dl", +] + +[[package]] +name = "tao-macros" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec114582505d158b669b136e6851f85840c109819d77c42bb7c0709f727d18c2" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "target-lexicon" +version = "0.12.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" + +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys 0.52.0", +] + +[[package]] +name = "tendril" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" +dependencies = [ + "futf", + "mac", + "utf-8", +] + +[[package]] +name = "thin-slice" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" + +[[package]] +name = "thiserror" +version = "1.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "itoa 1.0.11", + "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.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "to_method" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c4ceeeca15c8384bbc3e011dbd8fccb7f068a440b752b7d9b32ceb0ca0e2e8" + +[[package]] +name = "tokio" +version = "1.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", + "tokio-util", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite", +] + +[[package]] +name = "tokio-util" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "futures-util", + "hashbrown 0.14.5", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.20.2", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.2.6", + "toml_datetime", + "winnow", +] + +[[package]] +name = "toml_edit" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +dependencies = [ + "indexmap 2.2.6", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" +dependencies = [ + "bitflags 2.5.0", + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", + "http-range-header", + "httpdate", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project", + "tracing", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "nu-ansi-term", + "sharded-slab", + "smallvec", + "thread_local", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "tracing-wasm" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4575c663a174420fa2d78f4108ff68f65bf2fbb7dd89f33749b6e826b3626e07" +dependencies = [ + "tracing", + "tracing-subscriber", + "wasm-bindgen", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "tungstenite" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http 1.1.0", + "httparse", + "log", + "rand 0.8.5", + "sha1", + "thiserror", + "url", + "utf-8", +] + +[[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", +] + +[[package]] +name = "unicase" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +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 = "uuid" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" +dependencies = [ + "getrandom 0.2.15", + "wasm-bindgen", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[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.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[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 = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[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.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.66", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "wasm-streams" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webbrowser" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db67ae75a9405634f5882791678772c94ff5f16a66535aae186e26aa0841fc8b" +dependencies = [ + "core-foundation", + "home", + "jni", + "log", + "ndk-context", + "objc", + "raw-window-handle 0.5.2", + "url", + "web-sys", +] + +[[package]] +name = "webkit2gtk" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76b1bc1e54c581da1e9f179d0b38512ba358fb1af2d634a1affe42e37172361a" +dependencies = [ + "bitflags 1.3.2", + "cairo-rs", + "gdk", + "gdk-sys", + "gio", + "gio-sys", + "glib", + "glib-sys", + "gobject-sys", + "gtk", + "gtk-sys", + "javascriptcore-rs", + "libc", + "once_cell", + "soup3", + "webkit2gtk-sys", +] + +[[package]] +name = "webkit2gtk-sys" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62daa38afc514d1f8f12b8693d30d5993ff77ced33ce30cd04deebc267a6d57c" +dependencies = [ + "bitflags 1.3.2", + "cairo-sys-rs", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "gtk-sys", + "javascriptcore-rs-sys", + "libc", + "pkg-config", + "soup3-sys", + "system-deps", +] + +[[package]] +name = "webpki-roots" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + +[[package]] +name = "webview2-com" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0ae9c7e420783826cf769d2c06ac9ba462f450eca5893bb8c6c6529a4e5dd33" +dependencies = [ + "webview2-com-macros", + "webview2-com-sys", + "windows 0.52.0", + "windows-core 0.52.0", + "windows-implement 0.52.0", + "windows-interface 0.52.0", +] + +[[package]] +name = "webview2-com-macros" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac1345798ecd8122468840bcdf1b95e5dc6d2206c5e4b0eafa078d061f59c9bc" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "webview2-com-sys" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6ad85fceee6c42fa3d61239eba5a11401bf38407a849ed5ea1b407df08cca72" +dependencies = [ + "thiserror", + "windows 0.52.0", + "windows-core 0.52.0", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +dependencies = [ + "windows-sys 0.52.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.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core 0.52.0", + "windows-implement 0.52.0", + "windows-interface 0.52.0", + "windows-targets 0.52.5", +] + +[[package]] +name = "windows" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49" +dependencies = [ + "windows-core 0.54.0", + "windows-implement 0.53.0", + "windows-interface 0.53.0", + "windows-targets 0.52.5", +] + +[[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.5", +] + +[[package]] +name = "windows-core" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65" +dependencies = [ + "windows-result", + "windows-targets 0.52.5", +] + +[[package]] +name = "windows-implement" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12168c33176773b86799be25e2a2ba07c7aab9968b37541f1094dbd7a60c8946" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "windows-implement" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942ac266be9249c84ca862f0a164a39533dc2f6f33dc98ec89c8da99b82ea0bd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "windows-interface" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d8dc32e0095a7eeccebd0e3f09e9509365ecb3fc6ac4d6f5f14a3f6392942d1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "windows-interface" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da33557140a288fae4e1d5f8873aaf9eb6613a9cf82c3e070223ff177f598b60" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "windows-result" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "749f0da9cc72d82e600d8d2e44cadd0b9eedb9038f71a1c58556ac1c5791813b" +dependencies = [ + "windows-targets 0.52.5", +] + +[[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.5", +] + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", +] + +[[package]] +name = "windows-version" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6998aa457c9ba8ff2fb9f13e9d2a930dabcea28f1d0ab94d687d8b3654844515" +dependencies = [ + "windows-targets 0.52.5", +] + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "wry" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b717040ba9771fd88eb428c6ea6b555f8e734ff8534f02c13e8f10d97f5935e" +dependencies = [ + "base64 0.21.7", + "block", + "cfg_aliases", + "cocoa", + "core-graphics", + "crossbeam-channel", + "dunce", + "gdkx11", + "gtk", + "html5ever", + "http 0.2.12", + "javascriptcore-rs", + "jni", + "kuchikiki", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "objc", + "objc_id", + "once_cell", + "percent-encoding", + "raw-window-handle 0.6.2", + "serde", + "serde_json", + "sha2", + "soup3", + "tao-macros", + "thiserror", + "webkit2gtk", + "webkit2gtk-sys", + "webview2-com", + "windows 0.52.0", + "windows-implement 0.52.0", + "windows-version", + "x11-dl", +] + +[[package]] +name = "x11" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "xdg-home" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21e5a325c3cb8398ad6cf859c1135b25dd29e186679cf2da7581d9679f63b38e" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "xxhash-rust" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927da81e25be1e1a2901d59b81b37dd2efd1fc9c9345a55007f09bf5a2d3ee03" + +[[package]] +name = "yazi" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c94451ac9513335b5e23d7a8a2b61a7102398b8cca5160829d313e84c9d98be1" + +[[package]] +name = "zbus" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b8e3d6ae3342792a6cc2340e4394334c7402f3d793b390d2c5494a4032b3030" +dependencies = [ + "async-broadcast", + "async-executor", + "async-fs", + "async-io", + "async-lock", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "derivative", + "enumflags2", + "event-listener", + "futures-core", + "futures-sink", + "futures-util", + "hex", + "nix", + "ordered-stream", + "rand 0.8.5", + "serde", + "serde_repr", + "sha1", + "static_assertions", + "tracing", + "uds_windows", + "windows-sys 0.52.0", + "xdg-home", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7a3e850ff1e7217a3b7a07eba90d37fe9bb9e89a310f718afcde5885ca9b6d7" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "regex", + "syn 1.0.109", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" +dependencies = [ + "serde", + "static_assertions", + "zvariant", +] + +[[package]] +name = "zerocopy" +version = "0.7.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zvariant" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e09e8be97d44eeab994d752f341e67b3b0d80512a8b315a0671d47232ef1b65" +dependencies = [ + "endi", + "enumflags2", + "serde", + "static_assertions", + "url", + "zvariant_derive", +] + +[[package]] +name = "zvariant_derive" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72a5857e2856435331636a9fbb415b09243df4521a267c5bedcd5289b4d5799e" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00bedb16a193cc12451873fee2a1bc6550225acece0e36f333e68326c73c8172" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] diff --git a/examples/openid_connect_demo/Cargo.toml b/examples/openid_connect_demo/Cargo.toml new file mode 100644 index 0000000000..72ae74dc98 --- /dev/null +++ b/examples/openid_connect_demo/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "openid_auth_demo" +version = "0.1.0" +edition = "2021" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1.0.86" +console_error_panic_hook = "0.1" +dioxus = { path = "../../packages/dioxus", default_features = true, features = [ + "router", + "signals", +], version = "*" } +dioxus-logger = "0.5.1" +dioxus-sdk = { git = "https://github.com/Dioxuslabs/sdk", features = [ + "storage", +] } +form_urlencoded = "1.2.1" +log = "0.4" +openidconnect = "3.5.0" +serde = { version = "1.0.203", features = ["derive"] } +uuid = "1.8" + +[features] +default = ["web"] +server = ["dioxus/axum"] +web = ["dioxus/web"] +desktop = ["dioxus/desktop"] +fullstack = ["dioxus/fullstack"] + +# since we're using dioxus from local path, inform dioxus-sdk to use it as well +[patch.crates-io] +dioxus = { path = "../../packages/dioxus" } +dioxus-signals = { path = "../../packages/signals" } diff --git a/examples/openid_connect_demo/Dioxus.toml b/examples/openid_connect_demo/Dioxus.toml new file mode 100644 index 0000000000..3a130e2ea3 --- /dev/null +++ b/examples/openid_connect_demo/Dioxus.toml @@ -0,0 +1,35 @@ +[application] + +# dioxus project name +name = "OpenID Connect authentication demo" + +# default platform +# you can also use `dioxus serve/build --platform XXX` to use other platform +# value: web | desktop +default_platform = "web" + +# Web `build` & `serve` dist path +out_dir = "dist" + +# resource (static) file folder +asset_dir = "public" + +# hot reload by default +hot_reload = true + +[web.app] + +# HTML title tag content +title = "OpenID Connect authentication demo" + +[web.watcher] + +index_on_404 = true + +watch_path = ["src"] + +[application.plugins] + +available = true + +required = [] diff --git a/examples/openid_connect_demo/README.md b/examples/openid_connect_demo/README.md new file mode 100644 index 0000000000..7b0d100fd9 --- /dev/null +++ b/examples/openid_connect_demo/README.md @@ -0,0 +1,12 @@ +# OpenID Connect example to show how to authenticate an user + +The environment variables in [`.cargo/config.toml`](./.cargo/config.toml) must be set in order for this example to work. + +Once they are set, you can run `dx serve --platform web` or `dx serve --platform desktop`. + +### Environment variables summary + +- `DIOXUS_FRONT_ISSUER_URL`: The openid-connect's issuer url +- `DIOXUS_FRONT_CLIENT_ID`: The openid-connect's client id +- `DIOXUS_FRONT_CLIENT_SECRET`: The openid-connect's client secret +- `DIOXUS_FRONT_URL`: The url the frontend is supposed to be running on, it could be for example `http://localhost:8080` diff --git a/examples/openid_connect_demo/src/constants.rs b/examples/openid_connect_demo/src/constants.rs new file mode 100644 index 0000000000..0a0b4950a6 --- /dev/null +++ b/examples/openid_connect_demo/src/constants.rs @@ -0,0 +1,2 @@ +pub const DIOXUS_FRONT_AUTH_TOKEN: &str = "auth_token"; +pub const DIOXUS_FRONT_AUTH_REQUEST: &str = "auth_request"; diff --git a/examples/openid_connect_demo/src/main.rs b/examples/openid_connect_demo/src/main.rs new file mode 100644 index 0000000000..8487758595 --- /dev/null +++ b/examples/openid_connect_demo/src/main.rs @@ -0,0 +1,36 @@ +#![allow(non_snake_case)] +use dioxus::prelude::*; +use dioxus_logger::tracing::Level; +use router::Route; + +use crate::oidc::ClientState; +use crate::storage::{use_auth_request_provider, use_auth_token_provider}; + +pub(crate) mod constants; +pub(crate) mod model; +pub(crate) mod oidc; +pub(crate) mod props; +pub(crate) mod router; +pub(crate) mod storage; +pub(crate) mod views; + +pub static CLIENT: GlobalSignal = Signal::global(ClientState::default); + +pub static DIOXUS_FRONT_ISSUER_URL: &str = env!("DIOXUS_FRONT_ISSUER_URL"); +pub static DIOXUS_FRONT_CLIENT_ID: &str = env!("DIOXUS_FRONT_CLIENT_ID"); +pub static DIOXUS_FRONT_CLIENT_SECRET: &str = env!("DIOXUS_FRONT_CLIENT_SECRET"); +pub static DIOXUS_FRONT_URL: &str = env!("DIOXUS_FRONT_URL"); + +fn App() -> Element { + use_auth_request_provider(); + use_auth_token_provider(); + rsx! { Router:: {} } +} + +fn main() { + dioxus_logger::init(Level::DEBUG).expect("failed to init logger"); + dioxus_sdk::set_dir!(); + console_error_panic_hook::set_once(); + log::info!("starting app"); + launch(App); +} diff --git a/examples/openid_connect_demo/src/model/mod.rs b/examples/openid_connect_demo/src/model/mod.rs new file mode 100644 index 0000000000..68a79f5c0c --- /dev/null +++ b/examples/openid_connect_demo/src/model/mod.rs @@ -0,0 +1 @@ +pub(crate) mod user; diff --git a/examples/openid_connect_demo/src/model/user.rs b/examples/openid_connect_demo/src/model/user.rs new file mode 100644 index 0000000000..b1005fdd24 --- /dev/null +++ b/examples/openid_connect_demo/src/model/user.rs @@ -0,0 +1,7 @@ +use uuid::Uuid; + +#[derive(PartialEq)] +pub struct User { + pub id: Uuid, + pub name: String, +} diff --git a/examples/openid_connect_demo/src/oidc.rs b/examples/openid_connect_demo/src/oidc.rs new file mode 100644 index 0000000000..2c20c4e49b --- /dev/null +++ b/examples/openid_connect_demo/src/oidc.rs @@ -0,0 +1,127 @@ +use anyhow::Result; +use openidconnect::{ + core::{CoreClient, CoreIdToken, CoreResponseType, CoreTokenResponse}, + reqwest::async_http_client, + url::Url, + AuthenticationFlow, AuthorizationCode, ClaimsVerificationError, ClientId, ClientSecret, + CsrfToken, IssuerUrl, LogoutRequest, Nonce, ProviderMetadataWithLogout, RedirectUrl, + RefreshToken, +}; +use serde::{Deserialize, Serialize}; + +use crate::{props::client::ClientProps, DIOXUS_FRONT_CLIENT_ID}; + +#[derive(Clone, Debug, Default)] +pub struct ClientState { + pub oidc_client: Option, +} + +/// State that holds the nonce and authorization url and the nonce generated to log in an user +#[derive(Clone, PartialEq, Deserialize, Serialize, Default)] +pub struct AuthRequestState { + pub auth_request: Option, +} + +#[derive(Clone, PartialEq, Deserialize, Serialize)] +pub struct AuthRequest { + pub nonce: Nonce, + pub authorize_url: String, +} + +/// State the tokens returned once the user is authenticated +#[derive(Debug, Deserialize, Serialize, Default, Clone)] +pub struct AuthTokenState { + /// Token used to identify the user + pub id_token: Option, + /// Token used to refresh the tokens if they expire + pub refresh_token: Option, +} + +impl PartialEq for AuthTokenState { + fn eq(&self, other: &Self) -> bool { + self.id_token == other.id_token + && self.refresh_token.as_ref().map(|t| t.secret().clone()) + == other.refresh_token.as_ref().map(|t| t.secret().clone()) + } +} + +pub fn email( + client: CoreClient, + id_token: CoreIdToken, + nonce: Nonce, +) -> Result { + match id_token.claims(&client.id_token_verifier(), &nonce) { + Ok(claims) => Ok(claims.clone().email().unwrap().to_string()), + Err(error) => Err(error), + } +} + +pub fn authorize_url(client: CoreClient) -> AuthRequest { + let (authorize_url, _csrf_state, nonce) = client + .authorize_url( + AuthenticationFlow::::AuthorizationCode, + CsrfToken::new_random, + Nonce::new_random, + ) + .add_scope(openidconnect::Scope::new("email".to_string())) + .add_scope(openidconnect::Scope::new("profile".to_string())) + .url(); + AuthRequest { + authorize_url: authorize_url.to_string(), + nonce, + } +} + +pub async fn init_provider_metadata() -> Result { + let issuer_url = IssuerUrl::new(crate::DIOXUS_FRONT_ISSUER_URL.to_string())?; + Ok(ProviderMetadataWithLogout::discover_async(issuer_url, async_http_client).await?) +} + +pub async fn init_oidc_client() -> Result<(ClientId, CoreClient)> { + let client_id = ClientId::new(crate::DIOXUS_FRONT_CLIENT_ID.to_string()); + let provider_metadata = init_provider_metadata().await?; + let client_secret = Some(ClientSecret::new( + crate::DIOXUS_FRONT_CLIENT_SECRET.to_string(), + )); + let redirect_url = RedirectUrl::new(format!("{}/login", crate::DIOXUS_FRONT_URL))?; + + Ok(( + client_id.clone(), + CoreClient::from_provider_metadata(provider_metadata, client_id, client_secret) + .set_redirect_uri(redirect_url), + )) +} + +///TODO: Add pkce_pacifier +pub async fn token_response(oidc_client: CoreClient, code: String) -> Result { + // let (pkce_challenge, pkce_verifier) = PkceCodeChallenge::new_random_sha256(); + Ok(oidc_client + .exchange_code(AuthorizationCode::new(code.clone())) + // .set_pkce_verifier(pkce_verifier) + .request_async(async_http_client) + .await?) +} + +pub async fn exchange_refresh_token( + oidc_client: CoreClient, + refresh_token: RefreshToken, +) -> Result { + Ok(oidc_client + .exchange_refresh_token(&refresh_token) + .request_async(async_http_client) + .await?) +} + +pub async fn log_out_url(id_token_hint: CoreIdToken) -> Result { + let provider_metadata = init_provider_metadata().await?; + let end_session_url = provider_metadata + .additional_metadata() + .clone() + .end_session_endpoint + .unwrap(); + let logout_request: LogoutRequest = LogoutRequest::from(end_session_url); + Ok(logout_request + .set_client_id(ClientId::new(DIOXUS_FRONT_CLIENT_ID.to_string())) + .set_id_token_hint(&id_token_hint) + .http_get_url()) +} diff --git a/examples/openid_connect_demo/src/props/client.rs b/examples/openid_connect_demo/src/props/client.rs new file mode 100644 index 0000000000..2e02111e16 --- /dev/null +++ b/examples/openid_connect_demo/src/props/client.rs @@ -0,0 +1,20 @@ +use dioxus::prelude::*; +use openidconnect::{core::CoreClient, ClientId}; + +#[derive(Props, Clone, Debug)] +pub struct ClientProps { + pub client: CoreClient, + pub client_id: ClientId, +} + +impl PartialEq for ClientProps { + fn eq(&self, other: &Self) -> bool { + self.client_id == other.client_id + } +} + +impl ClientProps { + pub fn new(client_id: ClientId, client: CoreClient) -> Self { + ClientProps { client_id, client } + } +} diff --git a/examples/openid_connect_demo/src/props/mod.rs b/examples/openid_connect_demo/src/props/mod.rs new file mode 100644 index 0000000000..1d33131531 --- /dev/null +++ b/examples/openid_connect_demo/src/props/mod.rs @@ -0,0 +1 @@ +pub(crate) mod client; diff --git a/examples/openid_connect_demo/src/router.rs b/examples/openid_connect_demo/src/router.rs new file mode 100644 index 0000000000..8f4b1b36a6 --- /dev/null +++ b/examples/openid_connect_demo/src/router.rs @@ -0,0 +1,16 @@ +use crate::views::{header::AuthHeader, home::Home, login::Login, not_found::NotFound}; +use dioxus::prelude::*; + +#[derive(Routable, Clone)] +pub enum Route { + #[layout(AuthHeader)] + #[route("/")] + Home {}, + + // https://dioxuslabs.com/learn/0.4/router/reference/routes#query-segments + #[route("/login?:query_string")] + Login { query_string: String }, + #[end_layout] + #[route("/:..route")] + NotFound { route: Vec }, +} diff --git a/examples/openid_connect_demo/src/storage.rs b/examples/openid_connect_demo/src/storage.rs new file mode 100644 index 0000000000..c34819b965 --- /dev/null +++ b/examples/openid_connect_demo/src/storage.rs @@ -0,0 +1,35 @@ +use dioxus::prelude::*; +use dioxus_sdk::storage::*; + +use crate::{ + constants::{DIOXUS_FRONT_AUTH_REQUEST, DIOXUS_FRONT_AUTH_TOKEN}, + oidc::{AuthRequestState, AuthTokenState}, +}; + +pub fn use_auth_token_provider() { + let stored_token = + use_storage::(DIOXUS_FRONT_AUTH_TOKEN.to_owned(), AuthTokenState::default); + + use_context_provider(move || stored_token); +} + +pub fn use_auth_token() -> Signal { + use_context() +} + +pub fn use_auth_request_provider() { + let stored_req = use_storage::( + DIOXUS_FRONT_AUTH_REQUEST.to_owned(), + AuthRequestState::default, + ); + + use_context_provider(move || stored_req); +} + +pub fn use_auth_request() -> Signal { + use_context() +} + +pub fn auth_request() -> Signal { + consume_context() +} diff --git a/examples/openid_connect_demo/src/views/header.rs b/examples/openid_connect_demo/src/views/header.rs new file mode 100644 index 0000000000..274a0d22f0 --- /dev/null +++ b/examples/openid_connect_demo/src/views/header.rs @@ -0,0 +1,221 @@ +use crate::storage::{auth_request, use_auth_request, use_auth_token}; +use crate::{ + oidc::{ + authorize_url, email, exchange_refresh_token, init_oidc_client, log_out_url, + AuthRequestState, AuthTokenState, ClientState, + }, + props::client::ClientProps, + router::Route, + CLIENT, +}; +use anyhow::Result; +use dioxus::prelude::*; +use dioxus::router::prelude::{Link, Outlet}; +use openidconnect::{url::Url, OAuth2TokenResponse, TokenResponse}; + +#[component] +pub fn LogOut() -> Element { + let mut auth_token = use_auth_token(); + let log_out_url_state = use_signal(|| None::>>); + match auth_token().id_token { + Some(id_token) => match &*log_out_url_state.read() { + Some(log_out_url_result) => match log_out_url_result { + Some(uri) => match uri { + Ok(uri) => { + rsx! { + Link { + onclick: move |_| { + auth_token.take(); + }, + to: uri.to_string(), + "Log out" + } + } + } + Err(error) => { + rsx! { div { "Failed to load disconnection url: {error:?}" } } + } + }, + None => { + rsx! { div { "Loading... Please wait" } } + } + }, + None => { + let logout_url_task = move || { + spawn({ + let mut log_out_url_state = log_out_url_state.to_owned(); + async move { + let logout_url = log_out_url(id_token).await; + let logout_url_option = Some(logout_url); + log_out_url_state.set(Some(logout_url_option)); + } + }) + }; + logout_url_task(); + rsx! { div { "Loading log out url... Please wait" } } + } + }, + None => { + rsx! {{}} + } + } +} + +#[component] +pub fn RefreshToken(props: ClientProps) -> Element { + let mut auth_token = use_auth_token(); + match auth_token().refresh_token { + Some(refresh_token) => { + rsx! { div { + onmounted: { + move |_| { + let client = props.client.clone(); + let refresh_token = refresh_token.clone(); + async move { + let exchange_refresh_token = + exchange_refresh_token(client, refresh_token).await; + match exchange_refresh_token { + Ok(response_token) => { + auth_token.set(AuthTokenState { + id_token: response_token.id_token().cloned(), + refresh_token: response_token.refresh_token().cloned(), + }); + } + Err(_error) => { + auth_token.take(); + auth_request().take(); + } + } + } + } + }, + "Refreshing session, please wait" + } } + } + None => { + rsx! { div { "Id token expired and no refresh token found" } } + } + } +} + +#[component] +pub fn LoadClient() -> Element { + let init_client_future = use_resource(move || async move { init_oidc_client().await }); + match &*init_client_future.read_unchecked() { + Some(Ok((client_id, client))) => rsx! { + div { + onmounted: { + let client_id = client_id.clone(); + let client = client.clone(); + move |_| { + *CLIENT.write() = ClientState { + oidc_client: Some(ClientProps::new(client_id.clone(), client.clone())), + }; + } + }, + "Client successfully loaded" + } + Outlet:: {} + }, + Some(Err(error)) => { + log::info! {"Failed to load client: {:?}", error}; + rsx! { + div { "Failed to load client: {error:?}" } + Outlet:: {} + } + } + None => { + rsx! { + div { + div { "Loading client, please wait" } + Outlet:: {} + } + } + } + } +} + +#[component] +pub fn AuthHeader() -> Element { + let client = CLIENT.read().oidc_client.clone(); + let mut auth_request = use_auth_request(); + let auth_token = use_auth_token(); + match (client, auth_request(), auth_token()) { + // We have everything we need to attempt to authenticate the user + (Some(client_props), current_auth_request, current_auth_token) => { + match current_auth_request.auth_request { + Some(new_auth_request) => { + match current_auth_token.id_token { + Some(id_token) => { + match email( + client_props.client.clone(), + id_token.clone(), + new_auth_request.nonce.clone(), + ) { + Ok(email) => { + rsx! { + div { + div { {email} } + LogOut {} + Outlet:: {} + } + } + } + // Id token failed to be decoded + Err(error) => match error { + // Id token failed to be decoded because it expired, we refresh it + openidconnect::ClaimsVerificationError::Expired(_message) => { + log::info!("Token expired"); + rsx! { + div { + RefreshToken { client_id: client_props.client_id, client: client_props.client } + Outlet:: {} + } + } + } + // Other issue with token decoding + _ => { + log::info!("Other issue with token"); + rsx! { + div { + div { "{error}" } + Outlet:: {} + } + } + } + }, + } + } + // User is not logged in + None => { + rsx! { + div { + Link { to: new_auth_request.authorize_url.clone(), "Log in" } + Outlet:: {} + } + } + } + } + } + None => { + rsx! { div { + onmounted: { + let client = client_props.client; + move |_| { + let new_auth_request = authorize_url(client.clone()); + auth_request.set(AuthRequestState { + auth_request: Some(new_auth_request), + }); + } + }, + "Loading nonce" + } } + } + } + } + // Client is not initialized yet, we need it for everything + (None, _, _) => { + rsx! { LoadClient {} } + } + } +} diff --git a/examples/openid_connect_demo/src/views/home.rs b/examples/openid_connect_demo/src/views/home.rs new file mode 100644 index 0000000000..d91b5c2037 --- /dev/null +++ b/examples/openid_connect_demo/src/views/home.rs @@ -0,0 +1,5 @@ +use dioxus::prelude::*; + +pub fn Home() -> Element { + rsx! { div { "Hello world" } } +} diff --git a/examples/openid_connect_demo/src/views/login.rs b/examples/openid_connect_demo/src/views/login.rs new file mode 100644 index 0000000000..e3a4e0025b --- /dev/null +++ b/examples/openid_connect_demo/src/views/login.rs @@ -0,0 +1,86 @@ +use crate::{ + oidc::{token_response, AuthTokenState}, + router::Route, + storage::{auth_request, use_auth_token}, + CLIENT, +}; +use dioxus::prelude::*; +use dioxus::router::prelude::Link; +use openidconnect::{OAuth2TokenResponse, TokenResponse}; + +#[component] +pub fn Login(query_string: String) -> Element { + let client = CLIENT.read().oidc_client.clone(); + let mut auth_token = use_auth_token(); + let current_auth_token = auth_token(); + match client { + Some(client_props) => { + match ( + current_auth_token.id_token, + current_auth_token.refresh_token, + ) { + (Some(_id_token), Some(_refresh_token)) => { + rsx! { + div { "Sign in successful" } + Link { to: Route::Home {}, "Go back home" } + } + } + // If the refresh token is set but not the id_token, there was an error, we just go back home and reset their value + (None, Some(_)) | (Some(_), None) => { + rsx! { + div { "Error while attempting to log in" } + Link { + to: Route::Home {}, + onclick: move |_| { + auth_token.take(); + auth_request().take(); + }, + "Go back home" + } + } + } + (None, None) => { + let mut query_pairs = form_urlencoded::parse(query_string.as_bytes()); + let code_pair = query_pairs.find(|(key, _value)| key == "code"); + match code_pair { + Some((_key, code)) => { + let code = code.to_string(); + rsx! { div { + onmounted: { + move |_| { + let auth_code = code.to_string(); + let client_props = client_props.clone(); + async move { + let token_response_result = + token_response(client_props.client, auth_code).await; + match token_response_result { + Ok(token_response) => { + let id_token = token_response.id_token().unwrap(); + auth_token.set(AuthTokenState { + id_token: Some(id_token.clone()), + refresh_token: token_response + .refresh_token() + .cloned(), + }); + } + Err(error) => { + log::warn! {"{error}"}; + } + } + } + } + } + }} + } + None => { + rsx! { div { "No code provided" } } + } + } + } + } + } + _ => { + rsx! {{}} + } + } +} diff --git a/examples/openid_connect_demo/src/views/mod.rs b/examples/openid_connect_demo/src/views/mod.rs new file mode 100644 index 0000000000..1b8e08a585 --- /dev/null +++ b/examples/openid_connect_demo/src/views/mod.rs @@ -0,0 +1,4 @@ +pub(crate) mod header; +pub(crate) mod home; +pub(crate) mod login; +pub(crate) mod not_found; diff --git a/examples/openid_connect_demo/src/views/not_found.rs b/examples/openid_connect_demo/src/views/not_found.rs new file mode 100644 index 0000000000..833c610674 --- /dev/null +++ b/examples/openid_connect_demo/src/views/not_found.rs @@ -0,0 +1,10 @@ +use dioxus::prelude::*; + +#[component] +pub fn NotFound(route: Vec) -> Element { + rsx! { + div{ + {route.join("")} + } + } +} diff --git a/examples/optional_props.rs b/examples/optional_props.rs index 334852bea1..ad54ed33cd 100644 --- a/examples/optional_props.rs +++ b/examples/optional_props.rs @@ -6,7 +6,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/overlay.rs b/examples/overlay.rs index 59a9619d6a..e4e2e80592 100644 --- a/examples/overlay.rs +++ b/examples/overlay.rs @@ -11,9 +11,7 @@ use dioxus::desktop::{ use dioxus::prelude::*; fn main() { - dioxus::launch::builder() - .with_cfg(make_config()) - .launch(app); + LaunchBuilder::desktop().with_cfg(make_config()).launch(app); } fn app() -> Element { @@ -22,9 +20,9 @@ fn app() -> Element { _ = use_global_shortcut("cmd+g", move || show_overlay.toggle()); rsx! { - document::Link { + head::Link { rel: "stylesheet", - href: asset!("/examples/assets/overlay.css"), + href: asset!("./examples/assets/overlay.css"), } if show_overlay() { div { diff --git a/examples/popup.rs b/examples/popup.rs index e708b9cd9f..131d536edb 100644 --- a/examples/popup.rs +++ b/examples/popup.rs @@ -5,7 +5,7 @@ use dioxus::prelude::*; use std::rc::Rc; fn main() { - dioxus::launch(app); + launch_desktop(app); } fn app() -> Element { diff --git a/examples/query_segment_search.rs b/examples/query_segment_search.rs index 4366cc0118..018f475e45 100644 --- a/examples/query_segment_search.rs +++ b/examples/query_segment_search.rs @@ -2,7 +2,7 @@ //! //! The enum router makes it easy to use your route as state in your app. This example shows how to use the router to encode search text into the url and decode it back into a string. //! -//! Run this example on desktop with +//! Run this example on desktop with //! ```sh //! dx serve --example query_segment_search //! ``` @@ -14,7 +14,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(|| { + launch(|| { rsx! { Router:: {} } @@ -29,7 +29,7 @@ enum Route { // The each query segment must implement and Display. // You can use multiple query segments separated by `&`s. - #[route("/search?:query&:word_count")] + #[route("/search?:query&:word_count")] Search { query: String, word_count: usize, diff --git a/examples/read_size.rs b/examples/read_size.rs index dbde9353c7..1965597cfa 100644 --- a/examples/read_size.rs +++ b/examples/read_size.rs @@ -9,7 +9,7 @@ use std::rc::Rc; use dioxus::{html::geometry::euclid::Rect, prelude::*}; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { @@ -28,7 +28,7 @@ fn app() -> Element { }; rsx!( - document::Stylesheet { href: asset!("/examples/assets/read_size.css") } + head::Link { rel: "stylesheet", href: asset!("./examples/assets/read_size.css") } div { width: "50%", height: "50%", diff --git a/examples/readme.rs b/examples/readme.rs index 1c36c65c00..8f412a749d 100644 --- a/examples/readme.rs +++ b/examples/readme.rs @@ -8,7 +8,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/reducer.rs b/examples/reducer.rs index 32a15368e8..7c434e5858 100644 --- a/examples/reducer.rs +++ b/examples/reducer.rs @@ -7,17 +7,17 @@ use dioxus::prelude::*; -const STYLE: Asset = asset!("/examples/assets/radio.css"); +const STYLE: &str = asset!("./examples/assets/radio.css"); fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { let mut state = use_signal(|| PlayerState { is_playing: false }); rsx!( - document::Stylesheet { href: STYLE } + head::Link { rel: "stylesheet", href: STYLE } h1 {"Select an option"} // Add some cute animations if the radio is playing! diff --git a/examples/resize.rs b/examples/resize.rs index 52b55bfb8c..eace774a0b 100644 --- a/examples/resize.rs +++ b/examples/resize.rs @@ -8,14 +8,14 @@ use dioxus::prelude::*; use dioxus_elements::geometry::euclid::Size2D; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { let mut dimensions = use_signal(Size2D::zero); rsx!( - document::Stylesheet { href: asset!("/examples/assets/read_size.css") } + head::Link { rel: "stylesheet", href: asset!("./examples/assets/read_size.css") } div { width: "50%", height: "50%", diff --git a/examples/router.rs b/examples/router.rs index 377c595418..13197f57e2 100644 --- a/examples/router.rs +++ b/examples/router.rs @@ -8,12 +8,12 @@ use dioxus::prelude::*; -const STYLE: Asset = asset!("/examples/assets/router.css"); +const STYLE: &str = asset!("./examples/assets/router.css"); fn main() { - dioxus::launch(|| { + launch(|| { rsx! { - document::Stylesheet { href: STYLE } + head::Link { rel: "stylesheet", href: STYLE } Router:: {} } }); diff --git a/examples/router_resource.rs b/examples/router_resource.rs index 6898585f3b..de69f0e40b 100644 --- a/examples/router_resource.rs +++ b/examples/router_resource.rs @@ -15,7 +15,7 @@ enum Route { } fn main() { - dioxus::launch(App); + launch(App); } #[component] diff --git a/examples/rsx_usage.rs b/examples/rsx_usage.rs index b8ae72fd68..3bffe8c700 100644 --- a/examples/rsx_usage.rs +++ b/examples/rsx_usage.rs @@ -39,7 +39,7 @@ //! - Allow top-level fragments fn main() { - dioxus::launch(app) + launch(app) } use core::{fmt, str::FromStr}; @@ -228,9 +228,6 @@ fn app() -> Element { // Or we can shell out to a helper function {format_dollars(10, 50)} - - // some text? - {Some("hello world!")} } } } diff --git a/examples/scroll_to_top.rs b/examples/scroll_to_top.rs index 1cd91e4947..9f4f85955e 100644 --- a/examples/scroll_to_top.rs +++ b/examples/scroll_to_top.rs @@ -8,7 +8,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/shortcut.rs b/examples/shortcut.rs index a6626c27b7..d0192b9782 100644 --- a/examples/shortcut.rs +++ b/examples/shortcut.rs @@ -9,7 +9,7 @@ use dioxus::desktop::use_global_shortcut; use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch_desktop(app); } fn app() -> Element { diff --git a/examples/shorthand.rs b/examples/shorthand.rs index bd2047a8e6..33905d5710 100644 --- a/examples/shorthand.rs +++ b/examples/shorthand.rs @@ -3,7 +3,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/signals.rs b/examples/signals.rs index 6b881587bc..26db9685c1 100644 --- a/examples/signals.rs +++ b/examples/signals.rs @@ -10,7 +10,7 @@ use async_std::task::sleep; use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/simple_list.rs b/examples/simple_list.rs index 32e1d3eb85..3c1b9d71a1 100644 --- a/examples/simple_list.rs +++ b/examples/simple_list.rs @@ -5,7 +5,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/simple_router.rs b/examples/simple_router.rs index 0ec22a001e..e674f2f30e 100644 --- a/examples/simple_router.rs +++ b/examples/simple_router.rs @@ -5,7 +5,7 @@ use dioxus::prelude::*; fn main() { // Launch the router, using our `Route` component as the generic type // This will automatically boot the app to "/" unless otherwise specified - dioxus::launch(|| rsx! { Router:: {} }); + launch(|| rsx! { Router:: {} }); } /// By default, the Routable derive will use the name of the variant as the route diff --git a/examples/streams.rs b/examples/streams.rs index a71b5b9c18..6155cb35f9 100644 --- a/examples/streams.rs +++ b/examples/streams.rs @@ -5,7 +5,7 @@ use dioxus::prelude::*; use futures_util::{future, stream, Stream, StreamExt}; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/suspense.rs b/examples/suspense.rs index 14cd2573e9..014bb3276a 100644 --- a/examples/suspense.rs +++ b/examples/suspense.rs @@ -15,7 +15,7 @@ use dioxus::desktop::{Config, LogicalSize, WindowBuilder}; use dioxus::prelude::*; fn main() { - dioxus::builder() + LaunchBuilder::new() .with_cfg(desktop! { Config::new().with_window( WindowBuilder::new() diff --git a/examples/svg.rs b/examples/svg.rs index b462f30288..d4c23d1ddb 100644 --- a/examples/svg.rs +++ b/examples/svg.rs @@ -10,7 +10,7 @@ use dioxus::prelude::*; use rand::{thread_rng, Rng}; fn main() { - dioxus::launch(|| { + launch(|| { rsx! { div { user_select: "none", webkit_user_select: "none", margin_left: "10%", margin_right: "10%", h1 { "Click die to generate a new value" } diff --git a/examples/tailwind/.gitignore b/examples/tailwind/.gitignore new file mode 100644 index 0000000000..1521c8b765 --- /dev/null +++ b/examples/tailwind/.gitignore @@ -0,0 +1 @@ +dist diff --git a/examples/tailwind/Cargo.toml b/examples/tailwind/Cargo.toml new file mode 100644 index 0000000000..6660b87397 --- /dev/null +++ b/examples/tailwind/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "dioxus-tailwind" +version = "0.0.0" +authors = [] +edition = "2021" +description = "A tailwindcss example using Dioxus" +license = "MIT OR Apache-2.0" +repository = "https://github.com/DioxusLabs/dioxus/" +homepage = "https://dioxuslabs.com" +documentation = "https://dioxuslabs.com" +publish = false + +[dependencies] +manganis = { workspace = true } + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +dioxus = { path = "../../packages/dioxus", features = ["desktop"] } + +[target.'cfg(target_arch = "wasm32")'.dependencies] +dioxus = { path = "../../packages/dioxus", features = ["web"] } diff --git a/examples/tailwind/Dioxus.toml b/examples/tailwind/Dioxus.toml new file mode 100644 index 0000000000..af7fcf3b8f --- /dev/null +++ b/examples/tailwind/Dioxus.toml @@ -0,0 +1,27 @@ +[application] + +# App (Project) Name +name = "Tailwind CSS + Dioxus" + +# Dioxus App Default Platform +# desktop, web, mobile, ssr +default_platform = "web" + +# `build` & `serve` dist path +out_dir = "dist" + +# resource (public) file folder +asset_dir = "public" + +[web.app] + +# HTML title tag content +title = "dioxus | ⛺" + +[web.watcher] + +# when watcher trigger, regenerate the `index.html` +reload_html = true + +# which files or dirs will be watcher monitoring +watch_path = ["src", "public"] diff --git a/examples/tailwind/README.md b/examples/tailwind/README.md new file mode 100644 index 0000000000..6dca4b775e --- /dev/null +++ b/examples/tailwind/README.md @@ -0,0 +1,7 @@ +Example: Basic Tailwind usage + +This example shows how an app might be styled with TailwindCSS. + +## Running + +Our [Tailwind](https://dioxuslabs.com/learn/0.5/cookbook/tailwind) guide explains how to setup and run Dioxus-Tailwind projects. \ No newline at end of file diff --git a/examples/tailwind/input.css b/examples/tailwind/input.css new file mode 100644 index 0000000000..bd6213e1df --- /dev/null +++ b/examples/tailwind/input.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; \ No newline at end of file diff --git a/examples/tailwind/public/tailwind.css b/examples/tailwind/public/tailwind.css new file mode 100644 index 0000000000..65b95316e1 --- /dev/null +++ b/examples/tailwind/public/tailwind.css @@ -0,0 +1,833 @@ +/* +! tailwindcss v3.2.7 | MIT License | https://tailwindcss.com +*/ + +/* +1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) +2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) +*/ + +*, +::before, +::after { + box-sizing: border-box; + /* 1 */ + border-width: 0; + /* 2 */ + border-style: solid; + /* 2 */ + border-color: #e5e7eb; + /* 2 */ +} + +::before, +::after { + --tw-content: ''; +} + +/* +1. Use a consistent sensible line-height in all browsers. +2. Prevent adjustments of font size after orientation changes in iOS. +3. Use a more readable tab size. +4. Use the user's configured `sans` font-family by default. +5. Use the user's configured `sans` font-feature-settings by default. +*/ + +html { + line-height: 1.5; + /* 1 */ + -webkit-text-size-adjust: 100%; + /* 2 */ + -moz-tab-size: 4; + /* 3 */ + -o-tab-size: 4; + tab-size: 4; + /* 3 */ + font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + /* 4 */ + font-feature-settings: normal; + /* 5 */ +} + +/* +1. Remove the margin in all browsers. +2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. +*/ + +body { + margin: 0; + /* 1 */ + line-height: inherit; + /* 2 */ +} + +/* +1. Add the correct height in Firefox. +2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) +3. Ensure horizontal rules are visible by default. +*/ + +hr { + height: 0; + /* 1 */ + color: inherit; + /* 2 */ + border-top-width: 1px; + /* 3 */ +} + +/* +Add the correct text decoration in Chrome, Edge, and Safari. +*/ + +abbr:where([title]) { + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; +} + +/* +Remove the default font size and weight for headings. +*/ + +h1, +h2, +h3, +h4, +h5, +h6 { + font-size: inherit; + font-weight: inherit; +} + +/* +Reset links to optimize for opt-in styling instead of opt-out. +*/ + +a { + color: inherit; + text-decoration: inherit; +} + +/* +Add the correct font weight in Edge and Safari. +*/ + +b, +strong { + font-weight: bolder; +} + +/* +1. Use the user's configured `mono` font family by default. +2. Correct the odd `em` font sizing in all browsers. +*/ + +code, +kbd, +samp, +pre { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + /* 1 */ + font-size: 1em; + /* 2 */ +} + +/* +Add the correct font size in all browsers. +*/ + +small { + font-size: 80%; +} + +/* +Prevent `sub` and `sup` elements from affecting the line height in all browsers. +*/ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* +1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) +2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) +3. Remove gaps between table borders by default. +*/ + +table { + text-indent: 0; + /* 1 */ + border-color: inherit; + /* 2 */ + border-collapse: collapse; + /* 3 */ +} + +/* +1. Change the font styles in all browsers. +2. Remove the margin in Firefox and Safari. +3. Remove default padding in all browsers. +*/ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; + /* 1 */ + font-size: 100%; + /* 1 */ + font-weight: inherit; + /* 1 */ + line-height: inherit; + /* 1 */ + color: inherit; + /* 1 */ + margin: 0; + /* 2 */ + padding: 0; + /* 3 */ +} + +/* +Remove the inheritance of text transform in Edge and Firefox. +*/ + +button, +select { + text-transform: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Remove default button styles. +*/ + +button, +[type='button'], +[type='reset'], +[type='submit'] { + -webkit-appearance: button; + /* 1 */ + background-color: transparent; + /* 2 */ + background-image: none; + /* 2 */ +} + +/* +Use the modern Firefox focus style for all focusable elements. +*/ + +:-moz-focusring { + outline: auto; +} + +/* +Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) +*/ + +:-moz-ui-invalid { + box-shadow: none; +} + +/* +Add the correct vertical alignment in Chrome and Firefox. +*/ + +progress { + vertical-align: baseline; +} + +/* +Correct the cursor style of increment and decrement buttons in Safari. +*/ + +::-webkit-inner-spin-button, +::-webkit-outer-spin-button { + height: auto; +} + +/* +1. Correct the odd appearance in Chrome and Safari. +2. Correct the outline style in Safari. +*/ + +[type='search'] { + -webkit-appearance: textfield; + /* 1 */ + outline-offset: -2px; + /* 2 */ +} + +/* +Remove the inner padding in Chrome and Safari on macOS. +*/ + +::-webkit-search-decoration { + -webkit-appearance: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Change font properties to `inherit` in Safari. +*/ + +::-webkit-file-upload-button { + -webkit-appearance: button; + /* 1 */ + font: inherit; + /* 2 */ +} + +/* +Add the correct display in Chrome and Safari. +*/ + +summary { + display: list-item; +} + +/* +Removes the default spacing and border for appropriate elements. +*/ + +blockquote, +dl, +dd, +h1, +h2, +h3, +h4, +h5, +h6, +hr, +figure, +p, +pre { + margin: 0; +} + +fieldset { + margin: 0; + padding: 0; +} + +legend { + padding: 0; +} + +ol, +ul, +menu { + list-style: none; + margin: 0; + padding: 0; +} + +/* +Prevent resizing textareas horizontally by default. +*/ + +textarea { + resize: vertical; +} + +/* +1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) +2. Set the default placeholder color to the user's configured gray 400 color. +*/ + +input::-moz-placeholder, textarea::-moz-placeholder { + opacity: 1; + /* 1 */ + color: #9ca3af; + /* 2 */ +} + +input::placeholder, +textarea::placeholder { + opacity: 1; + /* 1 */ + color: #9ca3af; + /* 2 */ +} + +/* +Set the default cursor for buttons. +*/ + +button, +[role="button"] { + cursor: pointer; +} + +/* +Make sure disabled buttons don't get the pointer cursor. +*/ + +:disabled { + cursor: default; +} + +/* +1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) +2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) + This can trigger a poorly considered lint error in some tools but is included by design. +*/ + +img, +svg, +video, +canvas, +audio, +iframe, +embed, +object { + display: block; + /* 1 */ + vertical-align: middle; + /* 2 */ +} + +/* +Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) +*/ + +img, +video { + max-width: 100%; + height: auto; +} + +/* Make elements with the HTML hidden attribute stay hidden by default */ + +[hidden] { + display: none; +} + +*, ::before, ::after { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; +} + +::backdrop { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; +} + +.container { + width: 100%; +} + +@media (min-width: 640px) { + .container { + max-width: 640px; + } +} + +@media (min-width: 768px) { + .container { + max-width: 768px; + } +} + +@media (min-width: 1024px) { + .container { + max-width: 1024px; + } +} + +@media (min-width: 1280px) { + .container { + max-width: 1280px; + } +} + +@media (min-width: 1536px) { + .container { + max-width: 1536px; + } +} + +.mx-auto { + margin-left: auto; + margin-right: auto; +} + +.mb-16 { + margin-bottom: 4rem; +} + +.mb-4 { + margin-bottom: 1rem; +} + +.mb-8 { + margin-bottom: 2rem; +} + +.ml-1 { + margin-left: 0.25rem; +} + +.ml-3 { + margin-left: 0.75rem; +} + +.ml-4 { + margin-left: 1rem; +} + +.mr-5 { + margin-right: 1.25rem; +} + +.mt-4 { + margin-top: 1rem; +} + +.flex { + display: flex; +} + +.inline-flex { + display: inline-flex; +} + +.hidden { + display: none; +} + +.h-10 { + height: 2.5rem; +} + +.h-4 { + height: 1rem; +} + +.w-10 { + width: 2.5rem; +} + +.w-4 { + width: 1rem; +} + +.w-5\/6 { + width: 83.333333%; +} + +.flex-col { + flex-direction: column; +} + +.flex-wrap { + flex-wrap: wrap; +} + +.items-center { + align-items: center; +} + +.justify-center { + justify-content: center; +} + +.rounded { + border-radius: 0.25rem; +} + +.rounded-full { + border-radius: 9999px; +} + +.border-0 { + border-width: 0px; +} + +.bg-gray-800 { + --tw-bg-opacity: 1; + background-color: rgb(31 41 55 / var(--tw-bg-opacity)); +} + +.bg-gray-900 { + --tw-bg-opacity: 1; + background-color: rgb(17 24 39 / var(--tw-bg-opacity)); +} + +.bg-indigo-500 { + --tw-bg-opacity: 1; + background-color: rgb(99 102 241 / var(--tw-bg-opacity)); +} + +.object-cover { + -o-object-fit: cover; + object-fit: cover; +} + +.object-center { + -o-object-position: center; + object-position: center; +} + +.p-2 { + padding: 0.5rem; +} + +.p-5 { + padding: 1.25rem; +} + +.px-3 { + padding-left: 0.75rem; + padding-right: 0.75rem; +} + +.px-5 { + padding-left: 1.25rem; + padding-right: 1.25rem; +} + +.px-6 { + padding-left: 1.5rem; + padding-right: 1.5rem; +} + +.py-1 { + padding-top: 0.25rem; + padding-bottom: 0.25rem; +} + +.py-2 { + padding-top: 0.5rem; + padding-bottom: 0.5rem; +} + +.py-24 { + padding-top: 6rem; + padding-bottom: 6rem; +} + +.text-center { + text-align: center; +} + +.text-3xl { + font-size: 1.875rem; + line-height: 2.25rem; +} + +.text-base { + font-size: 1rem; + line-height: 1.5rem; +} + +.text-lg { + font-size: 1.125rem; + line-height: 1.75rem; +} + +.text-xl { + font-size: 1.25rem; + line-height: 1.75rem; +} + +.font-medium { + font-weight: 500; +} + +.leading-relaxed { + line-height: 1.625; +} + +.text-gray-400 { + --tw-text-opacity: 1; + color: rgb(156 163 175 / var(--tw-text-opacity)); +} + +.text-white { + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); +} + +.hover\:bg-gray-700:hover { + --tw-bg-opacity: 1; + background-color: rgb(55 65 81 / var(--tw-bg-opacity)); +} + +.hover\:bg-indigo-600:hover { + --tw-bg-opacity: 1; + background-color: rgb(79 70 229 / var(--tw-bg-opacity)); +} + +.hover\:text-white:hover { + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); +} + +.focus\:outline-none:focus { + outline: 2px solid transparent; + outline-offset: 2px; +} + +@media (min-width: 640px) { + .sm\:text-4xl { + font-size: 2.25rem; + line-height: 2.5rem; + } +} + +@media (min-width: 768px) { + .md\:mb-0 { + margin-bottom: 0px; + } + + .md\:ml-auto { + margin-left: auto; + } + + .md\:mt-0 { + margin-top: 0px; + } + + .md\:w-1\/2 { + width: 50%; + } + + .md\:flex-row { + flex-direction: row; + } + + .md\:items-start { + align-items: flex-start; + } + + .md\:pr-16 { + padding-right: 4rem; + } + + .md\:text-left { + text-align: left; + } +} + +@media (min-width: 1024px) { + .lg\:inline-block { + display: inline-block; + } + + .lg\:w-full { + width: 100%; + } + + .lg\:max-w-lg { + max-width: 32rem; + } + + .lg\:flex-grow { + flex-grow: 1; + } + + .lg\:pr-24 { + padding-right: 6rem; + } +} \ No newline at end of file diff --git a/examples/tailwind/src/main.rs b/examples/tailwind/src/main.rs new file mode 100644 index 0000000000..70ce484c38 --- /dev/null +++ b/examples/tailwind/src/main.rs @@ -0,0 +1,103 @@ +#![allow(non_snake_case)] + +use dioxus::prelude::*; + +const _STYLE: &str = asset!("public/tailwind.css"); + +fn main() { + launch(app); +} + +pub fn app() -> Element { + let grey_background = true; + rsx!( + div { + header { + class: "text-gray-400 body-font", + // you can use optional attributes to optionally apply a tailwind class + class: if grey_background { + "bg-gray-900" + }, + div { class: "container mx-auto flex flex-wrap p-5 flex-col md:flex-row items-center", + a { class: "flex title-font font-medium items-center text-white mb-4 md:mb-0", + StacksIcon {} + span { class: "ml-3 text-xl", "Hello Dioxus!" } + } + nav { class: "md:ml-auto flex flex-wrap items-center text-base justify-center", + a { class: "mr-5 hover:text-white", "First Link" } + a { class: "mr-5 hover:text-white", "Second Link" } + a { class: "mr-5 hover:text-white", "Third Link" } + a { class: "mr-5 hover:text-white", "Fourth Link" } + } + button { class: "inline-flex items-center bg-gray-800 border-0 py-1 px-3 focus:outline-none hover:bg-gray-700 rounded text-base mt-4 md:mt-0", + "Button" + RightArrowIcon {} + } + } + } + + section { class: "text-gray-400 bg-gray-900 body-font", + div { class: "container mx-auto flex px-5 py-24 md:flex-row flex-col items-center", + div { class: "lg:flex-grow md:w-1/2 lg:pr-24 md:pr-16 flex flex-col md:items-start md:text-left mb-16 md:mb-0 items-center text-center", + h1 { class: "title-font sm:text-4xl text-3xl mb-4 font-medium text-white", + br { class: "hidden lg:inline-block" } + "Dioxus Sneak Peek" + } + p { class: "mb-8 leading-relaxed", + + "Dioxus is a new UI framework that makes it easy and simple to write cross-platform apps using web + technologies! It is functional, fast, and portable. Dioxus can run on the web, on the desktop, and + on mobile and embedded platforms." + } + div { class: "flex justify-center", + button { class: "inline-flex text-white bg-indigo-500 border-0 py-2 px-6 focus:outline-none hover:bg-indigo-600 rounded text-lg", + "Learn more" + } + button { class: "ml-4 inline-flex text-gray-400 bg-gray-800 border-0 py-2 px-6 focus:outline-none hover:bg-gray-700 hover:text-white rounded text-lg", + "Build an app" + } + } + } + div { class: "lg:max-w-lg lg:w-full md:w-1/2 w-5/6", + img { + class: "object-cover object-center rounded", + src: "https://i.imgur.com/oK6BLtw.png", + referrerpolicy: "no-referrer", + alt: "hero" + } + } + } + } + } + ) +} + +pub fn StacksIcon() -> Element { + rsx!( + svg { + fill: "none", + stroke: "currentColor", + stroke_linecap: "round", + stroke_linejoin: "round", + stroke_width: "2", + class: "w-10 h-10 text-white p-2 bg-indigo-500 rounded-full", + view_box: "0 0 24 24", + path { d: "M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5" } + } + ) +} + +pub fn RightArrowIcon() -> Element { + rsx!( + svg { + fill: "none", + stroke: "currentColor", + stroke_linecap: "round", + stroke_linejoin: "round", + stroke_width: "2", + class: "w-4 h-4 ml-1", + view_box: "0 0 24 24", + path { d: "M5 12h14M12 5l7 7-7 7" } + } + ) +} diff --git a/examples/tailwind/tailwind.config.js b/examples/tailwind/tailwind.config.js new file mode 100644 index 0000000000..2a69d5803a --- /dev/null +++ b/examples/tailwind/tailwind.config.js @@ -0,0 +1,9 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + mode: "all", + content: ["./src/**/*.{rs,html,css}", "./dist/**/*.html"], + theme: { + extend: {}, + }, + plugins: [], +}; diff --git a/examples/title.rs b/examples/title.rs index f836a7f3d6..ebe258963c 100644 --- a/examples/title.rs +++ b/examples/title.rs @@ -3,7 +3,8 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + tracing_subscriber::fmt::init(); + launch(app); } fn app() -> Element { @@ -13,7 +14,7 @@ fn app() -> Element { div { // You can set the title of the page with the Title component // In web applications, this sets the title in the head. On desktop, it sets the window title - document::Title { "My Application (Count {count})" } + Title { "My Application (Count {count})" } button { onclick: move |_| count += 1, "Up high!" } button { onclick: move |_| count -= 1, "Down low!" } } diff --git a/examples/todomvc.rs b/examples/todomvc.rs index a95fda0e2d..ea2e528a12 100644 --- a/examples/todomvc.rs +++ b/examples/todomvc.rs @@ -3,10 +3,10 @@ use dioxus::prelude::*; use std::collections::HashMap; -const STYLE: Asset = asset!("/examples/assets/todomvc.css"); +const STYLE: &str = asset!("./examples/assets/todomvc.css"); fn main() { - dioxus::launch(app); + launch(app); } #[derive(PartialEq, Eq, Clone, Copy)] @@ -63,7 +63,7 @@ fn app() -> Element { }; rsx! { - document::Stylesheet { href: STYLE } + head::Link { rel: "stylesheet", href: STYLE } section { class: "todoapp", TodoHeader { todos } section { class: "main", diff --git a/examples/weather_app.rs b/examples/weather_app.rs index 7b56bbbe54..395fd81f19 100644 --- a/examples/weather_app.rs +++ b/examples/weather_app.rs @@ -4,7 +4,7 @@ use dioxus::prelude::*; use serde::{Deserialize, Serialize}; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { @@ -19,7 +19,7 @@ fn app() -> Element { let current_weather = use_resource(move || async move { get_weather(&country()).await }); rsx! { - document::Stylesheet { href: "https://unpkg.com/tailwindcss@^2.0/dist/tailwind.min.css" } + head::Link { rel: "stylesheet", href: "https://unpkg.com/tailwindcss@^2.0/dist/tailwind.min.css" } div { class: "mx-auto p-4 bg-gray-100 h-screen flex justify-center", div { class: "flex items-center justify-center flex-row", div { class: "flex items-start justify-center flex-row", diff --git a/examples/web_component.rs b/examples/web_component.rs index a5f831912b..df0515397b 100644 --- a/examples/web_component.rs +++ b/examples/web_component.rs @@ -7,7 +7,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/window_event.rs b/examples/window_event.rs index ca126f6a1d..cb91d2a5b2 100644 --- a/examples/window_event.rs +++ b/examples/window_event.rs @@ -13,7 +13,7 @@ use dioxus::desktop::{window, Config, WindowBuilder}; use dioxus::prelude::*; fn main() { - dioxus::launch::builder() + LaunchBuilder::desktop() .with_cfg( Config::new().with_window( WindowBuilder::new() @@ -26,7 +26,7 @@ fn main() { fn app() -> Element { rsx!( - document::Link { href: "https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css", rel: "stylesheet" } + head::Link { href: "https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css", rel: "stylesheet" } Header {} div { class: "container mx-auto", div { class: "grid grid-cols-5", diff --git a/examples/window_focus.rs b/examples/window_focus.rs index 1213b3e994..447544077b 100644 --- a/examples/window_focus.rs +++ b/examples/window_focus.rs @@ -12,7 +12,7 @@ use dioxus::desktop::{Config, WindowCloseBehaviour}; use dioxus::prelude::*; fn main() { - dioxus::launch::builder() + LaunchBuilder::desktop() .with_cfg(Config::new().with_close_behaviour(WindowCloseBehaviour::CloseWindow)) .launch(app) } diff --git a/examples/window_zoom.rs b/examples/window_zoom.rs index 35d25f4fde..8b10d503b0 100644 --- a/examples/window_zoom.rs +++ b/examples/window_zoom.rs @@ -5,7 +5,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch_desktop(app); } fn app() -> Element { diff --git a/examples/xss_safety.rs b/examples/xss_safety.rs index 904a42b07b..cb8d414dd7 100644 --- a/examples/xss_safety.rs +++ b/examples/xss_safety.rs @@ -5,7 +5,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { From 356b487919dc19752194d6e2d74e86fb7322327b Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Sun, 15 Sep 2024 18:11:18 -0700 Subject: [PATCH 110/139] revert changes to examples --- examples/PWA-example/Cargo.toml | 17 - examples/PWA-example/Dioxus.toml | 27 - examples/PWA-example/LICENSE | 21 - examples/PWA-example/README.md | 45 - examples/PWA-example/index.html | 30 - examples/PWA-example/public/favicon.ico | Bin 23104 -> 0 bytes examples/PWA-example/public/logo_192.png | Bin 11198 -> 0 bytes examples/PWA-example/public/logo_512.png | Bin 48151 -> 0 bytes examples/PWA-example/public/manifest.json | 34 - examples/PWA-example/public/sw.js | 198 - examples/PWA-example/src/main.rs | 21 - examples/all_events.rs | 6 +- examples/assets/purecss.css | 12 + examples/backgrounded_futures.rs | 2 +- examples/calculator.rs | 6 +- examples/calculator_mutable.rs | 4 +- examples/clock.rs | 10 +- examples/control_focus.rs | 9 +- examples/counters.rs | 6 +- examples/crm.rs | 11 +- examples/custom_assets.rs | 32 +- examples/custom_html.rs | 2 +- examples/custom_menu.rs | 2 +- examples/disabled.rs | 2 +- examples/dog_app.rs | 2 +- examples/dynamic_asset.rs | 6 +- examples/errors.rs | 36 +- examples/eval.rs | 29 +- examples/file_explorer.rs | 8 +- examples/file_upload.rs | 6 +- examples/flat_router.rs | 6 +- examples/form.rs | 2 +- examples/future.rs | 2 +- examples/generic_component.rs | 2 +- examples/global.rs | 6 +- examples/hash_fragment_state.rs | 4 +- examples/hello_world.rs | 2 +- examples/hydration.rs | 2 +- examples/image_generator_openai.rs | 4 +- examples/link.rs | 6 +- examples/login_form.rs | 2 +- examples/memo_chain.rs | 2 +- examples/meta.rs | 13 +- examples/mobile_demo/.gitignore | 10 - examples/mobile_demo/Cargo.lock | 4007 ----------- examples/mobile_demo/Cargo.toml | 51 - examples/mobile_demo/README.md | 11 - examples/mobile_demo/mobile.toml | 8 - examples/mobile_demo/src/index.html | 12 - examples/mobile_demo/src/lib.rs | 90 - examples/multiwindow.rs | 2 +- examples/nested_listeners.rs | 2 +- .../openid_connect_demo/.cargo/config.toml | 5 - examples/openid_connect_demo/.gitignore | 3 - examples/openid_connect_demo/Cargo.lock | 6372 ----------------- examples/openid_connect_demo/Cargo.toml | 36 - examples/openid_connect_demo/Dioxus.toml | 35 - examples/openid_connect_demo/README.md | 12 - examples/openid_connect_demo/src/constants.rs | 2 - examples/openid_connect_demo/src/main.rs | 36 - examples/openid_connect_demo/src/model/mod.rs | 1 - .../openid_connect_demo/src/model/user.rs | 7 - examples/openid_connect_demo/src/oidc.rs | 127 - .../openid_connect_demo/src/props/client.rs | 20 - examples/openid_connect_demo/src/props/mod.rs | 1 - examples/openid_connect_demo/src/router.rs | 16 - examples/openid_connect_demo/src/storage.rs | 35 - .../openid_connect_demo/src/views/header.rs | 221 - .../openid_connect_demo/src/views/home.rs | 5 - .../openid_connect_demo/src/views/login.rs | 86 - examples/openid_connect_demo/src/views/mod.rs | 4 - .../src/views/not_found.rs | 10 - examples/optional_props.rs | 2 +- examples/overlay.rs | 8 +- examples/popup.rs | 2 +- examples/query_segment_search.rs | 6 +- examples/read_size.rs | 4 +- examples/readme.rs | 2 +- examples/reducer.rs | 6 +- examples/resize.rs | 4 +- examples/router.rs | 6 +- examples/router_resource.rs | 2 +- examples/rsx_usage.rs | 5 +- examples/scroll_to_top.rs | 2 +- examples/shortcut.rs | 2 +- examples/shorthand.rs | 2 +- examples/signals.rs | 2 +- examples/simple_list.rs | 2 +- examples/simple_router.rs | 2 +- examples/streams.rs | 2 +- examples/suspense.rs | 2 +- examples/svg.rs | 2 +- examples/tailwind/.gitignore | 1 - examples/tailwind/Cargo.toml | 20 - examples/tailwind/Dioxus.toml | 27 - examples/tailwind/README.md | 7 - examples/tailwind/input.css | 3 - examples/tailwind/public/tailwind.css | 833 --- examples/tailwind/src/main.rs | 103 - examples/tailwind/tailwind.config.js | 9 - examples/title.rs | 5 +- examples/todomvc.rs | 6 +- examples/weather_app.rs | 4 +- examples/web_component.rs | 2 +- examples/window_event.rs | 4 +- examples/window_focus.rs | 2 +- examples/window_zoom.rs | 2 +- examples/xss_safety.rs | 2 +- 108 files changed, 164 insertions(+), 12795 deletions(-) delete mode 100644 examples/PWA-example/Cargo.toml delete mode 100644 examples/PWA-example/Dioxus.toml delete mode 100644 examples/PWA-example/LICENSE delete mode 100644 examples/PWA-example/README.md delete mode 100644 examples/PWA-example/index.html delete mode 100644 examples/PWA-example/public/favicon.ico delete mode 100644 examples/PWA-example/public/logo_192.png delete mode 100644 examples/PWA-example/public/logo_512.png delete mode 100644 examples/PWA-example/public/manifest.json delete mode 100644 examples/PWA-example/public/sw.js delete mode 100644 examples/PWA-example/src/main.rs create mode 100644 examples/assets/purecss.css delete mode 100644 examples/mobile_demo/.gitignore delete mode 100644 examples/mobile_demo/Cargo.lock delete mode 100644 examples/mobile_demo/Cargo.toml delete mode 100644 examples/mobile_demo/README.md delete mode 100644 examples/mobile_demo/mobile.toml delete mode 100644 examples/mobile_demo/src/index.html delete mode 100644 examples/mobile_demo/src/lib.rs delete mode 100644 examples/openid_connect_demo/.cargo/config.toml delete mode 100644 examples/openid_connect_demo/.gitignore delete mode 100644 examples/openid_connect_demo/Cargo.lock delete mode 100644 examples/openid_connect_demo/Cargo.toml delete mode 100644 examples/openid_connect_demo/Dioxus.toml delete mode 100644 examples/openid_connect_demo/README.md delete mode 100644 examples/openid_connect_demo/src/constants.rs delete mode 100644 examples/openid_connect_demo/src/main.rs delete mode 100644 examples/openid_connect_demo/src/model/mod.rs delete mode 100644 examples/openid_connect_demo/src/model/user.rs delete mode 100644 examples/openid_connect_demo/src/oidc.rs delete mode 100644 examples/openid_connect_demo/src/props/client.rs delete mode 100644 examples/openid_connect_demo/src/props/mod.rs delete mode 100644 examples/openid_connect_demo/src/router.rs delete mode 100644 examples/openid_connect_demo/src/storage.rs delete mode 100644 examples/openid_connect_demo/src/views/header.rs delete mode 100644 examples/openid_connect_demo/src/views/home.rs delete mode 100644 examples/openid_connect_demo/src/views/login.rs delete mode 100644 examples/openid_connect_demo/src/views/mod.rs delete mode 100644 examples/openid_connect_demo/src/views/not_found.rs delete mode 100644 examples/tailwind/.gitignore delete mode 100644 examples/tailwind/Cargo.toml delete mode 100644 examples/tailwind/Dioxus.toml delete mode 100644 examples/tailwind/README.md delete mode 100644 examples/tailwind/input.css delete mode 100644 examples/tailwind/public/tailwind.css delete mode 100644 examples/tailwind/src/main.rs delete mode 100644 examples/tailwind/tailwind.config.js diff --git a/examples/PWA-example/Cargo.toml b/examples/PWA-example/Cargo.toml deleted file mode 100644 index bee204f20a..0000000000 --- a/examples/PWA-example/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "dioxus-pwa-example" -version = "0.1.0" -authors = ["Antonio Curavalea "] -edition = "2021" -publish = false - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -dioxus = { workspace = true, features = ["web"] } - -log = "0.4.6" - -# WebAssembly Debug -wasm-logger = "0.2.0" -console_error_panic_hook = "0.1.7" diff --git a/examples/PWA-example/Dioxus.toml b/examples/PWA-example/Dioxus.toml deleted file mode 100644 index a831a28ff7..0000000000 --- a/examples/PWA-example/Dioxus.toml +++ /dev/null @@ -1,27 +0,0 @@ -[application] - -# App (Project) Name -name = "dioxus-pwa-example" - -# Dioxus App Default Platform -# desktop, web, mobile, ssr -default_platform = "web" - -# `build` & `serve` dist path -out_dir = "dist" - -# resource (public) file folder -asset_dir = "public" - -[web.app] - -# HTML title tag content -title = "dioxus | ⛺" - -[web.watcher] - -# when watcher trigger, regenerate the `index.html` -reload_html = true - -# which files or dirs will be watcher monitoring -watch_path = ["src", "public"] diff --git a/examples/PWA-example/LICENSE b/examples/PWA-example/LICENSE deleted file mode 100644 index bcdd828e9c..0000000000 --- a/examples/PWA-example/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2022 Dioxus - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/examples/PWA-example/README.md b/examples/PWA-example/README.md deleted file mode 100644 index d501df2126..0000000000 --- a/examples/PWA-example/README.md +++ /dev/null @@ -1,45 +0,0 @@ -# Dioxus PWA example - -This is a basic example of a progressive web app (PWA) using Dioxus and Dioxus CLI. -Currently PWA functionality requires the use of a service worker and manifest file, so this isn't 100% Rust yet. - -It is also very much usable as a template for your projects, if you're aiming to create a PWA. - -## Try the example - -Make sure you have Dioxus CLI installed (if you're unsure, run `cargo install dioxus-cli --locked`). - -You can run `dx serve` in this directory to start the web server locally, or run -`dx build --release` to build the project so you can deploy it on a separate web-server. - -## Project Structure - -``` -├── Cargo.toml -├── Dioxus.toml -├── index.html // Custom HTML is needed for this, to load the SW and manifest. -├── LICENSE -├── public -│ ├── favicon.ico -│ ├── logo_192.png -│ ├── logo_512.png -│ ├── manifest.json // The manifest file - edit this as you need to. -│ └── sw.js // The service worker - you must edit this for actual projects. -├── README.md -└── src - └── main.rs -``` - -## Resources - -If you're just getting started with PWAs, here are some useful resources: - -- [PWABuilder docs](https://docs.pwabuilder.com/#/) -- [MDN article on PWAs](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps) - -For service worker scripting (in JavaScript): - -- [Service worker guide from PWABuilder](https://docs.pwabuilder.com/#/home/sw-intro) -- [Service worker examples, also from PWABuilder](https://github.com/pwa-builder/pwabuilder-serviceworkers) - -If you want to stay as close to 100% Rust as possible, you can try using [wasi-worker](https://github.com/dunnock/wasi-worker) to replace the JS service worker file. The JSON manifest will still be required though. diff --git a/examples/PWA-example/index.html b/examples/PWA-example/index.html deleted file mode 100644 index 44bfa59cc4..0000000000 --- a/examples/PWA-example/index.html +++ /dev/null @@ -1,30 +0,0 @@ - - - - {app_title} - - - - - - {style_include} - - -
      - - {script_include} - - \ No newline at end of file diff --git a/examples/PWA-example/public/favicon.ico b/examples/PWA-example/public/favicon.ico deleted file mode 100644 index b11015bdbacdb3b99e03555edb85d78467b37c7d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23104 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 W+`s-{ME)24jHW23DqAIO8vH-~t)grI diff --git a/examples/PWA-example/public/logo_192.png b/examples/PWA-example/public/logo_192.png deleted file mode 100644 index 112eb5ee49ca4e69d742a99269d1ae08a970dd7a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11198 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 diff --git a/examples/PWA-example/public/logo_512.png b/examples/PWA-example/public/logo_512.png deleted file mode 100644 index 43129772a892a5ca82dfdc8bb4d9027cc830ddf5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 48151 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 Unavailable', { - status: 503, - statusText: 'Service Unavailable', - headers: new Headers({ - 'Content-Type': 'text/html' - }) - }); - } - }) - ); -}); - -/* The activate event fires after a service worker has been successfully installed. - It is most useful when phasing out an older version of a service worker, as at - this point you know that the new worker was installed correctly. In this example, - we delete old caches that don't match the version in the worker we just finished - installing. -*/ -self.addEventListener("activate", function (event) { - /* Just like with the install event, event.waitUntil blocks activate on a promise. - Activation will fail unless the promise is fulfilled. - */ - //console.log('WORKER: activate event in progress.'); - - event.waitUntil( - caches - /* This method returns a promise which will resolve to an array of available - cache keys. - */ - .keys() - .then(function (keys) { - // We return a promise that settles when all outdated caches are deleted. - return Promise.all( - keys - .filter(function (key) { - // Filter by keys that don't start with the latest version prefix. - return !key.startsWith(version); - }) - .map(function (key) { - /* Return a promise that's fulfilled - when each outdated cache is deleted. - */ - return caches.delete(key); - }) - ); - }) - .then(function () { - //console.log('WORKER: activate completed.'); - }) - ); -}); diff --git a/examples/PWA-example/src/main.rs b/examples/PWA-example/src/main.rs deleted file mode 100644 index 3e4d5b2361..0000000000 --- a/examples/PWA-example/src/main.rs +++ /dev/null @@ -1,21 +0,0 @@ -use dioxus::prelude::*; - -fn main() { - // init debug tool for WebAssembly - wasm_logger::init(wasm_logger::Config::default()); - console_error_panic_hook::set_once(); - - launch(app); -} - -fn app() -> Element { - rsx! ( - div { style: "text-align: center;", - h1 { "🌗 Dioxus 🚀" } - h3 { "Frontend that scales." } - p { - "Dioxus is a portable, performant, and ergonomic framework for building cross-platform user interfaces in Rust." - } - } - ) -} diff --git a/examples/all_events.rs b/examples/all_events.rs index 2a04ee2bb3..4e4ca3d6d5 100644 --- a/examples/all_events.rs +++ b/examples/all_events.rs @@ -6,10 +6,8 @@ use dioxus::prelude::*; use std::{collections::VecDeque, fmt::Debug, rc::Rc}; -const STYLE: &str = asset!("./examples/assets/events.css"); - fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { @@ -26,7 +24,7 @@ fn app() -> Element { }; rsx! { - head::Link { rel: "stylesheet", href: STYLE } + document::Stylesheet { href: asset!("/examples/assets/events.css") } div { id: "container", // focusing is necessary to catch keyboard events div { id: "receiver", tabindex: 0, diff --git a/examples/assets/purecss.css b/examples/assets/purecss.css new file mode 100644 index 0000000000..dd301cc364 --- /dev/null +++ b/examples/assets/purecss.css @@ -0,0 +1,12 @@ +/*! +Pure v2.0.6 +Copyright 2013 Yahoo! +Licensed under the BSD License. +https://github.com/pure-css/pure/blob/master/LICENSE +*/ +/*! +normalize.css v | MIT License | git.io/normalize +Copyright (c) Nicolas Gallagher and Jonathan Neal +*/ +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ +html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{-webkit-box-sizing:content-box;box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{-webkit-box-sizing:border-box;box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{-webkit-box-sizing:border-box;box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}html{font-family:sans-serif}.hidden,[hidden]{display:none!important}.pure-img{max-width:100%;height:auto;display:block}.pure-g{letter-spacing:-.31em;text-rendering:optimizespeed;font-family:FreeSans,Arimo,"Droid Sans",Helvetica,Arial,sans-serif;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-line-pack:start;align-content:flex-start}@media all and (-ms-high-contrast:none),(-ms-high-contrast:active){table .pure-g{display:block}}.opera-only :-o-prefocus,.pure-g{word-spacing:-0.43em}.pure-u{display:inline-block;letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-g [class*=pure-u]{font-family:sans-serif}.pure-u-1,.pure-u-1-1,.pure-u-1-12,.pure-u-1-2,.pure-u-1-24,.pure-u-1-3,.pure-u-1-4,.pure-u-1-5,.pure-u-1-6,.pure-u-1-8,.pure-u-10-24,.pure-u-11-12,.pure-u-11-24,.pure-u-12-24,.pure-u-13-24,.pure-u-14-24,.pure-u-15-24,.pure-u-16-24,.pure-u-17-24,.pure-u-18-24,.pure-u-19-24,.pure-u-2-24,.pure-u-2-3,.pure-u-2-5,.pure-u-20-24,.pure-u-21-24,.pure-u-22-24,.pure-u-23-24,.pure-u-24-24,.pure-u-3-24,.pure-u-3-4,.pure-u-3-5,.pure-u-3-8,.pure-u-4-24,.pure-u-4-5,.pure-u-5-12,.pure-u-5-24,.pure-u-5-5,.pure-u-5-6,.pure-u-5-8,.pure-u-6-24,.pure-u-7-12,.pure-u-7-24,.pure-u-7-8,.pure-u-8-24,.pure-u-9-24{display:inline-block;letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-u-1-24{width:4.1667%}.pure-u-1-12,.pure-u-2-24{width:8.3333%}.pure-u-1-8,.pure-u-3-24{width:12.5%}.pure-u-1-6,.pure-u-4-24{width:16.6667%}.pure-u-1-5{width:20%}.pure-u-5-24{width:20.8333%}.pure-u-1-4,.pure-u-6-24{width:25%}.pure-u-7-24{width:29.1667%}.pure-u-1-3,.pure-u-8-24{width:33.3333%}.pure-u-3-8,.pure-u-9-24{width:37.5%}.pure-u-2-5{width:40%}.pure-u-10-24,.pure-u-5-12{width:41.6667%}.pure-u-11-24{width:45.8333%}.pure-u-1-2,.pure-u-12-24{width:50%}.pure-u-13-24{width:54.1667%}.pure-u-14-24,.pure-u-7-12{width:58.3333%}.pure-u-3-5{width:60%}.pure-u-15-24,.pure-u-5-8{width:62.5%}.pure-u-16-24,.pure-u-2-3{width:66.6667%}.pure-u-17-24{width:70.8333%}.pure-u-18-24,.pure-u-3-4{width:75%}.pure-u-19-24{width:79.1667%}.pure-u-4-5{width:80%}.pure-u-20-24,.pure-u-5-6{width:83.3333%}.pure-u-21-24,.pure-u-7-8{width:87.5%}.pure-u-11-12,.pure-u-22-24{width:91.6667%}.pure-u-23-24{width:95.8333%}.pure-u-1,.pure-u-1-1,.pure-u-24-24,.pure-u-5-5{width:100%}.pure-button{display:inline-block;line-height:normal;white-space:nowrap;vertical-align:middle;text-align:center;cursor:pointer;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-box-sizing:border-box;box-sizing:border-box}.pure-button::-moz-focus-inner{padding:0;border:0}.pure-button-group{letter-spacing:-.31em;text-rendering:optimizespeed}.opera-only :-o-prefocus,.pure-button-group{word-spacing:-0.43em}.pure-button-group .pure-button{letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-button{font-family:inherit;font-size:100%;padding:.5em 1em;color:rgba(0,0,0,.8);border:none transparent;background-color:#e6e6e6;text-decoration:none;border-radius:2px}.pure-button-hover,.pure-button:focus,.pure-button:hover{background-image:-webkit-gradient(linear,left top,left bottom,from(transparent),color-stop(40%,rgba(0,0,0,.05)),to(rgba(0,0,0,.1)));background-image:linear-gradient(transparent,rgba(0,0,0,.05) 40%,rgba(0,0,0,.1))}.pure-button:focus{outline:0}.pure-button-active,.pure-button:active{-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.15) inset,0 0 6px rgba(0,0,0,.2) inset;box-shadow:0 0 0 1px rgba(0,0,0,.15) inset,0 0 6px rgba(0,0,0,.2) inset;border-color:#000}.pure-button-disabled,.pure-button-disabled:active,.pure-button-disabled:focus,.pure-button-disabled:hover,.pure-button[disabled]{border:none;background-image:none;opacity:.4;cursor:not-allowed;-webkit-box-shadow:none;box-shadow:none;pointer-events:none}.pure-button-hidden{display:none}.pure-button-primary,.pure-button-selected,a.pure-button-primary,a.pure-button-selected{background-color:#0078e7;color:#fff}.pure-button-group .pure-button{margin:0;border-radius:0;border-right:1px solid rgba(0,0,0,.2)}.pure-button-group .pure-button:first-child{border-top-left-radius:2px;border-bottom-left-radius:2px}.pure-button-group .pure-button:last-child{border-top-right-radius:2px;border-bottom-right-radius:2px;border-right:none}.pure-form input[type=color],.pure-form input[type=date],.pure-form input[type=datetime-local],.pure-form input[type=datetime],.pure-form input[type=email],.pure-form input[type=month],.pure-form input[type=number],.pure-form input[type=password],.pure-form input[type=search],.pure-form input[type=tel],.pure-form input[type=text],.pure-form input[type=time],.pure-form input[type=url],.pure-form input[type=week],.pure-form select,.pure-form textarea{padding:.5em .6em;display:inline-block;border:1px solid #ccc;-webkit-box-shadow:inset 0 1px 3px #ddd;box-shadow:inset 0 1px 3px #ddd;border-radius:4px;vertical-align:middle;-webkit-box-sizing:border-box;box-sizing:border-box}.pure-form input:not([type]){padding:.5em .6em;display:inline-block;border:1px solid #ccc;-webkit-box-shadow:inset 0 1px 3px #ddd;box-shadow:inset 0 1px 3px #ddd;border-radius:4px;-webkit-box-sizing:border-box;box-sizing:border-box}.pure-form input[type=color]{padding:.2em .5em}.pure-form input[type=color]:focus,.pure-form input[type=date]:focus,.pure-form input[type=datetime-local]:focus,.pure-form input[type=datetime]:focus,.pure-form input[type=email]:focus,.pure-form input[type=month]:focus,.pure-form input[type=number]:focus,.pure-form input[type=password]:focus,.pure-form input[type=search]:focus,.pure-form input[type=tel]:focus,.pure-form input[type=text]:focus,.pure-form input[type=time]:focus,.pure-form input[type=url]:focus,.pure-form input[type=week]:focus,.pure-form select:focus,.pure-form textarea:focus{outline:0;border-color:#129fea}.pure-form input:not([type]):focus{outline:0;border-color:#129fea}.pure-form input[type=checkbox]:focus,.pure-form input[type=file]:focus,.pure-form input[type=radio]:focus{outline:thin solid #129FEA;outline:1px auto #129FEA}.pure-form .pure-checkbox,.pure-form .pure-radio{margin:.5em 0;display:block}.pure-form input[type=color][disabled],.pure-form input[type=date][disabled],.pure-form input[type=datetime-local][disabled],.pure-form input[type=datetime][disabled],.pure-form input[type=email][disabled],.pure-form input[type=month][disabled],.pure-form input[type=number][disabled],.pure-form input[type=password][disabled],.pure-form input[type=search][disabled],.pure-form input[type=tel][disabled],.pure-form input[type=text][disabled],.pure-form input[type=time][disabled],.pure-form input[type=url][disabled],.pure-form input[type=week][disabled],.pure-form select[disabled],.pure-form textarea[disabled]{cursor:not-allowed;background-color:#eaeded;color:#cad2d3}.pure-form input:not([type])[disabled]{cursor:not-allowed;background-color:#eaeded;color:#cad2d3}.pure-form input[readonly],.pure-form select[readonly],.pure-form textarea[readonly]{background-color:#eee;color:#777;border-color:#ccc}.pure-form input:focus:invalid,.pure-form select:focus:invalid,.pure-form textarea:focus:invalid{color:#b94a48;border-color:#e9322d}.pure-form input[type=checkbox]:focus:invalid:focus,.pure-form input[type=file]:focus:invalid:focus,.pure-form input[type=radio]:focus:invalid:focus{outline-color:#e9322d}.pure-form select{height:2.25em;border:1px solid #ccc;background-color:#fff}.pure-form select[multiple]{height:auto}.pure-form label{margin:.5em 0 .2em}.pure-form fieldset{margin:0;padding:.35em 0 .75em;border:0}.pure-form legend{display:block;width:100%;padding:.3em 0;margin-bottom:.3em;color:#333;border-bottom:1px solid #e5e5e5}.pure-form-stacked input[type=color],.pure-form-stacked input[type=date],.pure-form-stacked input[type=datetime-local],.pure-form-stacked input[type=datetime],.pure-form-stacked input[type=email],.pure-form-stacked input[type=file],.pure-form-stacked input[type=month],.pure-form-stacked input[type=number],.pure-form-stacked input[type=password],.pure-form-stacked input[type=search],.pure-form-stacked input[type=tel],.pure-form-stacked input[type=text],.pure-form-stacked input[type=time],.pure-form-stacked input[type=url],.pure-form-stacked input[type=week],.pure-form-stacked label,.pure-form-stacked select,.pure-form-stacked textarea{display:block;margin:.25em 0}.pure-form-stacked input:not([type]){display:block;margin:.25em 0}.pure-form-aligned input,.pure-form-aligned select,.pure-form-aligned textarea,.pure-form-message-inline{display:inline-block;vertical-align:middle}.pure-form-aligned textarea{vertical-align:top}.pure-form-aligned .pure-control-group{margin-bottom:.5em}.pure-form-aligned .pure-control-group label{text-align:right;display:inline-block;vertical-align:middle;width:10em;margin:0 1em 0 0}.pure-form-aligned .pure-controls{margin:1.5em 0 0 11em}.pure-form .pure-input-rounded,.pure-form input.pure-input-rounded{border-radius:2em;padding:.5em 1em}.pure-form .pure-group fieldset{margin-bottom:10px}.pure-form .pure-group input,.pure-form .pure-group textarea{display:block;padding:10px;margin:0 0 -1px;border-radius:0;position:relative;top:-1px}.pure-form .pure-group input:focus,.pure-form .pure-group textarea:focus{z-index:3}.pure-form .pure-group input:first-child,.pure-form .pure-group textarea:first-child{top:1px;border-radius:4px 4px 0 0;margin:0}.pure-form .pure-group input:first-child:last-child,.pure-form .pure-group textarea:first-child:last-child{top:1px;border-radius:4px;margin:0}.pure-form .pure-group input:last-child,.pure-form .pure-group textarea:last-child{top:-2px;border-radius:0 0 4px 4px;margin:0}.pure-form .pure-group button{margin:.35em 0}.pure-form .pure-input-1{width:100%}.pure-form .pure-input-3-4{width:75%}.pure-form .pure-input-2-3{width:66%}.pure-form .pure-input-1-2{width:50%}.pure-form .pure-input-1-3{width:33%}.pure-form .pure-input-1-4{width:25%}.pure-form-message-inline{display:inline-block;padding-left:.3em;color:#666;vertical-align:middle;font-size:.875em}.pure-form-message{display:block;color:#666;font-size:.875em}@media only screen and (max-width :480px){.pure-form button[type=submit]{margin:.7em 0 0}.pure-form input:not([type]),.pure-form input[type=color],.pure-form input[type=date],.pure-form input[type=datetime-local],.pure-form input[type=datetime],.pure-form input[type=email],.pure-form input[type=month],.pure-form input[type=number],.pure-form input[type=password],.pure-form input[type=search],.pure-form input[type=tel],.pure-form input[type=text],.pure-form input[type=time],.pure-form input[type=url],.pure-form input[type=week],.pure-form label{margin-bottom:.3em;display:block}.pure-group input:not([type]),.pure-group input[type=color],.pure-group input[type=date],.pure-group input[type=datetime-local],.pure-group input[type=datetime],.pure-group input[type=email],.pure-group input[type=month],.pure-group input[type=number],.pure-group input[type=password],.pure-group input[type=search],.pure-group input[type=tel],.pure-group input[type=text],.pure-group input[type=time],.pure-group input[type=url],.pure-group input[type=week]{margin-bottom:0}.pure-form-aligned .pure-control-group label{margin-bottom:.3em;text-align:left;display:block;width:100%}.pure-form-aligned .pure-controls{margin:1.5em 0 0 0}.pure-form-message,.pure-form-message-inline{display:block;font-size:.75em;padding:.2em 0 .8em}}.pure-menu{-webkit-box-sizing:border-box;box-sizing:border-box}.pure-menu-fixed{position:fixed;left:0;top:0;z-index:3}.pure-menu-item,.pure-menu-list{position:relative}.pure-menu-list{list-style:none;margin:0;padding:0}.pure-menu-item{padding:0;margin:0;height:100%}.pure-menu-heading,.pure-menu-link{display:block;text-decoration:none;white-space:nowrap}.pure-menu-horizontal{width:100%;white-space:nowrap}.pure-menu-horizontal .pure-menu-list{display:inline-block}.pure-menu-horizontal .pure-menu-heading,.pure-menu-horizontal .pure-menu-item,.pure-menu-horizontal .pure-menu-separator{display:inline-block;vertical-align:middle}.pure-menu-item .pure-menu-item{display:block}.pure-menu-children{display:none;position:absolute;left:100%;top:0;margin:0;padding:0;z-index:3}.pure-menu-horizontal .pure-menu-children{left:0;top:auto;width:inherit}.pure-menu-active>.pure-menu-children,.pure-menu-allow-hover:hover>.pure-menu-children{display:block;position:absolute}.pure-menu-has-children>.pure-menu-link:after{padding-left:.5em;content:"\25B8";font-size:small}.pure-menu-horizontal .pure-menu-has-children>.pure-menu-link:after{content:"\25BE"}.pure-menu-scrollable{overflow-y:scroll;overflow-x:hidden}.pure-menu-scrollable .pure-menu-list{display:block}.pure-menu-horizontal.pure-menu-scrollable .pure-menu-list{display:inline-block}.pure-menu-horizontal.pure-menu-scrollable{white-space:nowrap;overflow-y:hidden;overflow-x:auto;padding:.5em 0}.pure-menu-horizontal .pure-menu-children .pure-menu-separator,.pure-menu-separator{background-color:#ccc;height:1px;margin:.3em 0}.pure-menu-horizontal .pure-menu-separator{width:1px;height:1.3em;margin:0 .3em}.pure-menu-horizontal .pure-menu-children .pure-menu-separator{display:block;width:auto}.pure-menu-heading{text-transform:uppercase;color:#565d64}.pure-menu-link{color:#777}.pure-menu-children{background-color:#fff}.pure-menu-heading,.pure-menu-link{padding:.5em 1em}.pure-menu-disabled{opacity:.5}.pure-menu-disabled .pure-menu-link:hover{background-color:transparent;cursor:default}.pure-menu-active>.pure-menu-link,.pure-menu-link:focus,.pure-menu-link:hover{background-color:#eee}.pure-menu-selected>.pure-menu-link,.pure-menu-selected>.pure-menu-link:visited{color:#000}.pure-table{border-collapse:collapse;border-spacing:0;empty-cells:show;border:1px solid #cbcbcb}.pure-table caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.pure-table td,.pure-table th{border-left:1px solid #cbcbcb;border-width:0 0 0 1px;font-size:inherit;margin:0;overflow:visible;padding:.5em 1em}.pure-table thead{background-color:#e0e0e0;color:#000;text-align:left;vertical-align:bottom}.pure-table td{background-color:transparent}.pure-table-odd td{background-color:#f2f2f2}.pure-table-striped tr:nth-child(2n-1) td{background-color:#f2f2f2}.pure-table-bordered td{border-bottom:1px solid #cbcbcb}.pure-table-bordered tbody>tr:last-child>td{border-bottom-width:0}.pure-table-horizontal td,.pure-table-horizontal th{border-width:0 0 1px 0;border-bottom:1px solid #cbcbcb}.pure-table-horizontal tbody>tr:last-child>td{border-bottom-width:0} diff --git a/examples/backgrounded_futures.rs b/examples/backgrounded_futures.rs index 4cafad439e..4fabef09f7 100644 --- a/examples/backgrounded_futures.rs +++ b/examples/backgrounded_futures.rs @@ -11,7 +11,7 @@ use async_std::task::sleep; use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/calculator.rs b/examples/calculator.rs index 9bf2a04618..b373923f6b 100644 --- a/examples/calculator.rs +++ b/examples/calculator.rs @@ -12,10 +12,10 @@ use dioxus::events::*; use dioxus::html::input_data::keyboard_types::Key; use dioxus::prelude::*; -const STYLE: &str = asset!("./examples/assets/calculator.css"); +const STYLE: Asset = asset!("/examples/assets/calculator.css"); fn main() { - LaunchBuilder::desktop() + dioxus::launch::builder() .with_cfg(desktop!({ use dioxus::desktop::{Config, LogicalSize, WindowBuilder}; Config::new().with_window( @@ -54,7 +54,7 @@ fn app() -> Element { }; rsx! { - head::Link { rel: "stylesheet", href: STYLE } + document::Stylesheet { href: STYLE } div { id: "wrapper", div { class: "app", div { class: "calculator", tabindex: "0", onkeydown: handle_key_down_event, diff --git a/examples/calculator_mutable.rs b/examples/calculator_mutable.rs index 2f933ebbea..9d1ec05fbc 100644 --- a/examples/calculator_mutable.rs +++ b/examples/calculator_mutable.rs @@ -13,7 +13,7 @@ use dioxus::html::MouseEvent; use dioxus::prelude::*; fn main() { - LaunchBuilder::desktop() + dioxus::launch::builder() .with_cfg( Config::new().with_window( WindowBuilder::new() @@ -29,7 +29,7 @@ fn app() -> Element { let mut state = use_signal(Calculator::new); rsx! { - head::Link { rel: "stylesheet", href: asset!("./examples/assets/calculator.css") } + document::Stylesheet { href: asset!("/examples/assets/calculator.css") } div { id: "wrapper", div { class: "app", div { diff --git a/examples/clock.rs b/examples/clock.rs index dc168a5ec2..d344311fae 100644 --- a/examples/clock.rs +++ b/examples/clock.rs @@ -5,18 +5,18 @@ use async_std::task::sleep; use dioxus::prelude::*; use web_time::Instant; -const STYLE: &str = asset!("./examples/assets/clock.css"); +const STYLE: Asset = asset!("/examples/assets/clock.css"); fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { let mut millis = use_signal(|| 0); use_future(move || async move { - // Save our initial timea - let start = Instant::now(); + // Save our initial time + let start = std::time::Instant::now(); loop { sleep(std::time::Duration::from_millis(27)).await; @@ -36,7 +36,7 @@ fn app() -> Element { ); rsx! { - head::Link { rel: "stylesheet", href: STYLE } + document::Stylesheet { href: STYLE } div { id: "app", div { id: "title", "Carpe diem 🎉" } div { id: "clock-display", "{time}" } diff --git a/examples/control_focus.rs b/examples/control_focus.rs index c69293c56d..6a383fa662 100644 --- a/examples/control_focus.rs +++ b/examples/control_focus.rs @@ -5,13 +5,8 @@ use std::rc::Rc; -use async_std::task::sleep; -use dioxus::prelude::*; - -const STYLE: &str = asset!("./examples/assets/roulette.css"); - fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { @@ -40,7 +35,7 @@ fn app() -> Element { }); rsx! { - head::Link { rel: "stylesheet", href: STYLE } + document::Stylesheet { href: asset!("/examples/assets/roulette.css") } h1 { "Input Roulette" } button { onclick: move |_| running.toggle(), "Toggle roulette" } div { id: "roulette-grid", diff --git a/examples/counters.rs b/examples/counters.rs index f2d8c4203c..63013a1cc5 100644 --- a/examples/counters.rs +++ b/examples/counters.rs @@ -2,10 +2,8 @@ use dioxus::prelude::*; -const STYLE: &str = asset!("./examples/assets/counter.css"); - fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { @@ -16,7 +14,7 @@ fn app() -> Element { let sum = use_memo(move || counters.read().iter().copied().sum::()); rsx! { - head::Link { rel: "stylesheet", href: STYLE } + document::Stylesheet { href: asset!("/examples/assets/counter.css") } div { id: "controls", button { onclick: move |_| counters.write().push(0), "Add counter" } diff --git a/examples/crm.rs b/examples/crm.rs index 21f07466cd..ca83f58d40 100644 --- a/examples/crm.rs +++ b/examples/crm.rs @@ -12,7 +12,7 @@ use dioxus::prelude::*; fn main() { - LaunchBuilder::new() + dioxus::builder() .with_cfg(desktop!({ use dioxus::desktop::{LogicalSize, WindowBuilder}; dioxus::desktop::Config::default() @@ -20,13 +20,8 @@ fn main() { })) .launch(|| { rsx! { - head::Link { - rel: "stylesheet", - href: asset!("https://unpkg.com/purecss@2.0.6/build/pure-min.css"), - integrity: "sha384-Uu6IeWbM+gzNVXJcM9XV3SohHtmWE+3VGi496jvgX1jyvDTXfdK+rfZc8C1Aehk5", - crossorigin: "anonymous" - } - head::Link { rel: "stylesheet", href: asset!("./examples/assets/crm.css") } + document::Stylesheet { href: asset!("/examples/assets/crm.css") } + document::Link { href: asset!("/examples/assets/purecss.css") } h1 { "Dioxus CRM Example" } Router:: {} } diff --git a/examples/custom_assets.rs b/examples/custom_assets.rs index 6c927d21a4..8a56530110 100644 --- a/examples/custom_assets.rs +++ b/examples/custom_assets.rs @@ -1,27 +1,37 @@ //! A simple example on how to use assets loading from the filesystem. //! -//! If the feature "collect-assets" is enabled, the assets will be collected via the dioxus CLI and embedded into the -//! final bundle. This lets you do various useful things like minify, compress, and optimize your assets. +//! Dioxus provides an asset!() macro which properly handles asset loading and bundling for you. +//! For bundling, asset!() must be paired with a tool that handles mangansis-link sections. The dioxus-cli +//! handles this for you, but this means you can't just simply `cargo build --release` to build and +//! distribute your app. //! -//! We can still use assets without the CLI middleware, but generally larger apps will benefit from it. - +//! You can run this example with `cargo run --example assets` or `dx serve --example assets`. +//! When manganis is not active, the asset!() macro will fallback to the path of the asset on +//! your filesystem. use dioxus::prelude::*; -#[cfg(not(feature = "collect-assets"))] -static ASSET_PATH: &str = "examples/assets/logo.png"; - -#[cfg(feature = "collect-assets")] -static ASSET_PATH: &str = asset!("examples/assets/logo.png".format(ImageType::Avif)); +/// asset!() will mark this asset as a dependency of the app without actually including it in the +/// generated code. This is better than include_str!() or include_bytes!() since it works +/// for web apps as well as native and mobile apps. +/// +/// When used with web apps, manganis will detect the import of the image, optimize it, and put it +/// in the output dist folder in the right location, ensuring no two images have the same name. +static IMAGE: ImageAsset = asset!("/examples/assets/logo.png".image().format(ImageType::Avif)); fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { rsx! { div { h1 { "This should show an image:" } - img { src: ASSET_PATH.to_string() } + img { src: IMAGE } + + // temporarily keep support for these too + img { src: "/Users/jonkelley/Development/dioxus/examples/assets/logo.png" } + img { src: "/examples/assets/logo.png" } + img { src: "examples/assets/logo.png" } } } } diff --git a/examples/custom_html.rs b/examples/custom_html.rs index dbbe334f05..f1ea4b91ad 100644 --- a/examples/custom_html.rs +++ b/examples/custom_html.rs @@ -4,7 +4,7 @@ use dioxus::prelude::*; fn main() { - LaunchBuilder::new() + dioxus::launch::builder() .with_cfg( dioxus::desktop::Config::new().with_custom_index( r#" diff --git a/examples/custom_menu.rs b/examples/custom_menu.rs index f78f33c884..da1085bfd5 100644 --- a/examples/custom_menu.rs +++ b/examples/custom_menu.rs @@ -28,7 +28,7 @@ fn main() { let config = dioxus::desktop::Config::new().with_menu(menu); // Launch the app with the custom menu - LaunchBuilder::new().with_cfg(config).launch(app) + dioxus::launch::builder().with_cfg(config).launch(app) } fn app() -> Element { diff --git a/examples/disabled.rs b/examples/disabled.rs index 6aaa70b9d4..b48b1a912e 100644 --- a/examples/disabled.rs +++ b/examples/disabled.rs @@ -5,7 +5,7 @@ use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/dog_app.rs b/examples/dog_app.rs index 1c53c8731f..09bf5f0a96 100644 --- a/examples/dog_app.rs +++ b/examples/dog_app.rs @@ -11,7 +11,7 @@ use dioxus::prelude::*; use std::collections::HashMap; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/dynamic_asset.rs b/examples/dynamic_asset.rs index 14a33c4809..b741ba57af 100644 --- a/examples/dynamic_asset.rs +++ b/examples/dynamic_asset.rs @@ -7,10 +7,8 @@ use dioxus::desktop::{use_asset_handler, wry::http::Response}; use dioxus::prelude::*; -const STYLE: &str = asset!("./examples/assets/custom_assets.css"); - fn main() { - launch_desktop(app); + dioxus::launch(app); } fn app() -> Element { @@ -24,7 +22,7 @@ fn app() -> Element { }); rsx! { - head::Link { rel: "stylesheet", href: STYLE } + document::Stylesheet { href: asset!("/examples/assets/custom_assets.css") } h1 { "Dynamic Assets" } img { src: "/logos/logo.png" } } diff --git a/examples/errors.rs b/examples/errors.rs index 4727cb5e03..fe16598e95 100644 --- a/examples/errors.rs +++ b/examples/errors.rs @@ -11,7 +11,11 @@ use dioxus::prelude::*; fn main() { - launch(|| rsx! { Router:: {} }); + dioxus::launch(|| { + rsx! { + Router:: {} + } + }); } /// You can use an ErrorBoundary to catch errors in children and display a warning @@ -34,11 +38,8 @@ fn ParseNumber() -> Element { h1 { "Error handler demo" } button { onclick: move |_| { - // You can return a result from an event handler which lets you easily quit rendering early if something fails let data: i32 = "0.5".parse()?; - println!("parsed {data}"); - Ok(()) }, "Click to throw an error" @@ -58,10 +59,7 @@ fn Show() -> Element { if let Some(error) = error.show() { {error} } else { - pre { - color: "red", - "{error}" - } + pre { color: "red", "{error}" } } } } @@ -88,15 +86,10 @@ fn ParseNumberWithShow() -> Element { border_width: "2px", border_radius: "5px", p { "Failed to parse data" } - Link { - to: Route::Home {}, - "Go back to the homepage" - } + Link { to: Route::Home {}, "Go back to the homepage" } } })?; - println!("parsed {data}"); - Ok(()) }, "Click to throw an error" @@ -139,22 +132,13 @@ fn Home() -> Element { rsx! { ul { li { - Link { - to: Route::Simple {}, - "Simple errors" - } + Link { to: Route::Simple {}, "Simple errors" } } li { - Link { - to: Route::Panic {}, - "Capture panics" - } + Link { to: Route::Panic {}, "Capture panics" } } li { - Link { - to: Route::Show {}, - "Show errors" - } + Link { to: Route::Show {}, "Show errors" } } } } diff --git a/examples/eval.rs b/examples/eval.rs index 9cfd15e941..68d26c8b09 100644 --- a/examples/eval.rs +++ b/examples/eval.rs @@ -7,7 +7,7 @@ use async_std::task::sleep; use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { @@ -19,29 +19,24 @@ fn app() -> Element { // The `eval` is available in the prelude - and simply takes a block of JS. // Dioxus' eval is interesting since it allows sending messages to and from the JS code using the `await dioxus.recv()` // builtin function. This allows you to create a two-way communication channel between Rust and JS. - let mut eval = eval( + let mut eval = document::eval( r#" - dioxus.send("Hi from JS!"); - let msg = await dioxus.recv(); - console.log(msg); - return "hi from JS!"; + return "hi from JS!"; "#, ); - // Send a message to the JS code. - eval.send("Hi from Rust!".into()).unwrap(); - - // Our line on the JS side will log the message and then return "hello world". - let res = eval.recv().await.unwrap(); - // This will print "Hi from JS!" and "Hi from Rust!". - println!("{:?}", eval.await); + let res = eval.await; + + println!("hello from js! {:?}", res); res }); - match future.value().as_ref() { - Some(v) => rsx!( p { "{v}" } ), - _ => rsx!( p { "waiting.." } ), - } + todo!() + // future.read_unchecked().as_ref().map(|f| match f { + // Some(Ok(v)) => rsx!( p { "{v:?}" } ), + // Some(Err(e)) => rsx!( p { "{v:?}" } ), + // None => rsx!( p { "waiting.." } ), + // }) } diff --git a/examples/file_explorer.rs b/examples/file_explorer.rs index 1ac571d2e8..c5d5f31756 100644 --- a/examples/file_explorer.rs +++ b/examples/file_explorer.rs @@ -9,7 +9,7 @@ use dioxus::desktop::{Config, WindowBuilder}; use dioxus::prelude::*; fn main() { - LaunchBuilder::desktop() + dioxus::launch::builder() .with_cfg(Config::new().with_window(WindowBuilder::new().with_resizable(true))) .launch(app) } @@ -18,12 +18,12 @@ fn app() -> Element { let mut files = use_signal(Files::new); rsx! { - head::Link { + document::Link { rel: "stylesheet", - href: asset!("./examples/assets/fileexplorer.css") + href: asset!("/examples/assets/fileexplorer.css") } div { - head::Link { href: "https://fonts.googleapis.com/icon?family=Material+Icons", rel: "stylesheet" } + document::Link { href: "https://fonts.googleapis.com/icon?family=Material+Icons", rel: "stylesheet" } header { i { class: "material-icons icon-menu", "menu" } h1 { "Files: " {files.read().current()} } diff --git a/examples/file_upload.rs b/examples/file_upload.rs index 9fb74072bb..ffc1858238 100644 --- a/examples/file_upload.rs +++ b/examples/file_upload.rs @@ -8,10 +8,10 @@ use std::sync::Arc; use dioxus::prelude::*; use dioxus::{html::HasFileData, prelude::dioxus_elements::FileEngine}; -const STYLE: &str = asset!("./examples/assets/file_upload.css"); +const STYLE: Asset = asset!("/examples/assets/file_upload.css"); fn main() { - launch(app); + dioxus::launch(app); } struct UploadedFile { @@ -43,7 +43,7 @@ fn app() -> Element { }; rsx! { - head::Link { rel: "stylesheet", href: STYLE } + document::Stylesheet { href: STYLE } h1 { "File Upload Example" } p { "Drop a .txt, .rs, or .js file here to read it" } diff --git a/examples/flat_router.rs b/examples/flat_router.rs index 79a9d2a3f2..d537ee4c1b 100644 --- a/examples/flat_router.rs +++ b/examples/flat_router.rs @@ -9,12 +9,12 @@ use dioxus::prelude::*; -const STYLE: &str = asset!("./examples/assets/flat_router.css"); +const STYLE: Asset = asset!("/examples/assets/flat_router.css"); fn main() { - launch(|| { + dioxus::launch(|| { rsx! { - head::Link { rel: "stylesheet", href: STYLE } + document::Stylesheet { href: STYLE } Router:: {} } }) diff --git a/examples/form.rs b/examples/form.rs index 91b09ecd34..ff6154b9d4 100644 --- a/examples/form.rs +++ b/examples/form.rs @@ -7,7 +7,7 @@ use dioxus::prelude::*; use std::collections::HashMap; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/future.rs b/examples/future.rs index f4ddc10468..e32386fe83 100644 --- a/examples/future.rs +++ b/examples/future.rs @@ -7,7 +7,7 @@ use async_std::task::sleep; use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/generic_component.rs b/examples/generic_component.rs index 6af34e542b..520f257787 100644 --- a/examples/generic_component.rs +++ b/examples/generic_component.rs @@ -7,7 +7,7 @@ use dioxus::prelude::*; use std::fmt::Display; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/global.rs b/examples/global.rs index 0a08485b02..693b912e93 100644 --- a/examples/global.rs +++ b/examples/global.rs @@ -7,18 +7,18 @@ use dioxus::prelude::*; -const STYLE: &str = asset!("./examples/assets/counter.css"); +const STYLE: Asset = asset!("/examples/assets/counter.css"); static COUNT: GlobalSignal = Signal::global(|| 0); static DOUBLED_COUNT: GlobalMemo = Memo::global(|| COUNT() * 2); fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { rsx! { - head::Link { rel: "stylesheet", href: STYLE } + document::Stylesheet { href: STYLE } Increment {} Decrement {} Reset {} diff --git a/examples/hash_fragment_state.rs b/examples/hash_fragment_state.rs index ed77c8c46e..bc0ec2cc88 100644 --- a/examples/hash_fragment_state.rs +++ b/examples/hash_fragment_state.rs @@ -2,7 +2,7 @@ //! //! You can set up two way data binding between the url hash and signals. //! -//! Run this example on desktop with +//! Run this example on desktop with //! ```sh //! dx serve --example hash_fragment_state --features=ciborium,base64 //! ``` @@ -19,7 +19,7 @@ use dioxus::prelude::*; use serde::{Deserialize, Serialize}; fn main() { - launch(|| { + dioxus::launch(|| { rsx! { Router:: {} } diff --git a/examples/hello_world.rs b/examples/hello_world.rs index ee33c22309..20c9af934a 100644 --- a/examples/hello_world.rs +++ b/examples/hello_world.rs @@ -12,7 +12,7 @@ use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/hydration.rs b/examples/hydration.rs index 1e7de2706f..cffacc86aa 100644 --- a/examples/hydration.rs +++ b/examples/hydration.rs @@ -13,7 +13,7 @@ use dioxus::desktop::Config; use dioxus::prelude::*; fn main() { - LaunchBuilder::desktop() + dioxus::launch::builder() .with_cfg(Config::new().with_prerendered({ // We build the dom a first time, then pre-render it to HTML let pre_rendered_dom = VirtualDom::prebuilt(app); diff --git a/examples/image_generator_openai.rs b/examples/image_generator_openai.rs index 474c46de90..67c743c427 100644 --- a/examples/image_generator_openai.rs +++ b/examples/image_generator_openai.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; use serde_json::{json, Error}; fn main() { - launch(app) + dioxus::launch(app) } fn app() -> Element { @@ -36,7 +36,7 @@ fn app() -> Element { }); rsx! { - head::Link { rel: "stylesheet", href: "https://unpkg.com/bulma@0.9.0/css/bulma.min.css" } + document::Stylesheet { href: "https://unpkg.com/bulma@0.9.0/css/bulma.min.css" } div { class: "container", div { class: "columns", div { class: "column", diff --git a/examples/link.rs b/examples/link.rs index ff29b65626..7d1f4be609 100644 --- a/examples/link.rs +++ b/examples/link.rs @@ -8,15 +8,15 @@ use dioxus::prelude::*; -const STYLE: &str = asset!("./examples/assets/links.css"); +const STYLE: Asset = asset!("/examples/assets/links.css"); fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { rsx! ( - head::Link { rel: "stylesheet", href: STYLE } + document::Stylesheet { href: STYLE } Router:: {} ) } diff --git a/examples/login_form.rs b/examples/login_form.rs index 39e874cc99..617723466c 100644 --- a/examples/login_form.rs +++ b/examples/login_form.rs @@ -9,7 +9,7 @@ use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/memo_chain.rs b/examples/memo_chain.rs index 205fe87693..ab57089397 100644 --- a/examples/memo_chain.rs +++ b/examples/memo_chain.rs @@ -6,7 +6,7 @@ use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/meta.rs b/examples/meta.rs index fae916258f..9d8e1a4ae0 100644 --- a/examples/meta.rs +++ b/examples/meta.rs @@ -3,8 +3,7 @@ use dioxus::prelude::*; fn main() { - tracing_subscriber::fmt::init(); - launch(app); + dioxus::launch(app); } fn app() -> Element { @@ -12,23 +11,23 @@ fn app() -> Element { // You can use the Meta component to render a meta tag into the head of the page // Meta tags are useful to provide information about the page to search engines and social media sites // This example sets up meta tags for the open graph protocol for social media previews - Meta { + document::Meta { property: "og:title", content: "My Site", } - Meta { + document::Meta { property: "og:type", content: "website", } - Meta { + document::Meta { property: "og:url", content: "https://www.example.com", } - Meta { + document::Meta { property: "og:image", content: "https://example.com/image.jpg", } - Meta { + document::Meta { name: "description", content: "My Site is a site", } diff --git a/examples/mobile_demo/.gitignore b/examples/mobile_demo/.gitignore deleted file mode 100644 index e1e084c4bb..0000000000 --- a/examples/mobile_demo/.gitignore +++ /dev/null @@ -1,10 +0,0 @@ -# Rust -target/ -**/*.rs.bk - -# cargo-mobile2 -.cargo/ -/gen - -# macOS -.DS_Store diff --git a/examples/mobile_demo/Cargo.lock b/examples/mobile_demo/Cargo.lock deleted file mode 100644 index 7f50292263..0000000000 --- a/examples/mobile_demo/Cargo.lock +++ /dev/null @@ -1,4007 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "addr2line" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "ahash" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy", -] - -[[package]] -name = "aho-corasick" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" -dependencies = [ - "memchr", -] - -[[package]] -name = "allocator-api2" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" - -[[package]] -name = "android_log-sys" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85965b6739a430150bdd138e2374a98af0c3ee0d030b3bb7fc3bddff58d0102e" - -[[package]] -name = "android_logger" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ec2333c185d826313162cee39d3fcc6a84ba08114a839bebf53b961e7e75773" -dependencies = [ - "android_log-sys", - "env_logger 0.7.1", - "lazy_static", - "log", -] - -[[package]] -name = "anyhow" -version = "1.0.72" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" - -[[package]] -name = "async-channel" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" -dependencies = [ - "concurrent-queue", - "event-listener", - "futures-core", -] - -[[package]] -name = "async-lock" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa24f727524730b077666307f2734b4a1a1c57acb79193127dcc8914d5242dd7" -dependencies = [ - "event-listener", -] - -[[package]] -name = "async-task" -version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae" - -[[package]] -name = "async-trait" -version = "0.1.72" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "atk" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4af014b17dd80e8af9fa689b2d4a211ddba6eb583c1622f35d0cb543f6b17e4" -dependencies = [ - "atk-sys", - "glib", - "libc", -] - -[[package]] -name = "atk-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "251e0b7d90e33e0ba930891a505a9a35ece37b2dd37a14f3ffc306c13b980009" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "atomic-waker" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3" - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "backtrace" -version = "0.3.68" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - -[[package]] -name = "base64" -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" -dependencies = [ - "serde", -] - -[[package]] -name = "block" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "blocking" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77231a1c8f801696fc0123ec6150ce92cffb8e164a02afb9c8ddee0e9b65ad65" -dependencies = [ - "async-channel", - "async-lock", - "async-task", - "atomic-waker", - "fastrand", - "futures-lite", - "log", -] - -[[package]] -name = "bumpalo" -version = "3.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" - -[[package]] -name = "bytemuck" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "bytes" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" - -[[package]] -name = "cairo-rs" -version = "0.18.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" -dependencies = [ - "bitflags 2.4.2", - "cairo-sys-rs", - "glib", - "libc", - "once_cell", - "thiserror", -] - -[[package]] -name = "cairo-sys-rs" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" -dependencies = [ - "glib-sys", - "libc", - "system-deps", -] - -[[package]] -name = "cc" -version = "1.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" - -[[package]] -name = "cesu8" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" - -[[package]] -name = "cfb" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" -dependencies = [ - "byteorder", - "fnv", - "uuid", -] - -[[package]] -name = "cfg-expr" -version = "0.15.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b40ccee03b5175c18cde8f37e7d2a33bcef6f8ec8f7cc0d81090d1bb380949c9" -dependencies = [ - "smallvec", - "target-lexicon", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "cfg_aliases" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" - -[[package]] -name = "ciborium" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" -dependencies = [ - "ciborium-io", - "ciborium-ll", - "serde", -] - -[[package]] -name = "ciborium-io" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" - -[[package]] -name = "ciborium-ll" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" -dependencies = [ - "ciborium-io", - "half", -] - -[[package]] -name = "cocoa" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" -dependencies = [ - "bitflags 1.3.2", - "block", - "cocoa-foundation", - "core-foundation", - "core-graphics", - "foreign-types 0.5.0", - "libc", - "objc", -] - -[[package]] -name = "cocoa-foundation" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "931d3837c286f56e3c58423ce4eba12d08db2374461a785c86f672b08b5650d6" -dependencies = [ - "bitflags 1.3.2", - "block", - "core-foundation", - "core-graphics-types", - "foreign-types 0.3.2", - "libc", - "objc", -] - -[[package]] -name = "color_quant" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" - -[[package]] -name = "combine" -version = "4.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" -dependencies = [ - "bytes", - "memchr", -] - -[[package]] -name = "concurrent-queue" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "const_format" -version = "0.2.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3a214c7af3d04997541b18d432afaff4c455e79e2029079647e72fc2bd27673" -dependencies = [ - "const_format_proc_macros", -] - -[[package]] -name = "const_format_proc_macros" -version = "0.2.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - -[[package]] -name = "constcat" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd7e35aee659887cbfb97aaf227ac12cad1a9d7c71e55ff3376839ed4e282d08" - -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - -[[package]] -name = "convert_case" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "core-foundation" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" - -[[package]] -name = "core-graphics" -version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "970a29baf4110c26fedbc7f82107d42c23f7e88e404c4577ed73fe99ff85a212" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "core-graphics-types", - "foreign-types 0.5.0", - "libc", -] - -[[package]] -name = "core-graphics-types" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bb142d41022986c1d8ff29103a1411c8a3dfad3552f87a4f8dc50d61d4f4e33" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "libc", -] - -[[package]] -name = "cpufeatures" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" -dependencies = [ - "libc", -] - -[[package]] -name = "crc32fast" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[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 = "cssparser" -version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a" -dependencies = [ - "cssparser-macros", - "dtoa-short", - "itoa 0.4.8", - "matches", - "phf 0.8.0", - "proc-macro2", - "quote", - "smallvec", - "syn 1.0.109", -] - -[[package]] -name = "cssparser-macros" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" -dependencies = [ - "quote", - "syn 2.0.52", -] - -[[package]] -name = "darling" -version = "0.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "darling_macro" -version = "0.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" -dependencies = [ - "darling_core", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "dashmap" -version = "5.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" -dependencies = [ - "cfg-if", - "hashbrown 0.14.0", - "lock_api", - "once_cell", - "parking_lot_core", -] - -[[package]] -name = "derive_more" -version = "0.99.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" -dependencies = [ - "convert_case 0.4.0", - "proc-macro2", - "quote", - "rustc_version", - "syn 1.0.109", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", -] - -[[package]] -name = "dioxus" -version = "0.5.0-alpha.0" -dependencies = [ - "dioxus-config-macro", - "dioxus-core", - "dioxus-core-macro", - "dioxus-desktop", - "dioxus-fullstack", - "dioxus-hooks", - "dioxus-hot-reload", - "dioxus-html", - "dioxus-mobile", - "dioxus-signals", -] - -[[package]] -name = "dioxus-cli-config" -version = "0.5.0-alpha.0" -dependencies = [ - "once_cell", - "serde", - "serde_json", - "tracing", -] - -[[package]] -name = "dioxus-config-macro" -version = "0.5.0-alpha.0" -dependencies = [ - "proc-macro2", - "quote", -] - -[[package]] -name = "dioxus-core" -version = "0.5.0-alpha.0" -dependencies = [ - "futures-channel", - "futures-util", - "longest-increasing-subsequence", - "rustc-hash", - "serde", - "slab", - "tracing", - "tracing-subscriber", -] - -[[package]] -name = "dioxus-core-macro" -version = "0.5.0-alpha.0" -dependencies = [ - "constcat", - "convert_case 0.6.0", - "dioxus-rsx", - "prettyplease", - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "dioxus-debug-cell" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ea539174bb236e0e7dc9c12b19b88eae3cb574dedbd0252a2d43ea7e6de13e2" - -[[package]] -name = "dioxus-desktop" -version = "0.5.0-alpha.0" -dependencies = [ - "async-trait", - "core-foundation", - "dioxus-cli-config", - "dioxus-core", - "dioxus-hooks", - "dioxus-hot-reload", - "dioxus-html", - "dioxus-interpreter-js", - "dunce", - "futures-channel", - "futures-util", - "generational-box", - "global-hotkey", - "infer", - "muda", - "objc", - "objc_id", - "rfd", - "rustc-hash", - "serde", - "serde_json", - "slab", - "tao", - "thiserror", - "tokio", - "tracing", - "urlencoding", - "webbrowser", - "wry 0.37.0", -] - -[[package]] -name = "dioxus-fullstack" -version = "0.5.0-alpha.0" -dependencies = [ - "async-trait", - "base64", - "bytes", - "ciborium", - "dioxus-hot-reload", - "dioxus-lib", - "dioxus-mobile", - "dioxus_server_macro", - "futures-util", - "once_cell", - "serde", - "serde_json", - "server_fn", - "tracing", -] - -[[package]] -name = "dioxus-hooks" -version = "0.5.0-alpha.0" -dependencies = [ - "dioxus-core", - "dioxus-debug-cell", - "dioxus-signals", - "futures-channel", - "futures-util", - "generational-box", - "slab", - "thiserror", - "tracing", -] - -[[package]] -name = "dioxus-hot-reload" -version = "0.5.0-alpha.0" -dependencies = [ - "dioxus-core", - "dioxus-html", - "dioxus-rsx", - "interprocess", - "serde", - "serde_json", -] - -[[package]] -name = "dioxus-html" -version = "0.5.0-alpha.0" -dependencies = [ - "async-trait", - "dioxus-core", - "dioxus-html-internal-macro", - "enumset", - "euclid", - "futures-channel", - "generational-box", - "keyboard-types", - "serde", - "serde-value", - "serde_json", - "serde_repr", - "tokio", - "web-sys", -] - -[[package]] -name = "dioxus-html-internal-macro" -version = "0.5.0-alpha.0" -dependencies = [ - "convert_case 0.6.0", - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "dioxus-interpreter-js" -version = "0.5.0-alpha.0" -dependencies = [ - "dioxus-core", - "dioxus-html", - "md5", - "sledgehammer_bindgen", - "sledgehammer_utils", -] - -[[package]] -name = "dioxus-lib" -version = "0.5.0-alpha.0" -dependencies = [ - "dioxus-core", - "dioxus-core-macro", - "dioxus-hooks", - "dioxus-html", - "dioxus-rsx", - "dioxus-signals", -] - -[[package]] -name = "dioxus-mobile" -version = "0.5.0-alpha.0" -dependencies = [ - "dioxus-desktop", -] - -[[package]] -name = "dioxus-rsx" -version = "0.5.0-alpha.0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", - "tracing", -] - -[[package]] -name = "dioxus-signals" -version = "0.5.0-alpha.0" -dependencies = [ - "dioxus-core", - "futures-channel", - "futures-util", - "generational-box", - "once_cell", - "parking_lot", - "rustc-hash", - "tracing", -] - -[[package]] -name = "dioxus_server_macro" -version = "0.5.0-alpha.0" -dependencies = [ - "convert_case 0.6.0", - "proc-macro2", - "quote", - "server_fn_macro", - "syn 2.0.52", -] - -[[package]] -name = "dispatch" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" - -[[package]] -name = "dlopen2" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1297103d2bbaea85724fcee6294c2d50b1081f9ad47d0f6f6f61eda65315a6" -dependencies = [ - "dlopen2_derive", - "libc", - "once_cell", - "winapi", -] - -[[package]] -name = "dlopen2_derive" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "dtoa" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" - -[[package]] -name = "dtoa-short" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbaceec3c6e4211c79e7b1800fb9680527106beb2f9c51904a3210c03a448c74" -dependencies = [ - "dtoa", -] - -[[package]] -name = "dunce" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" - -[[package]] -name = "enumset" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e875f1719c16de097dee81ed675e2d9bb63096823ed3f0ca827b7dea3028bbbb" -dependencies = [ - "enumset_derive", -] - -[[package]] -name = "enumset_derive" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08b6c6ab82d70f08844964ba10c7babb716de2ecaeab9be5717918a5177d3af" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "env_logger" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" -dependencies = [ - "log", - "regex", -] - -[[package]] -name = "env_logger" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", -] - -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - -[[package]] -name = "euclid" -version = "0.22.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f253bc5c813ca05792837a0ff4b3a580336b224512d48f7eda1d7dd9210787" -dependencies = [ - "num-traits", - "serde", -] - -[[package]] -name = "event-listener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - -[[package]] -name = "fastrand" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] - -[[package]] -name = "fdeflate" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10" -dependencies = [ - "simd-adler32", -] - -[[package]] -name = "field-offset" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" -dependencies = [ - "memoffset", - "rustc_version", -] - -[[package]] -name = "flate2" -version = "1.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "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.52", -] - -[[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.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "futf" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" -dependencies = [ - "mac", - "new_debug_unreachable", -] - -[[package]] -name = "futures" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" - -[[package]] -name = "futures-executor" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" - -[[package]] -name = "futures-lite" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" -dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", -] - -[[package]] -name = "futures-macro" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "futures-sink" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" - -[[package]] -name = "futures-task" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" - -[[package]] -name = "futures-util" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "fxhash" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" -dependencies = [ - "byteorder", -] - -[[package]] -name = "gdk" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5ba081bdef3b75ebcdbfc953699ed2d7417d6bd853347a42a37d76406a33646" -dependencies = [ - "cairo-rs", - "gdk-pixbuf", - "gdk-sys", - "gio", - "glib", - "libc", - "pango", -] - -[[package]] -name = "gdk-pixbuf" -version = "0.18.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" -dependencies = [ - "gdk-pixbuf-sys", - "gio", - "glib", - "libc", - "once_cell", -] - -[[package]] -name = "gdk-pixbuf-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" -dependencies = [ - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "gdk-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31ff856cb3386dae1703a920f803abafcc580e9b5f711ca62ed1620c25b51ff2" -dependencies = [ - "cairo-sys-rs", - "gdk-pixbuf-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "pango-sys", - "pkg-config", - "system-deps", -] - -[[package]] -name = "gdkwayland-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a90fbf5c033c65d93792192a49a8efb5bb1e640c419682a58bb96f5ae77f3d4a" -dependencies = [ - "gdk-sys", - "glib-sys", - "gobject-sys", - "libc", - "pkg-config", - "system-deps", -] - -[[package]] -name = "gdkx11" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2ea8a4909d530f79921290389cbd7c34cb9d623bfe970eaae65ca5f9cd9cce" -dependencies = [ - "gdk", - "gdkx11-sys", - "gio", - "glib", - "libc", - "x11", -] - -[[package]] -name = "gdkx11-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fee8f00f4ee46cad2939b8990f5c70c94ff882c3028f3cc5abf950fa4ab53043" -dependencies = [ - "gdk-sys", - "glib-sys", - "libc", - "system-deps", - "x11", -] - -[[package]] -name = "generational-box" -version = "0.5.0-alpha.0" -dependencies = [ - "parking_lot", -] - -[[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 = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - -[[package]] -name = "getrandom" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.11.0+wasi-snapshot-preview1", -] - -[[package]] -name = "gimli" -version = "0.27.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" - -[[package]] -name = "gio" -version = "0.18.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-util", - "gio-sys", - "glib", - "libc", - "once_cell", - "pin-project-lite", - "smallvec", - "thiserror", -] - -[[package]] -name = "gio-sys" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", - "winapi", -] - -[[package]] -name = "glib" -version = "0.18.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" -dependencies = [ - "bitflags 2.4.2", - "futures-channel", - "futures-core", - "futures-executor", - "futures-task", - "futures-util", - "gio-sys", - "glib-macros", - "glib-sys", - "gobject-sys", - "libc", - "memchr", - "once_cell", - "smallvec", - "thiserror", -] - -[[package]] -name = "glib-macros" -version = "0.18.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" -dependencies = [ - "heck", - "proc-macro-crate 2.0.2", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "glib-sys" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" -dependencies = [ - "libc", - "system-deps", -] - -[[package]] -name = "global-hotkey" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0d37e95d3937251ee2019709389bb793c1237f16d45fc0fe7b2464b5f97c68" -dependencies = [ - "crossbeam-channel", - "keyboard-types", - "once_cell", - "thiserror", - "windows-sys 0.52.0", - "x11-dl", -] - -[[package]] -name = "gloo-net" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43aaa242d1239a8822c15c645f02166398da4f8b5c4bae795c1f5b44e9eee173" -dependencies = [ - "futures-channel", - "futures-core", - "futures-sink", - "gloo-utils", - "http 0.2.9", - "js-sys", - "pin-project", - "serde", - "serde_json", - "thiserror", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "gloo-utils" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" -dependencies = [ - "js-sys", - "serde", - "serde_json", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gobject-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" -dependencies = [ - "glib-sys", - "libc", - "system-deps", -] - -[[package]] -name = "gtk" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93c4f5e0e20b60e10631a5f06da7fe3dda744b05ad0ea71fee2f47adf865890c" -dependencies = [ - "atk", - "cairo-rs", - "field-offset", - "futures-channel", - "gdk", - "gdk-pixbuf", - "gio", - "glib", - "gtk-sys", - "gtk3-macros", - "libc", - "pango", - "pkg-config", -] - -[[package]] -name = "gtk-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771437bf1de2c1c0b496c11505bdf748e26066bbe942dfc8f614c9460f6d7722" -dependencies = [ - "atk-sys", - "cairo-sys-rs", - "gdk-pixbuf-sys", - "gdk-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "pango-sys", - "system-deps", -] - -[[package]] -name = "gtk3-macros" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6063efb63db582968fb7df72e1ae68aa6360dcfb0a75143f34fc7d616bad75e" -dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "half" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5eceaaeec696539ddaf7b333340f1af35a5aa87ae3e4f3ead0532f72affab2e" -dependencies = [ - "cfg-if", - "crunchy", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hashbrown" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" -dependencies = [ - "ahash", - "allocator-api2", -] - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" - -[[package]] -name = "home" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" -dependencies = [ - "windows-sys 0.48.0", -] - -[[package]] -name = "html5ever" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" -dependencies = [ - "log", - "mac", - "markup5ever", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "http" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" -dependencies = [ - "bytes", - "fnv", - "itoa 1.0.9", -] - -[[package]] -name = "http" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" -dependencies = [ - "bytes", - "fnv", - "itoa 1.0.9", -] - -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "idna" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "image" -version = "0.24.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "527909aa81e20ac3a44803521443a765550f09b5130c2c2fa1ea59c2f8f50a3a" -dependencies = [ - "bytemuck", - "byteorder", - "color_quant", - "num-rational", - "num-traits", -] - -[[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", -] - -[[package]] -name = "indexmap" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" -dependencies = [ - "equivalent", - "hashbrown 0.14.0", -] - -[[package]] -name = "infer" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6c16b11a665b26aeeb9b1d7f954cdeb034be38dd00adab4f2ae921a8fee804" -dependencies = [ - "cfb", -] - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "interprocess" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81f2533f3be42fffe3b5e63b71aeca416c1c3bc33e4e27be018521e76b1f38fb" -dependencies = [ - "blocking", - "cfg-if", - "futures-core", - "futures-io", - "intmap", - "libc", - "once_cell", - "rustc_version", - "spinning", - "thiserror", - "to_method", - "winapi", -] - -[[package]] -name = "intmap" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae52f28f45ac2bc96edb7714de995cffc174a395fb0abf5bff453587c980d7b9" - -[[package]] -name = "itoa" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" - -[[package]] -name = "itoa" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" - -[[package]] -name = "javascriptcore-rs" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca5671e9ffce8ffba57afc24070e906da7fc4b1ba66f2cabebf61bf2ea257fcc" -dependencies = [ - "bitflags 1.3.2", - "glib", - "javascriptcore-rs-sys", -] - -[[package]] -name = "javascriptcore-rs-sys" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1be78d14ffa4b75b66df31840478fef72b51f8c2465d4ca7c194da9f7a5124" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "jni" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec" -dependencies = [ - "cesu8", - "combine", - "jni-sys", - "log", - "thiserror", - "walkdir", -] - -[[package]] -name = "jni" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" -dependencies = [ - "cesu8", - "cfg-if", - "combine", - "jni-sys", - "log", - "thiserror", - "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 = "js-sys" -version = "0.3.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "keyboard-types" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" -dependencies = [ - "bitflags 2.4.2", - "serde", - "unicode-segmentation", -] - -[[package]] -name = "kuchikiki" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e4755b7b995046f510a7520c42b2fed58b77bd94d5a87a8eb43d2fd126da8" -dependencies = [ - "cssparser", - "html5ever", - "indexmap 1.9.3", - "matches", - "selectors", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "libc" -version = "0.2.147" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" - -[[package]] -name = "libxdo" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00333b8756a3d28e78def82067a377de7fa61b24909000aeaa2b446a948d14db" -dependencies = [ - "libxdo-sys", -] - -[[package]] -name = "libxdo-sys" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db23b9e7e2b7831bbd8aac0bbeeeb7b68cbebc162b227e7052e8e55829a09212" -dependencies = [ - "libc", - "x11", -] - -[[package]] -name = "lock_api" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" - -[[package]] -name = "longest-increasing-subsequence" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3bd0dd2cd90571056fdb71f6275fada10131182f84899f4b2a916e565d81d86" - -[[package]] -name = "lru" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" -dependencies = [ - "hashbrown 0.14.0", -] - -[[package]] -name = "mac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" - -[[package]] -name = "malloc_buf" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" -dependencies = [ - "libc", -] - -[[package]] -name = "markup5ever" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" -dependencies = [ - "log", - "phf 0.10.1", - "phf_codegen 0.10.0", - "string_cache", - "string_cache_codegen", - "tendril", -] - -[[package]] -name = "matches" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" - -[[package]] -name = "md5" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "memoffset" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" -dependencies = [ - "autocfg", -] - -[[package]] -name = "miniz_oxide" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" -dependencies = [ - "adler", - "simd-adler32", -] - -[[package]] -name = "mobile-demo" -version = "0.1.0" -dependencies = [ - "android_logger", - "anyhow", - "core-foundation", - "dioxus", - "env_logger 0.9.3", - "jni 0.19.0", - "log", - "paste", - "wry 0.35.2", -] - -[[package]] -name = "muda" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c47e7625990fc1af2226ea4f34fb2412b03c12639fcb91868581eb3a6893453" -dependencies = [ - "cocoa", - "crossbeam-channel", - "gtk", - "keyboard-types", - "libxdo", - "objc", - "once_cell", - "png", - "thiserror", - "windows-sys 0.52.0", -] - -[[package]] -name = "ndk" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" -dependencies = [ - "bitflags 1.3.2", - "jni-sys", - "ndk-sys", - "num_enum", - "raw-window-handle 0.5.2", - "thiserror", -] - -[[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.4.1+23.1.7779620" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cf2aae958bd232cac5069850591667ad422d263686d75b52a065f9badeee5a3" -dependencies = [ - "jni-sys", -] - -[[package]] -name = "new_debug_unreachable" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" - -[[package]] -name = "nodrop" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" - -[[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "winapi", -] - -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" -dependencies = [ - "autocfg", -] - -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi 0.3.2", - "libc", -] - -[[package]] -name = "num_enum" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" -dependencies = [ - "num_enum_derive", -] - -[[package]] -name = "num_enum_derive" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" -dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "objc" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" -dependencies = [ - "malloc_buf", - "objc_exception", -] - -[[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_exception" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" -dependencies = [ - "cc", -] - -[[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.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" - -[[package]] -name = "ordered-float" -version = "2.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7940cf2ca942593318d07fcf2596cdca60a85c9e7fab408a5e21a4f9dcd40d87" -dependencies = [ - "num-traits", -] - -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - -[[package]] -name = "pango" -version = "0.18.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" -dependencies = [ - "gio", - "glib", - "libc", - "once_cell", - "pango-sys", -] - -[[package]] -name = "pango-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "parking" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e" - -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets 0.48.1", -] - -[[package]] -name = "paste" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" - -[[package]] -name = "percent-encoding" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" - -[[package]] -name = "phf" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" -dependencies = [ - "phf_macros", - "phf_shared 0.8.0", - "proc-macro-hack", -] - -[[package]] -name = "phf" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" -dependencies = [ - "phf_shared 0.10.0", -] - -[[package]] -name = "phf_codegen" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" -dependencies = [ - "phf_generator 0.8.0", - "phf_shared 0.8.0", -] - -[[package]] -name = "phf_codegen" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" -dependencies = [ - "phf_generator 0.10.0", - "phf_shared 0.10.0", -] - -[[package]] -name = "phf_generator" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" -dependencies = [ - "phf_shared 0.8.0", - "rand 0.7.3", -] - -[[package]] -name = "phf_generator" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" -dependencies = [ - "phf_shared 0.10.0", - "rand 0.8.5", -] - -[[package]] -name = "phf_macros" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" -dependencies = [ - "phf_generator 0.8.0", - "phf_shared 0.8.0", - "proc-macro-hack", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "phf_shared" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" -dependencies = [ - "siphasher", -] - -[[package]] -name = "phf_shared" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" -dependencies = [ - "siphasher", -] - -[[package]] -name = "pin-project" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkg-config" -version = "0.3.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" - -[[package]] -name = "png" -version = "0.17.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59871cc5b6cce7eaccca5a802b4173377a1c2ba90654246789a8fa2334426d11" -dependencies = [ - "bitflags 1.3.2", - "crc32fast", - "fdeflate", - "flate2", - "miniz_oxide", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "precomputed-hash" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" - -[[package]] -name = "prettyplease" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" -dependencies = [ - "proc-macro2", - "syn 2.0.52", -] - -[[package]] -name = "proc-macro-crate" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" -dependencies = [ - "once_cell", - "toml_edit 0.19.14", -] - -[[package]] -name = "proc-macro-crate" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" -dependencies = [ - "toml_datetime", - "toml_edit 0.20.2", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro-hack" -version = "0.5.20+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" - -[[package]] -name = "proc-macro2" -version = "1.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", - "rand_pcg", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", -] - -[[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 0.6.4", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.10", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "rand_pcg" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "raw-window-handle" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" - -[[package]] -name = "raw-window-handle" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42a9830a0e1b9fb145ebb365b8bc4ccd75f290f98c0247deafbbe2c75cefb544" - -[[package]] -name = "redox_syscall" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "regex" -version = "1.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7b6d6190b7594385f61bd3911cd1be99dfddcfc365a4160cc2ab5bff4aed294" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" - -[[package]] -name = "rfd" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c9e7b57df6e8472152674607f6cc68aa14a748a3157a857a94f516e11aeacc2" -dependencies = [ - "block", - "dispatch", - "glib-sys", - "gobject-sys", - "gtk-sys", - "js-sys", - "log", - "objc", - "objc-foundation", - "objc_id", - "raw-window-handle 0.5.2", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "windows-sys 0.48.0", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" - -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - -[[package]] -name = "rustc_version" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver", -] - -[[package]] -name = "ryu" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" - -[[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 = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "selectors" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe" -dependencies = [ - "bitflags 1.3.2", - "cssparser", - "derive_more", - "fxhash", - "log", - "matches", - "phf 0.8.0", - "phf_codegen 0.8.0", - "precomputed-hash", - "servo_arc", - "smallvec", - "thin-slice", -] - -[[package]] -name = "semver" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" - -[[package]] -name = "send_wrapper" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" -dependencies = [ - "futures-core", -] - -[[package]] -name = "serde" -version = "1.0.180" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ea67f183f058fe88a4e3ec6e2788e003840893b91bac4559cabedd00863b3ed" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde-value" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" -dependencies = [ - "ordered-float", - "serde", -] - -[[package]] -name = "serde_derive" -version = "1.0.180" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24e744d7782b686ab3b73267ef05697159cc0e5abbed3f47f9933165e5219036" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "serde_json" -version = "1.0.104" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" -dependencies = [ - "itoa 1.0.9", - "ryu", - "serde", -] - -[[package]] -name = "serde_qs" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0431a35568651e363364210c91983c1da5eb29404d9f0928b67d4ebcfa7d330c" -dependencies = [ - "percent-encoding", - "serde", - "thiserror", -] - -[[package]] -name = "serde_repr" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "serde_spanned" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" -dependencies = [ - "serde", -] - -[[package]] -name = "server_fn" -version = "0.6.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2955da1dc5fcd970c182ebf1089af6c5f19051e1f286a21f7b96490a49b7a531" -dependencies = [ - "bytes", - "const_format", - "dashmap", - "futures", - "gloo-net", - "http 1.1.0", - "js-sys", - "once_cell", - "send_wrapper", - "serde", - "serde_json", - "serde_qs", - "server_fn_macro_default", - "thiserror", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-streams", - "web-sys", - "xxhash-rust", -] - -[[package]] -name = "server_fn_macro" -version = "0.6.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adfdd051ef905fdb3da20942b0c52d536158d7489a724e14cc2fd47323e7ca91" -dependencies = [ - "const_format", - "convert_case 0.6.0", - "proc-macro2", - "quote", - "syn 2.0.52", - "xxhash-rust", -] - -[[package]] -name = "server_fn_macro_default" -version = "0.6.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "060af1def72353a779fcc184c53e1965d3055a38b9e827f2259b2bff2d9c371e" -dependencies = [ - "server_fn_macro", - "syn 2.0.52", -] - -[[package]] -name = "servo_arc" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432" -dependencies = [ - "nodrop", - "stable_deref_trait", -] - -[[package]] -name = "sha2" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "simd-adler32" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" - -[[package]] -name = "siphasher" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" - -[[package]] -name = "slab" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" -dependencies = [ - "autocfg", -] - -[[package]] -name = "sledgehammer_bindgen" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcfaf791ff02f48f3518ce825d32cf419c13a43c1d8b1232f74ac89f339c46d2" -dependencies = [ - "sledgehammer_bindgen_macro", -] - -[[package]] -name = "sledgehammer_bindgen_macro" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdd941cc539bd3dc694edaf9d0c4e1221d02baa67c6b45ec04fad1024d9e8139" -dependencies = [ - "quote", - "syn 2.0.52", -] - -[[package]] -name = "sledgehammer_utils" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f20798defa0e9d4eff9ca451c7f84774c7378a9c3b5a40112cfa2b3eadb97ae2" -dependencies = [ - "lru", - "once_cell", - "rustc-hash", -] - -[[package]] -name = "smallvec" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" - -[[package]] -name = "soup3" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "471f924a40f31251afc77450e781cb26d55c0b650842efafc9c6cbd2f7cc4f9f" -dependencies = [ - "futures-channel", - "gio", - "glib", - "libc", - "soup3-sys", -] - -[[package]] -name = "soup3-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ebe8950a680a12f24f15ebe1bf70db7af98ad242d9db43596ad3108aab86c27" -dependencies = [ - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "spinning" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d4f0e86297cad2658d92a707320d87bf4e6ae1050287f51d19b67ef3f153a7b" -dependencies = [ - "lock_api", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - -[[package]] -name = "string_cache" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" -dependencies = [ - "new_debug_unreachable", - "once_cell", - "parking_lot", - "phf_shared 0.10.0", - "precomputed-hash", - "serde", -] - -[[package]] -name = "string_cache_codegen" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" -dependencies = [ - "phf_generator 0.10.0", - "phf_shared 0.10.0", - "proc-macro2", - "quote", -] - -[[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.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "system-deps" -version = "6.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30c2de8a4d8f4b823d634affc9cd2a74ec98c53a756f317e529a48046cbf71f3" -dependencies = [ - "cfg-expr", - "heck", - "pkg-config", - "toml", - "version-compare", -] - -[[package]] -name = "tao" -version = "0.26.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccba570365293ca309d60f30fdac2c5271b732dc762e6154e59c85d2c762a0a1" -dependencies = [ - "bitflags 1.3.2", - "cocoa", - "core-foundation", - "core-graphics", - "crossbeam-channel", - "dispatch", - "dlopen2", - "gdkwayland-sys", - "gdkx11-sys", - "gtk", - "image", - "instant", - "jni 0.21.1", - "lazy_static", - "libc", - "log", - "ndk", - "ndk-context", - "ndk-sys", - "objc", - "once_cell", - "parking_lot", - "png", - "raw-window-handle 0.5.2", - "raw-window-handle 0.6.0", - "scopeguard", - "tao-macros", - "unicode-segmentation", - "url", - "windows", - "windows-implement", - "windows-version", - "x11-dl", -] - -[[package]] -name = "tao-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b27a4bcc5eb524658234589bdffc7e7bfb996dbae6ce9393bfd39cb4159b445" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "target-lexicon" -version = "0.12.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a" - -[[package]] -name = "tendril" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" -dependencies = [ - "futf", - "mac", - "utf-8", -] - -[[package]] -name = "termcolor" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "thin-slice" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" - -[[package]] -name = "thiserror" -version = "1.0.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "thread_local" -version = "1.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" -dependencies = [ - "cfg-if", - "once_cell", -] - -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "to_method" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7c4ceeeca15c8384bbc3e011dbd8fccb7f068a440b752b7d9b32ceb0ca0e2e8" - -[[package]] -name = "tokio" -version = "1.29.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" -dependencies = [ - "autocfg", - "backtrace", - "bytes", - "num_cpus", - "pin-project-lite", - "tokio-macros", -] - -[[package]] -name = "tokio-macros" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "toml" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit 0.19.14", -] - -[[package]] -name = "toml_datetime" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_edit" -version = "0.19.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" -dependencies = [ - "indexmap 2.0.0", - "serde", - "serde_spanned", - "toml_datetime", - "winnow", -] - -[[package]] -name = "toml_edit" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" -dependencies = [ - "indexmap 2.0.0", - "toml_datetime", - "winnow", -] - -[[package]] -name = "tracing" -version = "0.1.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" -dependencies = [ - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "tracing-core" -version = "0.1.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" -dependencies = [ - "nu-ansi-term", - "sharded-slab", - "smallvec", - "thread_local", - "tracing-core", - "tracing-log", -] - -[[package]] -name = "typenum" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" - -[[package]] -name = "unicode-bidi" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" - -[[package]] -name = "unicode-ident" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" - -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-segmentation" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" - -[[package]] -name = "unicode-xid" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" - -[[package]] -name = "url" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", -] - -[[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 = "uuid" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" - -[[package]] -name = "valuable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - -[[package]] -name = "version-compare" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "waker-fn" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" - -[[package]] -name = "walkdir" -version = "2.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - -[[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.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn 2.0.52", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" - -[[package]] -name = "wasm-streams" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" -dependencies = [ - "futures-util", - "js-sys", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "web-sys" -version = "0.3.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webbrowser" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd222aa310eb7532e3fd427a5d7db7e44bc0b0cf1c1e21139c345325511a85b6" -dependencies = [ - "core-foundation", - "home", - "jni 0.21.1", - "log", - "ndk-context", - "objc", - "raw-window-handle 0.5.2", - "url", - "web-sys", -] - -[[package]] -name = "webkit2gtk" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76b1bc1e54c581da1e9f179d0b38512ba358fb1af2d634a1affe42e37172361a" -dependencies = [ - "bitflags 1.3.2", - "cairo-rs", - "gdk", - "gdk-sys", - "gio", - "gio-sys", - "glib", - "glib-sys", - "gobject-sys", - "gtk", - "gtk-sys", - "javascriptcore-rs", - "libc", - "once_cell", - "soup3", - "webkit2gtk-sys", -] - -[[package]] -name = "webkit2gtk-sys" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62daa38afc514d1f8f12b8693d30d5993ff77ced33ce30cd04deebc267a6d57c" -dependencies = [ - "bitflags 1.3.2", - "cairo-sys-rs", - "gdk-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "gtk-sys", - "javascriptcore-rs-sys", - "libc", - "pkg-config", - "soup3-sys", - "system-deps", -] - -[[package]] -name = "webview2-com" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0ae9c7e420783826cf769d2c06ac9ba462f450eca5893bb8c6c6529a4e5dd33" -dependencies = [ - "webview2-com-macros", - "webview2-com-sys", - "windows", - "windows-core", - "windows-implement", - "windows-interface", -] - -[[package]] -name = "webview2-com-macros" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1345798ecd8122468840bcdf1b95e5dc6d2206c5e4b0eafa078d061f59c9bc" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "webview2-com-sys" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6ad85fceee6c42fa3d61239eba5a11401bf38407a849ed5ea1b407df08cca72" -dependencies = [ - "thiserror", - "windows", - "windows-core", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[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.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" -dependencies = [ - "windows-core", - "windows-implement", - "windows-interface", - "windows-targets 0.52.4", -] - -[[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.4", -] - -[[package]] -name = "windows-implement" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12168c33176773b86799be25e2a2ba07c7aab9968b37541f1094dbd7a60c8946" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "windows-interface" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d8dc32e0095a7eeccebd0e3f09e9509365ecb3fc6ac4d6f5f14a3f6392942d1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[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.1", -] - -[[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.4", -] - -[[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.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" -dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", -] - -[[package]] -name = "windows-targets" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" -dependencies = [ - "windows_aarch64_gnullvm 0.52.4", - "windows_aarch64_msvc 0.52.4", - "windows_i686_gnu 0.52.4", - "windows_i686_msvc 0.52.4", - "windows_x86_64_gnu 0.52.4", - "windows_x86_64_gnullvm 0.52.4", - "windows_x86_64_msvc 0.52.4", -] - -[[package]] -name = "windows-version" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75aa004c988e080ad34aff5739c39d0312f4684699d6d71fc8a198d057b8b9b4" -dependencies = [ - "windows-targets 0.52.4", -] - -[[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.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" - -[[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.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" - -[[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.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" - -[[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.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" - -[[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.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" - -[[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.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" - -[[package]] -name = "winnow" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bd122eb777186e60c3fdf765a58ac76e41c582f1f535fbf3314434c6b58f3f7" -dependencies = [ - "memchr", -] - -[[package]] -name = "wry" -version = "0.35.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3016c47c9b6f7029a9da7cd48af8352327226bba0e955f3c92e2966651365a9" -dependencies = [ - "base64", - "block", - "cfg_aliases", - "cocoa", - "core-graphics", - "crossbeam-channel", - "dunce", - "gdkx11", - "gtk", - "html5ever", - "http 0.2.9", - "javascriptcore-rs", - "jni 0.21.1", - "kuchikiki", - "libc", - "log", - "ndk", - "ndk-context", - "ndk-sys", - "objc", - "objc_id", - "once_cell", - "raw-window-handle 0.5.2", - "serde", - "serde_json", - "sha2", - "soup3", - "tao-macros", - "thiserror", - "url", - "webkit2gtk", - "webkit2gtk-sys", - "webview2-com", - "windows", - "windows-implement", - "windows-version", - "x11-dl", -] - -[[package]] -name = "wry" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b717040ba9771fd88eb428c6ea6b555f8e734ff8534f02c13e8f10d97f5935e" -dependencies = [ - "base64", - "block", - "cfg_aliases", - "cocoa", - "core-graphics", - "crossbeam-channel", - "dunce", - "gdkx11", - "gtk", - "html5ever", - "http 0.2.9", - "javascriptcore-rs", - "jni 0.21.1", - "kuchikiki", - "libc", - "log", - "ndk", - "ndk-context", - "ndk-sys", - "objc", - "objc_id", - "once_cell", - "percent-encoding", - "raw-window-handle 0.6.0", - "serde", - "serde_json", - "sha2", - "soup3", - "tao-macros", - "thiserror", - "webkit2gtk", - "webkit2gtk-sys", - "webview2-com", - "windows", - "windows-implement", - "windows-version", - "x11-dl", -] - -[[package]] -name = "x11" -version = "2.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" -dependencies = [ - "libc", - "pkg-config", -] - -[[package]] -name = "x11-dl" -version = "2.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" -dependencies = [ - "libc", - "once_cell", - "pkg-config", -] - -[[package]] -name = "xxhash-rust" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927da81e25be1e1a2901d59b81b37dd2efd1fc9c9345a55007f09bf5a2d3ee03" - -[[package]] -name = "zerocopy" -version = "0.7.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", -] diff --git a/examples/mobile_demo/Cargo.toml b/examples/mobile_demo/Cargo.toml deleted file mode 100644 index 4d4155750b..0000000000 --- a/examples/mobile_demo/Cargo.toml +++ /dev/null @@ -1,51 +0,0 @@ -[package] -name = "mobile-demo" -version = "0.1.0" -authors = ["Jonathan Kelley "] -edition = "2021" - -[lib] -crate-type = ["staticlib", "cdylib", "rlib"] - -[[bin]] -name = "mobile-demo-desktop" -path = "gen/bin/desktop.rs" - -[package.metadata.cargo-android] -app-activity-name = "com.example.mobile_demo.MainActivity" -app-dependencies = [ - "androidx.webkit:webkit:1.6.1", - "androidx.appcompat:appcompat:1.6.1", - "com.google.android.material:material:1.8.0", -] -project-dependencies = ["org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21"] -app-plugins = ["org.jetbrains.kotlin.android"] -app-permissions = ["android.permission.INTERNET"] -app-theme-parent = "Theme.MaterialComponents.DayNight.DarkActionBar" -vulkan-validation = false - -[package.metadata.cargo-android.env-vars] -WRY_ANDROID_PACKAGE = "com.example.mobile_demo" -WRY_ANDROID_LIBRARY = "mobile_demo" -WRY_ANDROID_KOTLIN_FILES_OUT_DIR = "/app/src/main/kotlin/com/example/mobile_demo" - -[package.metadata.cargo-apple.ios] -frameworks = ["WebKit"] - -[dependencies] -anyhow = "1.0.56" -log = "0.4.11" -wry = "0.35.0" -dioxus = { path = "../../packages/dioxus", features = ["mobile"]} - - -[target.'cfg(target_os = "android")'.dependencies] -android_logger = "0.9.0" -jni = "0.19.0" -paste = "1.0" - -[target.'cfg(not(target_os = "android"))'.dependencies] -env_logger = "0.9.0" - -[target.'cfg(target_os = "ios")'.dependencies] -core-foundation = "0.9.3" diff --git a/examples/mobile_demo/README.md b/examples/mobile_demo/README.md deleted file mode 100644 index 4d5ea46ed0..0000000000 --- a/examples/mobile_demo/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Dioxus Mobile demo - -## How this project was generated - -Right now, Dioxus supports mobile targets including iOS and Android. However, our tooling is not mature enough to include the build commands directly. - -This project was generated using [cargo-mobile2](https://github.com/tauri-apps/cargo-mobile2). We have yet to integrate this generation into the Dioxus-CLI. The open issue for this is [#1157](https://github.com/DioxusLabs/dioxus/issues/1157). - -## Running this project - -Because the tooling and ecosystem is still young, Dioxus mobile can be difficult to setup and run. We have [detailed guides](https://dioxuslabs.com/learn/0.5/getting_started) for creating, building, and running on both iOS and Android. diff --git a/examples/mobile_demo/mobile.toml b/examples/mobile_demo/mobile.toml deleted file mode 100644 index 3b87772508..0000000000 --- a/examples/mobile_demo/mobile.toml +++ /dev/null @@ -1,8 +0,0 @@ -[app] -name = "mobile-demo" -stylized-name = "Mobile Demo" -domain = "example.com" -template-pack = "wry" - -[apple] -development-team = "34U4FG9TJ8" diff --git a/examples/mobile_demo/src/index.html b/examples/mobile_demo/src/index.html deleted file mode 100644 index b0e6290e12..0000000000 --- a/examples/mobile_demo/src/index.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - Dioxus app - - - - -
      - - - diff --git a/examples/mobile_demo/src/lib.rs b/examples/mobile_demo/src/lib.rs deleted file mode 100644 index 6fc142d9a3..0000000000 --- a/examples/mobile_demo/src/lib.rs +++ /dev/null @@ -1,90 +0,0 @@ -use anyhow::Result; -use dioxus::mobile::Config; -use dioxus::prelude::*; - -#[cfg(target_os = "android")] -use dioxus::mobile::wry::android_binding; - -#[cfg(target_os = "android")] -fn init_logging() { - android_logger::init_once( - android_logger::Config::default() - .with_min_level(log::Level::Trace) - .with_tag("mobile-demo"), - ); -} - -#[cfg(not(target_os = "android"))] -fn init_logging() { - env_logger::init(); -} - -#[cfg(any(target_os = "android", target_os = "ios"))] -fn stop_unwind T, T>(f: F) -> T { - match std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)) { - Ok(t) => t, - Err(err) => { - eprintln!("attempt to unwind out of `rust` with err: {:?}", err); - std::process::abort() - } - } -} - -#[cfg(any(target_os = "android", target_os = "ios"))] -fn _start_app() { - stop_unwind(|| main().unwrap()); -} - -#[no_mangle] -#[inline(never)] -#[cfg(any(target_os = "android", target_os = "ios"))] -pub extern "C" fn start_app() { - #[cfg(target_os = "android")] - android_binding!(com_example, mobile_demo, _start_app); - #[cfg(target_os = "ios")] - _start_app() -} - -pub fn main() -> Result<()> { - init_logging(); - - // Right now we're going through dioxus-desktop but we'd like to go through dioxus-mobile - // That will seed the index.html with some fixes that prevent the page from scrolling/zooming etc - LaunchBuilder::mobile() - .with_cfg( - // Note that we have to disable the viewport goofiness of the browser. - // Dioxus_mobile should do this for us - Config::default().with_custom_index(include_str!("index.html").to_string()), - ) - .launch(app); - - Ok(()) -} - -fn app() -> Element { - let mut items = use_signal(|| vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); - - log::debug!("Hello from the app"); - - rsx! { - div { - h1 { "Hello, Mobile" } - div { - margin_left: "auto", - margin_right: "auto", - width: "200px", - padding: "10px", - border: "1px solid black", - button { - onclick: move |_| { - items.push(items.len()); - }, - "Add item" - } - for item in items.iter() { - div { "- {item}" } - } - } - } - } -} diff --git a/examples/multiwindow.rs b/examples/multiwindow.rs index 6858f65896..d89a78ee6d 100644 --- a/examples/multiwindow.rs +++ b/examples/multiwindow.rs @@ -7,7 +7,7 @@ use dioxus::prelude::*; fn main() { - launch_desktop(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/nested_listeners.rs b/examples/nested_listeners.rs index 33cf3d4043..c2316aed37 100644 --- a/examples/nested_listeners.rs +++ b/examples/nested_listeners.rs @@ -7,7 +7,7 @@ use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/openid_connect_demo/.cargo/config.toml b/examples/openid_connect_demo/.cargo/config.toml deleted file mode 100644 index fcb494816a..0000000000 --- a/examples/openid_connect_demo/.cargo/config.toml +++ /dev/null @@ -1,5 +0,0 @@ -[env] -DIOXUS_FRONT_ISSUER_URL = "TODO" -DIOXUS_FRONT_CLIENT_ID = "TODO" -DIOXUS_FRONT_CLIENT_SECRET = "TODO" -DIOXUS_FRONT_URL = "http://localhost:8080" diff --git a/examples/openid_connect_demo/.gitignore b/examples/openid_connect_demo/.gitignore deleted file mode 100644 index 919c8ee550..0000000000 --- a/examples/openid_connect_demo/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/target -/dist -.env diff --git a/examples/openid_connect_demo/Cargo.lock b/examples/openid_connect_demo/Cargo.lock deleted file mode 100644 index b7d513c11e..0000000000 --- a/examples/openid_connect_demo/Cargo.lock +++ /dev/null @@ -1,6372 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "addr2line" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "ahash" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy", -] - -[[package]] -name = "aho-corasick" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] - -[[package]] -name = "allocator-api2" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" - -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "anyhow" -version = "1.0.86" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" - -[[package]] -name = "anymap" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33954243bd79057c2de7338850b85983a44588021f8a5fee574a8888c6de4344" - -[[package]] -name = "anymap2" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c" - -[[package]] -name = "ashpd" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd884d7c72877a94102c3715f3b1cd09ff4fac28221add3e57cfbe25c236d093" -dependencies = [ - "async-fs", - "async-net", - "enumflags2", - "futures-channel", - "futures-util", - "rand 0.8.5", - "serde", - "serde_repr", - "url", - "zbus", -] - -[[package]] -name = "askama_escape" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341" - -[[package]] -name = "async-broadcast" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20cd0e2e25ea8e5f7e9df04578dc6cf5c83577fd09b1a46aaf5c85e1c33f2a7e" -dependencies = [ - "event-listener", - "event-listener-strategy", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-channel" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" -dependencies = [ - "concurrent-queue", - "event-listener-strategy", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-executor" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8828ec6e544c02b0d6691d21ed9f9218d0384a82542855073c2a3f58304aaf0" -dependencies = [ - "async-task", - "concurrent-queue", - "fastrand", - "futures-lite", - "slab", -] - -[[package]] -name = "async-fs" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" -dependencies = [ - "async-lock", - "blocking", - "futures-lite", -] - -[[package]] -name = "async-io" -version = "2.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d6baa8f0178795da0e71bc42c9e5d13261aac7ee549853162e66a241ba17964" -dependencies = [ - "async-lock", - "cfg-if", - "concurrent-queue", - "futures-io", - "futures-lite", - "parking", - "polling", - "rustix", - "slab", - "tracing", - "windows-sys 0.52.0", -] - -[[package]] -name = "async-lock" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" -dependencies = [ - "event-listener", - "event-listener-strategy", - "pin-project-lite", -] - -[[package]] -name = "async-net" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" -dependencies = [ - "async-io", - "blocking", - "futures-lite", -] - -[[package]] -name = "async-process" -version = "2.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7eda79bbd84e29c2b308d1dc099d7de8dcc7035e48f4bf5dc4a531a44ff5e2a" -dependencies = [ - "async-channel", - "async-io", - "async-lock", - "async-signal", - "async-task", - "blocking", - "cfg-if", - "event-listener", - "futures-lite", - "rustix", - "tracing", - "windows-sys 0.52.0", -] - -[[package]] -name = "async-recursion" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "async-signal" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "329972aa325176e89114919f2a80fdae4f4c040f66a370b1a1159c6c0f94e7aa" -dependencies = [ - "async-io", - "async-lock", - "atomic-waker", - "cfg-if", - "futures-core", - "futures-io", - "rustix", - "signal-hook-registry", - "slab", - "windows-sys 0.52.0", -] - -[[package]] -name = "async-task" -version = "4.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" - -[[package]] -name = "async-trait" -version = "0.1.80" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "atk" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4af014b17dd80e8af9fa689b2d4a211ddba6eb583c1622f35d0cb543f6b17e4" -dependencies = [ - "atk-sys", - "glib", - "libc", -] - -[[package]] -name = "atk-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "251e0b7d90e33e0ba930891a505a9a35ece37b2dd37a14f3ffc306c13b980009" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "atomic-polyfill" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" -dependencies = [ - "critical-section", -] - -[[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - -[[package]] -name = "autocfg" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" - -[[package]] -name = "axum" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" -dependencies = [ - "async-trait", - "axum-core", - "axum-macros", - "base64 0.21.7", - "bytes", - "futures-util", - "http 1.1.0", - "http-body 1.0.0", - "http-body-util", - "hyper 1.3.1", - "hyper-util", - "itoa 1.0.11", - "matchit", - "memchr", - "mime", - "multer", - "percent-encoding", - "pin-project-lite", - "rustversion", - "serde", - "serde_json", - "serde_path_to_error", - "serde_urlencoded", - "sha1", - "sync_wrapper 1.0.1", - "tokio", - "tokio-tungstenite", - "tower", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "axum-core" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" -dependencies = [ - "async-trait", - "bytes", - "futures-util", - "http 1.1.0", - "http-body 1.0.0", - "http-body-util", - "mime", - "pin-project-lite", - "rustversion", - "sync_wrapper 0.1.2", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "axum-macros" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00c055ee2d014ae5981ce1016374e8213682aa14d9bf40e48ab48b5f3ef20eaa" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "backtrace" -version = "0.3.72" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17c6a35df3749d2e8bb1b7b21a976d82b15548788d2735b9d82f329268f71a11" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - -[[package]] -name = "base16ct" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "base64" -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "base64ct" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" - -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" -dependencies = [ - "serde", -] - -[[package]] -name = "block" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array 0.14.7", -] - -[[package]] -name = "blocking" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" -dependencies = [ - "async-channel", - "async-task", - "futures-io", - "futures-lite", - "piper", -] - -[[package]] -name = "bstr" -version = "1.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" -dependencies = [ - "memchr", - "serde", -] - -[[package]] -name = "bumpalo" -version = "3.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "bytes" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" - -[[package]] -name = "cairo-rs" -version = "0.18.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" -dependencies = [ - "bitflags 2.5.0", - "cairo-sys-rs", - "glib", - "libc", - "once_cell", - "thiserror", -] - -[[package]] -name = "cairo-sys-rs" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" -dependencies = [ - "glib-sys", - "libc", - "system-deps", -] - -[[package]] -name = "camino" -version = "1.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo-platform" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo_metadata" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" -dependencies = [ - "camino", - "cargo-platform", - "semver", - "serde", - "serde_json", - "thiserror", -] - -[[package]] -name = "cc" -version = "1.0.98" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" - -[[package]] -name = "cesu8" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" - -[[package]] -name = "cfb" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" -dependencies = [ - "byteorder", - "fnv", - "uuid", -] - -[[package]] -name = "cfg-expr" -version = "0.15.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" -dependencies = [ - "smallvec", - "target-lexicon", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "cfg_aliases" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" - -[[package]] -name = "chrono" -version = "0.4.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "js-sys", - "num-traits", - "serde", - "wasm-bindgen", - "windows-targets 0.52.5", -] - -[[package]] -name = "ciborium" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" -dependencies = [ - "ciborium-io", - "ciborium-ll", - "serde", -] - -[[package]] -name = "ciborium-io" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" - -[[package]] -name = "ciborium-ll" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" -dependencies = [ - "ciborium-io", - "half", -] - -[[package]] -name = "cobs" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" - -[[package]] -name = "cocoa" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" -dependencies = [ - "bitflags 1.3.2", - "block", - "cocoa-foundation", - "core-foundation", - "core-graphics", - "foreign-types", - "libc", - "objc", -] - -[[package]] -name = "cocoa-foundation" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" -dependencies = [ - "bitflags 1.3.2", - "block", - "core-foundation", - "core-graphics-types", - "libc", - "objc", -] - -[[package]] -name = "combine" -version = "4.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" -dependencies = [ - "bytes", - "memchr", -] - -[[package]] -name = "concurrent-queue" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "console_error_panic_hook" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" -dependencies = [ - "cfg-if", - "wasm-bindgen", -] - -[[package]] -name = "const-oid" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" - -[[package]] -name = "const_format" -version = "0.2.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3a214c7af3d04997541b18d432afaff4c455e79e2029079647e72fc2bd27673" -dependencies = [ - "const_format_proc_macros", -] - -[[package]] -name = "const_format_proc_macros" -version = "0.2.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - -[[package]] -name = "convert_case" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" -dependencies = [ - "unicode-segmentation", -] - -[[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-sys" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" - -[[package]] -name = "core-graphics" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "core-graphics-types", - "foreign-types", - "libc", -] - -[[package]] -name = "core-graphics-types" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "libc", -] - -[[package]] -name = "cpufeatures" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" -dependencies = [ - "libc", -] - -[[package]] -name = "crc32fast" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "critical-section" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" - -[[package]] -name = "crossbeam-channel" -version = "0.5.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" -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.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[package]] -name = "crypto-bigint" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" -dependencies = [ - "generic-array 0.14.7", - "rand_core 0.6.4", - "subtle", - "zeroize", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array 0.14.7", - "typenum", -] - -[[package]] -name = "cssparser" -version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a" -dependencies = [ - "cssparser-macros", - "dtoa-short", - "itoa 0.4.8", - "matches", - "phf 0.8.0", - "proc-macro2", - "quote", - "smallvec", - "syn 1.0.109", -] - -[[package]] -name = "cssparser-macros" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" -dependencies = [ - "quote", - "syn 2.0.66", -] - -[[package]] -name = "curve25519-dalek" -version = "4.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348" -dependencies = [ - "cfg-if", - "cpufeatures", - "curve25519-dalek-derive", - "digest", - "fiat-crypto", - "platforms", - "rustc_version", - "subtle", - "zeroize", -] - -[[package]] -name = "curve25519-dalek-derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "darling" -version = "0.20.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.20.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 2.0.66", -] - -[[package]] -name = "darling_macro" -version = "0.20.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" -dependencies = [ - "darling_core", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "dashmap" -version = "5.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" -dependencies = [ - "cfg-if", - "hashbrown 0.14.5", - "lock_api", - "once_cell", - "parking_lot_core", -] - -[[package]] -name = "data-encoding" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" - -[[package]] -name = "der" -version = "0.7.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" -dependencies = [ - "const-oid", - "pem-rfc7468", - "zeroize", -] - -[[package]] -name = "deranged" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" -dependencies = [ - "powerfmt", - "serde", -] - -[[package]] -name = "derivative" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "derive_more" -version = "0.99.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" -dependencies = [ - "convert_case 0.4.0", - "proc-macro2", - "quote", - "rustc_version", - "syn 1.0.109", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "const-oid", - "crypto-common", - "subtle", -] - -[[package]] -name = "dioxus" -version = "0.5.2" -dependencies = [ - "dioxus-config-macro", - "dioxus-core", - "dioxus-core-macro", - "dioxus-desktop", - "dioxus-fullstack", - "dioxus-hooks", - "dioxus-hot-reload", - "dioxus-html", - "dioxus-liveview", - "dioxus-router", - "dioxus-signals", - "dioxus-ssr", - "dioxus-static-site-generation", - "dioxus-web", - "serde", -] - -[[package]] -name = "dioxus-cli-config" -version = "0.5.2" -dependencies = [ - "once_cell", - "serde", - "serde_json", - "tracing", -] - -[[package]] -name = "dioxus-config-macro" -version = "0.5.2" -dependencies = [ - "proc-macro2", - "quote", -] - -[[package]] -name = "dioxus-core" -version = "0.5.2" -dependencies = [ - "futures-channel", - "futures-util", - "generational-box", - "longest-increasing-subsequence", - "rustc-hash", - "serde", - "slab", - "tracing", - "tracing-subscriber", -] - -[[package]] -name = "dioxus-core-macro" -version = "0.5.2" -dependencies = [ - "convert_case 0.6.0", - "dioxus-rsx", - "prettyplease", - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "dioxus-debug-cell" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ea539174bb236e0e7dc9c12b19b88eae3cb574dedbd0252a2d43ea7e6de13e2" - -[[package]] -name = "dioxus-desktop" -version = "0.5.2" -dependencies = [ - "async-trait", - "cocoa", - "core-foundation", - "dioxus-cli-config", - "dioxus-core", - "dioxus-hooks", - "dioxus-hot-reload", - "dioxus-html", - "dioxus-interpreter-js", - "dunce", - "futures-channel", - "futures-util", - "generational-box", - "global-hotkey", - "infer", - "muda", - "objc", - "objc_id", - "rfd", - "rustc-hash", - "serde", - "serde_json", - "signal-hook", - "slab", - "tao", - "thiserror", - "tokio", - "tracing", - "urlencoding", - "webbrowser", - "wry", -] - -[[package]] -name = "dioxus-fullstack" -version = "0.5.2" -dependencies = [ - "anymap", - "async-trait", - "axum", - "base64 0.21.7", - "bytes", - "ciborium", - "dioxus-cli-config", - "dioxus-desktop", - "dioxus-hot-reload", - "dioxus-lib", - "dioxus-ssr", - "dioxus-web", - "dioxus_server_macro", - "futures-util", - "http 1.1.0", - "hyper 1.3.1", - "once_cell", - "pin-project", - "serde", - "serde_json", - "server_fn", - "thiserror", - "tokio", - "tokio-stream", - "tokio-util", - "tower", - "tower-http", - "tower-layer", - "tracing", - "tracing-futures", - "web-sys", -] - -[[package]] -name = "dioxus-hooks" -version = "0.5.2" -dependencies = [ - "dioxus-core", - "dioxus-debug-cell", - "dioxus-signals", - "futures-channel", - "futures-util", - "generational-box", - "slab", - "thiserror", - "tracing", -] - -[[package]] -name = "dioxus-hot-reload" -version = "0.5.2" -dependencies = [ - "axum", - "chrono", - "dioxus-core", - "dioxus-html", - "dioxus-rsx", - "execute", - "futures-util", - "ignore", - "interprocess-docfix", - "notify", - "once_cell", - "serde", - "serde_json", - "tokio", - "tokio-stream", - "tracing", -] - -[[package]] -name = "dioxus-html" -version = "0.5.2" -dependencies = [ - "async-trait", - "dioxus-core", - "dioxus-html-internal-macro", - "dioxus-rsx", - "enumset", - "euclid", - "futures-channel", - "generational-box", - "keyboard-types", - "serde", - "serde-value", - "serde_json", - "serde_repr", - "tokio", - "tracing", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "dioxus-html-internal-macro" -version = "0.5.2" -dependencies = [ - "convert_case 0.6.0", - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "dioxus-interpreter-js" -version = "0.5.2" -dependencies = [ - "dioxus-core", - "dioxus-html", - "js-sys", - "md5", - "sledgehammer_bindgen", - "sledgehammer_utils", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "dioxus-lib" -version = "0.5.2" -dependencies = [ - "dioxus-config-macro", - "dioxus-core", - "dioxus-core-macro", - "dioxus-hooks", - "dioxus-html", - "dioxus-rsx", - "dioxus-signals", -] - -[[package]] -name = "dioxus-liveview" -version = "0.5.2" -dependencies = [ - "axum", - "dioxus-cli-config", - "dioxus-core", - "dioxus-hot-reload", - "dioxus-html", - "dioxus-interpreter-js", - "futures-channel", - "futures-util", - "generational-box", - "rustc-hash", - "serde", - "serde_json", - "slab", - "thiserror", - "tokio", - "tokio-stream", - "tokio-util", - "tracing", -] - -[[package]] -name = "dioxus-logger" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe09dc9773dc1f1bb0d38529203d6555d08f67aadca5cf955ac3d1a9e69880" -dependencies = [ - "console_error_panic_hook", - "tracing", - "tracing-subscriber", - "tracing-wasm", -] - -[[package]] -name = "dioxus-router" -version = "0.5.2" -dependencies = [ - "dioxus-cli-config", - "dioxus-fullstack", - "dioxus-lib", - "dioxus-router-macro", - "dioxus-ssr", - "gloo", - "gloo-utils 0.1.7", - "http 1.1.0", - "js-sys", - "tokio", - "tracing", - "url", - "urlencoding", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "dioxus-router-macro" -version = "0.5.2" -dependencies = [ - "proc-macro2", - "quote", - "slab", - "syn 2.0.66", -] - -[[package]] -name = "dioxus-rsx" -version = "0.5.2" -dependencies = [ - "dioxus-core", - "internment", - "krates", - "proc-macro2", - "quote", - "syn 2.0.66", - "tracing", -] - -[[package]] -name = "dioxus-sdk" -version = "0.5.0" -source = "git+https://github.com/Dioxuslabs/sdk#d49812fbc9d4506bd3b1ec994f45ef4447f34c79" -dependencies = [ - "cfg-if", - "dioxus", - "dioxus-signals", - "directories", - "futures-util", - "js-sys", - "once_cell", - "postcard", - "rustc-hash", - "serde", - "tokio", - "tracing", - "uuid", - "wasm-bindgen", - "web-sys", - "yazi", -] - -[[package]] -name = "dioxus-signals" -version = "0.5.2" -dependencies = [ - "dioxus-core", - "futures-channel", - "futures-util", - "generational-box", - "once_cell", - "parking_lot", - "rustc-hash", - "serde", - "tracing", -] - -[[package]] -name = "dioxus-ssr" -version = "0.5.2" -dependencies = [ - "askama_escape", - "async-trait", - "chrono", - "dioxus-cli-config", - "dioxus-core", - "dioxus-html", - "generational-box", - "http 1.1.0", - "lru", - "rustc-hash", - "serde_json", - "thiserror", - "tokio", - "tracing", -] - -[[package]] -name = "dioxus-static-site-generation" -version = "0.5.2" -dependencies = [ - "axum", - "dioxus-cli-config", - "dioxus-fullstack", - "dioxus-hot-reload", - "dioxus-lib", - "dioxus-router", - "dioxus-ssr", - "dioxus-web", - "http 1.1.0", - "tokio", - "tower", - "tower-http", - "tracing", -] - -[[package]] -name = "dioxus-web" -version = "0.5.2" -dependencies = [ - "async-trait", - "console_error_panic_hook", - "dioxus-core", - "dioxus-html", - "dioxus-interpreter-js", - "futures-channel", - "futures-util", - "generational-box", - "js-sys", - "rustc-hash", - "serde", - "serde-wasm-bindgen", - "serde_json", - "tracing", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "dioxus_server_macro" -version = "0.5.2" -dependencies = [ - "convert_case 0.6.0", - "proc-macro2", - "quote", - "server_fn_macro", - "syn 2.0.66", -] - -[[package]] -name = "directories" -version = "4.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-sys" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - -[[package]] -name = "dispatch" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" - -[[package]] -name = "dlopen2" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1297103d2bbaea85724fcee6294c2d50b1081f9ad47d0f6f6f61eda65315a6" -dependencies = [ - "dlopen2_derive", - "libc", - "once_cell", - "winapi", -] - -[[package]] -name = "dlopen2_derive" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "dtoa" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" - -[[package]] -name = "dtoa-short" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbaceec3c6e4211c79e7b1800fb9680527106beb2f9c51904a3210c03a448c74" -dependencies = [ - "dtoa", -] - -[[package]] -name = "dunce" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" - -[[package]] -name = "dyn-clone" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" - -[[package]] -name = "ecdsa" -version = "0.16.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" -dependencies = [ - "der", - "digest", - "elliptic-curve", - "rfc6979", - "signature", - "spki", -] - -[[package]] -name = "ed25519" -version = "2.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" -dependencies = [ - "pkcs8", - "signature", -] - -[[package]] -name = "ed25519-dalek" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" -dependencies = [ - "curve25519-dalek", - "ed25519", - "serde", - "sha2", - "subtle", - "zeroize", -] - -[[package]] -name = "either" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" - -[[package]] -name = "elliptic-curve" -version = "0.13.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" -dependencies = [ - "base16ct", - "crypto-bigint", - "digest", - "ff", - "generic-array 0.14.7", - "group", - "hkdf", - "pem-rfc7468", - "pkcs8", - "rand_core 0.6.4", - "sec1", - "subtle", - "zeroize", -] - -[[package]] -name = "embedded-io" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" - -[[package]] -name = "encoding_rs" -version = "0.8.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" -dependencies = [ - "cfg-if", -] - -[[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.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3278c9d5fb675e0a51dabcf4c0d355f692b064171535ba72361be1528a9d8e8d" -dependencies = [ - "enumflags2_derive", - "serde", -] - -[[package]] -name = "enumflags2_derive" -version = "0.7.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c785274071b1b420972453b306eeca06acf4633829db4223b58a2a8c5953bc4" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "enumset" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "226c0da7462c13fb57e5cc9e0dc8f0635e7d27f276a3a7fd30054647f669007d" -dependencies = [ - "enumset_derive", -] - -[[package]] -name = "enumset_derive" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08b6c6ab82d70f08844964ba10c7babb716de2ecaeab9be5717918a5177d3af" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[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.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "euclid" -version = "0.22.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0f0eb73b934648cd7a4a61f1b15391cd95dab0b4da6e2e66c2a072c144b4a20" -dependencies = [ - "num-traits", - "serde", -] - -[[package]] -name = "event-listener" -version = "5.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener-strategy" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" -dependencies = [ - "event-listener", - "pin-project-lite", -] - -[[package]] -name = "execute" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a82608ee96ce76aeab659e9b8d3c2b787bffd223199af88c674923d861ada10" -dependencies = [ - "execute-command-macro", - "execute-command-tokens", - "generic-array 1.0.0", -] - -[[package]] -name = "execute-command-macro" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90dec53d547564e911dc4ff3ecb726a64cf41a6fa01a2370ebc0d95175dd08bd" -dependencies = [ - "execute-command-macro-impl", -] - -[[package]] -name = "execute-command-macro-impl" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce8cd46a041ad005ab9c71263f9a0ff5b529eac0fe4cc9b4a20f4f0765d8cf4b" -dependencies = [ - "execute-command-tokens", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "execute-command-tokens" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69dc321eb6be977f44674620ca3aa21703cb20ffbe560e1ae97da08401ffbcad" - -[[package]] -name = "fastrand" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" - -[[package]] -name = "fdeflate" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" -dependencies = [ - "simd-adler32", -] - -[[package]] -name = "ff" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" -dependencies = [ - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "fiat-crypto" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" - -[[package]] -name = "field-offset" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" -dependencies = [ - "memoffset", - "rustc_version", -] - -[[package]] -name = "filetime" -version = "0.2.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall 0.4.1", - "windows-sys 0.52.0", -] - -[[package]] -name = "fixedbitset" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" - -[[package]] -name = "flate2" -version = "1.0.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foreign-types" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" -dependencies = [ - "foreign-types-macros", - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-macros" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[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 = "fsevent-sys" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" -dependencies = [ - "libc", -] - -[[package]] -name = "futf" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" -dependencies = [ - "mac", - "new_debug_unreachable", -] - -[[package]] -name = "futures" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" - -[[package]] -name = "futures-executor" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" - -[[package]] -name = "futures-lite" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" -dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "parking", - "pin-project-lite", -] - -[[package]] -name = "futures-macro" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "futures-sink" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" - -[[package]] -name = "futures-task" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" - -[[package]] -name = "futures-util" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "fxhash" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" -dependencies = [ - "byteorder", -] - -[[package]] -name = "gdk" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5ba081bdef3b75ebcdbfc953699ed2d7417d6bd853347a42a37d76406a33646" -dependencies = [ - "cairo-rs", - "gdk-pixbuf", - "gdk-sys", - "gio", - "glib", - "libc", - "pango", -] - -[[package]] -name = "gdk-pixbuf" -version = "0.18.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" -dependencies = [ - "gdk-pixbuf-sys", - "gio", - "glib", - "libc", - "once_cell", -] - -[[package]] -name = "gdk-pixbuf-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" -dependencies = [ - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "gdk-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31ff856cb3386dae1703a920f803abafcc580e9b5f711ca62ed1620c25b51ff2" -dependencies = [ - "cairo-sys-rs", - "gdk-pixbuf-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "pango-sys", - "pkg-config", - "system-deps", -] - -[[package]] -name = "gdkwayland-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a90fbf5c033c65d93792192a49a8efb5bb1e640c419682a58bb96f5ae77f3d4a" -dependencies = [ - "gdk-sys", - "glib-sys", - "gobject-sys", - "libc", - "pkg-config", - "system-deps", -] - -[[package]] -name = "gdkx11" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2ea8a4909d530f79921290389cbd7c34cb9d623bfe970eaae65ca5f9cd9cce" -dependencies = [ - "gdk", - "gdkx11-sys", - "gio", - "glib", - "libc", - "x11", -] - -[[package]] -name = "gdkx11-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fee8f00f4ee46cad2939b8990f5c70c94ff882c3028f3cc5abf950fa4ab53043" -dependencies = [ - "gdk-sys", - "glib-sys", - "libc", - "system-deps", - "x11", -] - -[[package]] -name = "generational-box" -version = "0.5.2" -dependencies = [ - "parking_lot", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", - "zeroize", -] - -[[package]] -name = "generic-array" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe739944a5406424e080edccb6add95685130b9f160d5407c639c7df0c5836b0" -dependencies = [ - "typenum", -] - -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - -[[package]] -name = "getrandom" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi 0.11.0+wasi-snapshot-preview1", - "wasm-bindgen", -] - -[[package]] -name = "gimli" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" - -[[package]] -name = "gio" -version = "0.18.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-util", - "gio-sys", - "glib", - "libc", - "once_cell", - "pin-project-lite", - "smallvec", - "thiserror", -] - -[[package]] -name = "gio-sys" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", - "winapi", -] - -[[package]] -name = "glib" -version = "0.18.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" -dependencies = [ - "bitflags 2.5.0", - "futures-channel", - "futures-core", - "futures-executor", - "futures-task", - "futures-util", - "gio-sys", - "glib-macros", - "glib-sys", - "gobject-sys", - "libc", - "memchr", - "once_cell", - "smallvec", - "thiserror", -] - -[[package]] -name = "glib-macros" -version = "0.18.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" -dependencies = [ - "heck 0.4.1", - "proc-macro-crate 2.0.2", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "glib-sys" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" -dependencies = [ - "libc", - "system-deps", -] - -[[package]] -name = "global-hotkey" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89cb13e8c52c87e28a46eae3e5e65b8f0cd465c4c9e67b13d56c70412e792bc3" -dependencies = [ - "bitflags 2.5.0", - "cocoa", - "crossbeam-channel", - "keyboard-types", - "objc", - "once_cell", - "thiserror", - "windows-sys 0.52.0", - "x11-dl", -] - -[[package]] -name = "globset" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" -dependencies = [ - "aho-corasick", - "bstr", - "log", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "gloo" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28999cda5ef6916ffd33fb4a7b87e1de633c47c0dc6d97905fee1cdaa142b94d" -dependencies = [ - "gloo-console", - "gloo-dialogs", - "gloo-events", - "gloo-file", - "gloo-history", - "gloo-net 0.3.1", - "gloo-render", - "gloo-storage", - "gloo-timers", - "gloo-utils 0.1.7", - "gloo-worker", -] - -[[package]] -name = "gloo-console" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b7ce3c05debe147233596904981848862b068862e9ec3e34be446077190d3f" -dependencies = [ - "gloo-utils 0.1.7", - "js-sys", - "serde", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gloo-dialogs" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67062364ac72d27f08445a46cab428188e2e224ec9e37efdba48ae8c289002e6" -dependencies = [ - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gloo-events" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b107f8abed8105e4182de63845afcc7b69c098b7852a813ea7462a320992fc" -dependencies = [ - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gloo-file" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d5564e570a38b43d78bdc063374a0c3098c4f0d64005b12f9bbe87e869b6d7" -dependencies = [ - "gloo-events", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gloo-history" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85725d90bf0ed47063b3930ef28e863658a7905989e9929a8708aab74a1d5e7f" -dependencies = [ - "gloo-events", - "gloo-utils 0.1.7", - "serde", - "serde-wasm-bindgen", - "serde_urlencoded", - "thiserror", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gloo-net" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a66b4e3c7d9ed8d315fd6b97c8b1f74a7c6ecbbc2320e65ae7ed38b7068cc620" -dependencies = [ - "futures-channel", - "futures-core", - "futures-sink", - "gloo-utils 0.1.7", - "http 0.2.12", - "js-sys", - "pin-project", - "serde", - "serde_json", - "thiserror", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "gloo-net" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43aaa242d1239a8822c15c645f02166398da4f8b5c4bae795c1f5b44e9eee173" -dependencies = [ - "futures-channel", - "futures-core", - "futures-sink", - "gloo-utils 0.2.0", - "http 0.2.12", - "js-sys", - "pin-project", - "serde", - "serde_json", - "thiserror", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "gloo-render" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd9306aef67cfd4449823aadcd14e3958e0800aa2183955a309112a84ec7764" -dependencies = [ - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gloo-storage" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d6ab60bf5dbfd6f0ed1f7843da31b41010515c745735c970e821945ca91e480" -dependencies = [ - "gloo-utils 0.1.7", - "js-sys", - "serde", - "serde_json", - "thiserror", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gloo-timers" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "gloo-utils" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037fcb07216cb3a30f7292bd0176b050b7b9a052ba830ef7d5d65f6dc64ba58e" -dependencies = [ - "js-sys", - "serde", - "serde_json", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gloo-utils" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" -dependencies = [ - "js-sys", - "serde", - "serde_json", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gloo-worker" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13471584da78061a28306d1359dd0178d8d6fc1c7c80e5e35d27260346e0516a" -dependencies = [ - "anymap2", - "bincode", - "gloo-console", - "gloo-utils 0.1.7", - "js-sys", - "serde", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "gobject-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" -dependencies = [ - "glib-sys", - "libc", - "system-deps", -] - -[[package]] -name = "group" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" -dependencies = [ - "ff", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "gtk" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93c4f5e0e20b60e10631a5f06da7fe3dda744b05ad0ea71fee2f47adf865890c" -dependencies = [ - "atk", - "cairo-rs", - "field-offset", - "futures-channel", - "gdk", - "gdk-pixbuf", - "gio", - "glib", - "gtk-sys", - "gtk3-macros", - "libc", - "pango", - "pkg-config", -] - -[[package]] -name = "gtk-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771437bf1de2c1c0b496c11505bdf748e26066bbe942dfc8f614c9460f6d7722" -dependencies = [ - "atk-sys", - "cairo-sys-rs", - "gdk-pixbuf-sys", - "gdk-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "pango-sys", - "system-deps", -] - -[[package]] -name = "gtk3-macros" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6063efb63db582968fb7df72e1ae68aa6360dcfb0a75143f34fc7d616bad75e" -dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "h2" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http 0.2.12", - "indexmap 2.2.6", - "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", - "crunchy", -] - -[[package]] -name = "hash32" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" -dependencies = [ - "byteorder", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", - "allocator-api2", -] - -[[package]] -name = "heapless" -version = "0.7.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" -dependencies = [ - "atomic-polyfill", - "hash32", - "rustc_version", - "serde", - "spin 0.9.8", - "stable_deref_trait", -] - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hkdf" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" -dependencies = [ - "hmac", -] - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] - -[[package]] -name = "home" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" -dependencies = [ - "windows-sys 0.52.0", -] - -[[package]] -name = "html5ever" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" -dependencies = [ - "log", - "mac", - "markup5ever", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "http" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" -dependencies = [ - "bytes", - "fnv", - "itoa 1.0.11", -] - -[[package]] -name = "http" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" -dependencies = [ - "bytes", - "fnv", - "itoa 1.0.11", -] - -[[package]] -name = "http-body" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" -dependencies = [ - "bytes", - "http 0.2.12", - "pin-project-lite", -] - -[[package]] -name = "http-body" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" -dependencies = [ - "bytes", - "http 1.1.0", -] - -[[package]] -name = "http-body-util" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" -dependencies = [ - "bytes", - "futures-core", - "http 1.1.0", - "http-body 1.0.0", - "pin-project-lite", -] - -[[package]] -name = "http-range-header" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08a397c49fec283e3d6211adbe480be95aae5f304cfb923e9970e08956d5168a" - -[[package]] -name = "httparse" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" - -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - -[[package]] -name = "hyper" -version = "0.14.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http 0.2.12", - "http-body 0.4.6", - "httparse", - "httpdate", - "itoa 1.0.11", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "http 1.1.0", - "http-body 1.0.0", - "httparse", - "httpdate", - "itoa 1.0.11", - "pin-project-lite", - "smallvec", - "tokio", -] - -[[package]] -name = "hyper-rustls" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" -dependencies = [ - "futures-util", - "http 0.2.12", - "hyper 0.14.29", - "rustls", - "tokio", - "tokio-rustls", -] - -[[package]] -name = "hyper-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b875924a60b96e5d7b9ae7b066540b1dd1cbd90d1828f54c92e02a283351c56" -dependencies = [ - "bytes", - "futures-util", - "http 1.1.0", - "http-body 1.0.0", - "hyper 1.3.1", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" -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 = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "idna" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "ignore" -version = "0.4.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1" -dependencies = [ - "crossbeam-deque", - "globset", - "log", - "memchr", - "regex-automata", - "same-file", - "walkdir", - "winapi-util", -] - -[[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.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" -dependencies = [ - "equivalent", - "hashbrown 0.14.5", - "serde", -] - -[[package]] -name = "infer" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6c16b11a665b26aeeb9b1d7f954cdeb034be38dd00adab4f2ae921a8fee804" -dependencies = [ - "cfb", -] - -[[package]] -name = "inotify" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" -dependencies = [ - "bitflags 1.3.2", - "inotify-sys", - "libc", -] - -[[package]] -name = "inotify-sys" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" -dependencies = [ - "libc", -] - -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "internment" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04e8e537b529b8674e97e9fb82c10ff168a290ac3867a0295f112061ffbca1ef" -dependencies = [ - "hashbrown 0.14.5", - "parking_lot", -] - -[[package]] -name = "interprocess-docfix" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b84ee245c606aeb0841649a9288e3eae8c61b853a8cd5c0e14450e96d53d28f" -dependencies = [ - "blocking", - "cfg-if", - "futures-core", - "futures-io", - "intmap", - "libc", - "once_cell", - "rustc_version", - "spinning", - "thiserror", - "to_method", - "winapi", -] - -[[package]] -name = "intmap" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae52f28f45ac2bc96edb7714de995cffc174a395fb0abf5bff453587c980d7b9" - -[[package]] -name = "inventory" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f958d3d68f4167080a18141e10381e7634563984a537f2a49a30fd8e53ac5767" - -[[package]] -name = "ipnet" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" - -[[package]] -name = "itoa" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" - -[[package]] -name = "javascriptcore-rs" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca5671e9ffce8ffba57afc24070e906da7fc4b1ba66f2cabebf61bf2ea257fcc" -dependencies = [ - "bitflags 1.3.2", - "glib", - "javascriptcore-rs-sys", -] - -[[package]] -name = "javascriptcore-rs-sys" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1be78d14ffa4b75b66df31840478fef72b51f8c2465d4ca7c194da9f7a5124" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "jni" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" -dependencies = [ - "cesu8", - "cfg-if", - "combine", - "jni-sys", - "log", - "thiserror", - "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 = "js-sys" -version = "0.3.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "keyboard-types" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" -dependencies = [ - "bitflags 2.5.0", - "serde", - "unicode-segmentation", -] - -[[package]] -name = "kqueue" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" -dependencies = [ - "kqueue-sys", - "libc", -] - -[[package]] -name = "kqueue-sys" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" -dependencies = [ - "bitflags 1.3.2", - "libc", -] - -[[package]] -name = "krates" -version = "0.16.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcb3baf2360eb25ad31f0ada3add63927ada6db457791979b82ac199f835cb9" -dependencies = [ - "cargo-platform", - "cargo_metadata", - "cfg-expr", - "petgraph", - "semver", -] - -[[package]] -name = "kuchikiki" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e4755b7b995046f510a7520c42b2fed58b77bd94d5a87a8eb43d2fd126da8" -dependencies = [ - "cssparser", - "html5ever", - "indexmap 1.9.3", - "matches", - "selectors", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -dependencies = [ - "spin 0.5.2", -] - -[[package]] -name = "libc" -version = "0.2.155" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" - -[[package]] -name = "libm" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" - -[[package]] -name = "libredox" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" -dependencies = [ - "bitflags 2.5.0", - "libc", -] - -[[package]] -name = "libxdo" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00333b8756a3d28e78def82067a377de7fa61b24909000aeaa2b446a948d14db" -dependencies = [ - "libxdo-sys", -] - -[[package]] -name = "libxdo-sys" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db23b9e7e2b7831bbd8aac0bbeeeb7b68cbebc162b227e7052e8e55829a09212" -dependencies = [ - "libc", - "x11", -] - -[[package]] -name = "linux-raw-sys" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" - -[[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 = "log" -version = "0.4.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" - -[[package]] -name = "longest-increasing-subsequence" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3bd0dd2cd90571056fdb71f6275fada10131182f84899f4b2a916e565d81d86" - -[[package]] -name = "lru" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" -dependencies = [ - "hashbrown 0.14.5", -] - -[[package]] -name = "mac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" - -[[package]] -name = "malloc_buf" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" -dependencies = [ - "libc", -] - -[[package]] -name = "markup5ever" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" -dependencies = [ - "log", - "phf 0.10.1", - "phf_codegen 0.10.0", - "string_cache", - "string_cache_codegen", - "tendril", -] - -[[package]] -name = "matches" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" - -[[package]] -name = "matchit" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" - -[[package]] -name = "md5" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" - -[[package]] -name = "memchr" -version = "2.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" - -[[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 = "mime_guess" -version = "2.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" -dependencies = [ - "mime", - "unicase", -] - -[[package]] -name = "miniz_oxide" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" -dependencies = [ - "adler", - "simd-adler32", -] - -[[package]] -name = "mio" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" -dependencies = [ - "libc", - "log", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.48.0", -] - -[[package]] -name = "muda" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c47e7625990fc1af2226ea4f34fb2412b03c12639fcb91868581eb3a6893453" -dependencies = [ - "cocoa", - "crossbeam-channel", - "gtk", - "keyboard-types", - "libxdo", - "objc", - "once_cell", - "png", - "thiserror", - "windows-sys 0.52.0", -] - -[[package]] -name = "multer" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b" -dependencies = [ - "bytes", - "encoding_rs", - "futures-util", - "http 1.1.0", - "httparse", - "memchr", - "mime", - "spin 0.9.8", - "version_check", -] - -[[package]] -name = "ndk" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" -dependencies = [ - "bitflags 1.3.2", - "jni-sys", - "ndk-sys", - "num_enum", - "raw-window-handle 0.5.2", - "thiserror", -] - -[[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.4.1+23.1.7779620" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cf2aae958bd232cac5069850591667ad422d263686d75b52a065f9badeee5a3" -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.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" -dependencies = [ - "bitflags 2.5.0", - "cfg-if", - "libc", - "memoffset", -] - -[[package]] -name = "nodrop" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" - -[[package]] -name = "notify" -version = "5.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "729f63e1ca555a43fe3efa4f3efdf4801c479da85b432242a7b726f353c88486" -dependencies = [ - "bitflags 1.3.2", - "crossbeam-channel", - "filetime", - "fsevent-sys", - "inotify", - "kqueue", - "libc", - "mio", - "walkdir", - "windows-sys 0.45.0", -] - -[[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "winapi", -] - -[[package]] -name = "num-bigint-dig" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" -dependencies = [ - "byteorder", - "lazy_static", - "libm", - "num-integer", - "num-iter", - "num-traits", - "rand 0.8.5", - "smallvec", - "zeroize", -] - -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - -[[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-iter" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" -dependencies = [ - "autocfg", - "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", - "libm", -] - -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "num_enum" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" -dependencies = [ - "num_enum_derive", -] - -[[package]] -name = "num_enum_derive" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" -dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "oauth2" -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c38841cdd844847e3e7c8d29cef9dcfed8877f8f56f9071f77843ecf3baf937f" -dependencies = [ - "base64 0.13.1", - "chrono", - "getrandom 0.2.15", - "http 0.2.12", - "rand 0.8.5", - "reqwest", - "serde", - "serde_json", - "serde_path_to_error", - "sha2", - "thiserror", - "url", -] - -[[package]] -name = "objc" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" -dependencies = [ - "malloc_buf", - "objc_exception", -] - -[[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_exception" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" -dependencies = [ - "cc", -] - -[[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.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" - -[[package]] -name = "openid_auth_demo" -version = "0.1.0" -dependencies = [ - "anyhow", - "console_error_panic_hook", - "dioxus", - "dioxus-logger", - "dioxus-sdk", - "form_urlencoded", - "log", - "openidconnect", - "serde", - "uuid", -] - -[[package]] -name = "openidconnect" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f47e80a9cfae4462dd29c41e987edd228971d6565553fbc14b8a11e666d91590" -dependencies = [ - "base64 0.13.1", - "chrono", - "dyn-clone", - "ed25519-dalek", - "hmac", - "http 0.2.12", - "itertools", - "log", - "oauth2", - "p256", - "p384", - "rand 0.8.5", - "rsa", - "serde", - "serde-value", - "serde_derive", - "serde_json", - "serde_path_to_error", - "serde_plain", - "serde_with", - "sha2", - "subtle", - "thiserror", - "url", -] - -[[package]] -name = "ordered-float" -version = "2.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" -dependencies = [ - "num-traits", -] - -[[package]] -name = "ordered-stream" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" -dependencies = [ - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - -[[package]] -name = "p256" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" -dependencies = [ - "ecdsa", - "elliptic-curve", - "primeorder", - "sha2", -] - -[[package]] -name = "p384" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" -dependencies = [ - "ecdsa", - "elliptic-curve", - "primeorder", - "sha2", -] - -[[package]] -name = "pango" -version = "0.18.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" -dependencies = [ - "gio", - "glib", - "libc", - "once_cell", - "pango-sys", -] - -[[package]] -name = "pango-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "parking" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" - -[[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", - "libc", - "redox_syscall 0.5.1", - "smallvec", - "windows-targets 0.52.5", -] - -[[package]] -name = "pem-rfc7468" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" -dependencies = [ - "base64ct", -] - -[[package]] -name = "percent-encoding" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" - -[[package]] -name = "petgraph" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" -dependencies = [ - "fixedbitset", - "indexmap 2.2.6", -] - -[[package]] -name = "phf" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" -dependencies = [ - "phf_macros", - "phf_shared 0.8.0", - "proc-macro-hack", -] - -[[package]] -name = "phf" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" -dependencies = [ - "phf_shared 0.10.0", -] - -[[package]] -name = "phf_codegen" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" -dependencies = [ - "phf_generator 0.8.0", - "phf_shared 0.8.0", -] - -[[package]] -name = "phf_codegen" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" -dependencies = [ - "phf_generator 0.10.0", - "phf_shared 0.10.0", -] - -[[package]] -name = "phf_generator" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" -dependencies = [ - "phf_shared 0.8.0", - "rand 0.7.3", -] - -[[package]] -name = "phf_generator" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" -dependencies = [ - "phf_shared 0.10.0", - "rand 0.8.5", -] - -[[package]] -name = "phf_macros" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" -dependencies = [ - "phf_generator 0.8.0", - "phf_shared 0.8.0", - "proc-macro-hack", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "phf_shared" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" -dependencies = [ - "siphasher", -] - -[[package]] -name = "phf_shared" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" -dependencies = [ - "siphasher", -] - -[[package]] -name = "pin-project" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" - -[[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.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1d5c74c9876f070d3e8fd503d748c7d974c3e48da8f41350fa5222ef9b4391" -dependencies = [ - "atomic-waker", - "fastrand", - "futures-io", -] - -[[package]] -name = "pkcs1" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" -dependencies = [ - "der", - "pkcs8", - "spki", -] - -[[package]] -name = "pkcs8" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" -dependencies = [ - "der", - "spki", -] - -[[package]] -name = "pkg-config" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" - -[[package]] -name = "platforms" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7" - -[[package]] -name = "png" -version = "0.17.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" -dependencies = [ - "bitflags 1.3.2", - "crc32fast", - "fdeflate", - "flate2", - "miniz_oxide", -] - -[[package]] -name = "polling" -version = "3.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6a007746f34ed64099e88783b0ae369eaa3da6392868ba262e2af9b8fbaea1" -dependencies = [ - "cfg-if", - "concurrent-queue", - "hermit-abi", - "pin-project-lite", - "rustix", - "tracing", - "windows-sys 0.52.0", -] - -[[package]] -name = "pollster" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2" - -[[package]] -name = "postcard" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a55c51ee6c0db07e68448e336cf8ea4131a620edefebf9893e759b2d793420f8" -dependencies = [ - "cobs", - "embedded-io", - "heapless", - "serde", -] - -[[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.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "precomputed-hash" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" - -[[package]] -name = "prettyplease" -version = "0.2.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" -dependencies = [ - "proc-macro2", - "syn 2.0.66", -] - -[[package]] -name = "primeorder" -version = "0.13.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" -dependencies = [ - "elliptic-curve", -] - -[[package]] -name = "proc-macro-crate" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" -dependencies = [ - "once_cell", - "toml_edit 0.19.15", -] - -[[package]] -name = "proc-macro-crate" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" -dependencies = [ - "toml_datetime", - "toml_edit 0.20.2", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro-hack" -version = "0.5.20+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" - -[[package]] -name = "proc-macro2" -version = "1.0.85" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", - "rand_pcg", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", -] - -[[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 0.6.4", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.15", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "rand_pcg" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "raw-window-handle" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" - -[[package]] -name = "raw-window-handle" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" - -[[package]] -name = "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.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" -dependencies = [ - "bitflags 2.5.0", -] - -[[package]] -name = "redox_users" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" -dependencies = [ - "getrandom 0.2.15", - "libredox", - "thiserror", -] - -[[package]] -name = "regex" -version = "1.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" - -[[package]] -name = "reqwest" -version = "0.11.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" -dependencies = [ - "base64 0.21.7", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.29", - "hyper-rustls", - "ipnet", - "js-sys", - "log", - "mime", - "mime_guess", - "once_cell", - "percent-encoding", - "pin-project-lite", - "rustls", - "rustls-pemfile", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper 0.1.2", - "system-configuration", - "tokio", - "tokio-rustls", - "tokio-util", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-streams", - "web-sys", - "webpki-roots", - "winreg", -] - -[[package]] -name = "rfc6979" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" -dependencies = [ - "hmac", - "subtle", -] - -[[package]] -name = "rfd" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25a73a7337fc24366edfca76ec521f51877b114e42dab584008209cca6719251" -dependencies = [ - "ashpd", - "block", - "dispatch", - "js-sys", - "log", - "objc", - "objc-foundation", - "objc_id", - "pollster", - "raw-window-handle 0.6.2", - "urlencoding", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "windows-sys 0.48.0", -] - -[[package]] -name = "ring" -version = "0.17.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" -dependencies = [ - "cc", - "cfg-if", - "getrandom 0.2.15", - "libc", - "spin 0.9.8", - "untrusted", - "windows-sys 0.52.0", -] - -[[package]] -name = "rsa" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" -dependencies = [ - "const-oid", - "digest", - "num-bigint-dig", - "num-integer", - "num-traits", - "pkcs1", - "pkcs8", - "rand_core 0.6.4", - "signature", - "spki", - "subtle", - "zeroize", -] - -[[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_version" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver", -] - -[[package]] -name = "rustix" -version = "0.38.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" -dependencies = [ - "bitflags 2.5.0", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.52.0", -] - -[[package]] -name = "rustls" -version = "0.21.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" -dependencies = [ - "log", - "ring", - "rustls-webpki", - "sct", -] - -[[package]] -name = "rustls-pemfile" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" -dependencies = [ - "base64 0.21.7", -] - -[[package]] -name = "rustls-webpki" -version = "0.101.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "rustversion" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" - -[[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 = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "sct" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "sec1" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" -dependencies = [ - "base16ct", - "der", - "generic-array 0.14.7", - "pkcs8", - "subtle", - "zeroize", -] - -[[package]] -name = "selectors" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe" -dependencies = [ - "bitflags 1.3.2", - "cssparser", - "derive_more", - "fxhash", - "log", - "matches", - "phf 0.8.0", - "phf_codegen 0.8.0", - "precomputed-hash", - "servo_arc", - "smallvec", - "thin-slice", -] - -[[package]] -name = "semver" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" -dependencies = [ - "serde", -] - -[[package]] -name = "send_wrapper" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" -dependencies = [ - "futures-core", -] - -[[package]] -name = "serde" -version = "1.0.203" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde-value" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" -dependencies = [ - "ordered-float", - "serde", -] - -[[package]] -name = "serde-wasm-bindgen" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3b143e2833c57ab9ad3ea280d21fd34e285a42837aeb0ee301f4f41890fa00e" -dependencies = [ - "js-sys", - "serde", - "wasm-bindgen", -] - -[[package]] -name = "serde_derive" -version = "1.0.203" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "serde_json" -version = "1.0.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" -dependencies = [ - "itoa 1.0.11", - "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 1.0.11", - "serde", -] - -[[package]] -name = "serde_plain" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1fc6db65a611022b23a0dec6975d63fb80a302cb3388835ff02c097258d50" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_qs" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0431a35568651e363364210c91983c1da5eb29404d9f0928b67d4ebcfa7d330c" -dependencies = [ - "percent-encoding", - "serde", - "thiserror", -] - -[[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.66", -] - -[[package]] -name = "serde_spanned" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" -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 1.0.11", - "ryu", - "serde", -] - -[[package]] -name = "serde_with" -version = "3.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad483d2ab0149d5a5ebcd9972a3852711e0153d863bf5a5d0391d28883c4a20" -dependencies = [ - "base64 0.22.1", - "chrono", - "hex", - "indexmap 1.9.3", - "indexmap 2.2.6", - "serde", - "serde_derive", - "serde_json", - "serde_with_macros", - "time", -] - -[[package]] -name = "serde_with_macros" -version = "3.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65569b702f41443e8bc8bbb1c5779bd0450bbe723b56198980e80ec45780bce2" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "server_fn" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06e6e5467a2cd93ce1accfdfd8b859404f0b3b2041131ffd774fabf666b8219" -dependencies = [ - "axum", - "bytes", - "const_format", - "dashmap", - "futures", - "gloo-net 0.5.0", - "http 1.1.0", - "http-body-util", - "hyper 1.3.1", - "inventory", - "js-sys", - "once_cell", - "reqwest", - "send_wrapper", - "serde", - "serde_json", - "serde_qs", - "server_fn_macro_default", - "thiserror", - "tower", - "tower-layer", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-streams", - "web-sys", - "xxhash-rust", -] - -[[package]] -name = "server_fn_macro" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09c216bb1c1ac890151397643c663c875a1836adf0b269be4e389cb1b48c173c" -dependencies = [ - "const_format", - "convert_case 0.6.0", - "proc-macro2", - "quote", - "syn 2.0.66", - "xxhash-rust", -] - -[[package]] -name = "server_fn_macro_default" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00783df297ec85ea605779f2fef9cbec98981dffe2e01e1a9845c102ee1f1ae6" -dependencies = [ - "server_fn_macro", - "syn 2.0.66", -] - -[[package]] -name = "servo_arc" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432" -dependencies = [ - "nodrop", - "stable_deref_trait", -] - -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha2" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "signal-hook" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" -dependencies = [ - "libc", - "signal-hook-registry", -] - -[[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 = "signature" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" -dependencies = [ - "digest", - "rand_core 0.6.4", -] - -[[package]] -name = "simd-adler32" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" - -[[package]] -name = "siphasher" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" - -[[package]] -name = "slab" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] - -[[package]] -name = "sledgehammer_bindgen" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcfaf791ff02f48f3518ce825d32cf419c13a43c1d8b1232f74ac89f339c46d2" -dependencies = [ - "sledgehammer_bindgen_macro", - "wasm-bindgen", -] - -[[package]] -name = "sledgehammer_bindgen_macro" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdd941cc539bd3dc694edaf9d0c4e1221d02baa67c6b45ec04fad1024d9e8139" -dependencies = [ - "quote", - "syn 2.0.66", -] - -[[package]] -name = "sledgehammer_utils" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f20798defa0e9d4eff9ca451c7f84774c7378a9c3b5a40112cfa2b3eadb97ae2" -dependencies = [ - "lru", - "once_cell", - "rustc-hash", -] - -[[package]] -name = "smallvec" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" - -[[package]] -name = "socket2" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "soup3" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "471f924a40f31251afc77450e781cb26d55c0b650842efafc9c6cbd2f7cc4f9f" -dependencies = [ - "futures-channel", - "gio", - "glib", - "libc", - "soup3-sys", -] - -[[package]] -name = "soup3-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ebe8950a680a12f24f15ebe1bf70db7af98ad242d9db43596ad3108aab86c27" -dependencies = [ - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] - -[[package]] -name = "spinning" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d4f0e86297cad2658d92a707320d87bf4e6ae1050287f51d19b67ef3f153a7b" -dependencies = [ - "lock_api", -] - -[[package]] -name = "spki" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" -dependencies = [ - "base64ct", - "der", -] - -[[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 = "string_cache" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" -dependencies = [ - "new_debug_unreachable", - "once_cell", - "parking_lot", - "phf_shared 0.10.0", - "precomputed-hash", - "serde", -] - -[[package]] -name = "string_cache_codegen" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" -dependencies = [ - "phf_generator 0.10.0", - "phf_shared 0.10.0", - "proc-macro2", - "quote", -] - -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - -[[package]] -name = "subtle" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" - -[[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.66" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "sync_wrapper" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - -[[package]] -name = "sync_wrapper" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" - -[[package]] -name = "system-configuration" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" -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 0.5.0", - "pkg-config", - "toml", - "version-compare", -] - -[[package]] -name = "tao" -version = "0.26.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69ebbccb78deb5a36744c079eea2981b4a48ecbbe6b1b2ffbaa528bea3f5e5db" -dependencies = [ - "bitflags 1.3.2", - "cocoa", - "core-foundation", - "core-graphics", - "crossbeam-channel", - "dispatch", - "dlopen2", - "gdkwayland-sys", - "gdkx11-sys", - "gtk", - "instant", - "jni", - "lazy_static", - "libc", - "log", - "ndk", - "ndk-context", - "ndk-sys", - "objc", - "once_cell", - "parking_lot", - "raw-window-handle 0.5.2", - "raw-window-handle 0.6.2", - "scopeguard", - "tao-macros", - "unicode-segmentation", - "url", - "windows 0.54.0", - "windows-version", - "x11-dl", -] - -[[package]] -name = "tao-macros" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec114582505d158b669b136e6851f85840c109819d77c42bb7c0709f727d18c2" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "target-lexicon" -version = "0.12.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" - -[[package]] -name = "tempfile" -version = "3.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" -dependencies = [ - "cfg-if", - "fastrand", - "rustix", - "windows-sys 0.52.0", -] - -[[package]] -name = "tendril" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" -dependencies = [ - "futf", - "mac", - "utf-8", -] - -[[package]] -name = "thin-slice" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" - -[[package]] -name = "thiserror" -version = "1.0.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "thread_local" -version = "1.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" -dependencies = [ - "cfg-if", - "once_cell", -] - -[[package]] -name = "time" -version = "0.3.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" -dependencies = [ - "deranged", - "itoa 1.0.11", - "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.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" -dependencies = [ - "num-conv", - "time-core", -] - -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "to_method" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7c4ceeeca15c8384bbc3e011dbd8fccb7f068a440b752b7d9b32ceb0ca0e2e8" - -[[package]] -name = "tokio" -version = "1.38.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" -dependencies = [ - "backtrace", - "bytes", - "libc", - "mio", - "num_cpus", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "windows-sys 0.48.0", -] - -[[package]] -name = "tokio-macros" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "tokio-rustls" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" -dependencies = [ - "rustls", - "tokio", -] - -[[package]] -name = "tokio-stream" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", - "tokio-util", -] - -[[package]] -name = "tokio-tungstenite" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" -dependencies = [ - "futures-util", - "log", - "tokio", - "tungstenite", -] - -[[package]] -name = "tokio-util" -version = "0.7.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "futures-util", - "hashbrown 0.14.5", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "toml" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit 0.20.2", -] - -[[package]] -name = "toml_datetime" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_edit" -version = "0.19.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" -dependencies = [ - "indexmap 2.2.6", - "toml_datetime", - "winnow", -] - -[[package]] -name = "toml_edit" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" -dependencies = [ - "indexmap 2.2.6", - "serde", - "serde_spanned", - "toml_datetime", - "winnow", -] - -[[package]] -name = "tower" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" -dependencies = [ - "futures-core", - "futures-util", - "pin-project", - "pin-project-lite", - "tokio", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-http" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" -dependencies = [ - "bitflags 2.5.0", - "bytes", - "futures-util", - "http 1.1.0", - "http-body 1.0.0", - "http-body-util", - "http-range-header", - "httpdate", - "mime", - "mime_guess", - "percent-encoding", - "pin-project-lite", - "tokio", - "tokio-util", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-layer" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" - -[[package]] -name = "tower-service" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" - -[[package]] -name = "tracing" -version = "0.1.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" -dependencies = [ - "log", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "tracing-core" -version = "0.1.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-futures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" -dependencies = [ - "pin-project", - "tracing", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" -dependencies = [ - "nu-ansi-term", - "sharded-slab", - "smallvec", - "thread_local", - "tracing-core", - "tracing-log", -] - -[[package]] -name = "tracing-wasm" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4575c663a174420fa2d78f4108ff68f65bf2fbb7dd89f33749b6e826b3626e07" -dependencies = [ - "tracing", - "tracing-subscriber", - "wasm-bindgen", -] - -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - -[[package]] -name = "tungstenite" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" -dependencies = [ - "byteorder", - "bytes", - "data-encoding", - "http 1.1.0", - "httparse", - "log", - "rand 0.8.5", - "sha1", - "thiserror", - "url", - "utf-8", -] - -[[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", -] - -[[package]] -name = "unicase" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" -dependencies = [ - "version_check", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" - -[[package]] -name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "unicode-normalization" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-segmentation" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" - -[[package]] -name = "unicode-xid" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "url" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" -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 = "uuid" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" -dependencies = [ - "getrandom 0.2.15", - "wasm-bindgen", -] - -[[package]] -name = "valuable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - -[[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.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[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 = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - -[[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.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn 2.0.66", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" - -[[package]] -name = "wasm-streams" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" -dependencies = [ - "futures-util", - "js-sys", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "web-sys" -version = "0.3.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webbrowser" -version = "0.8.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db67ae75a9405634f5882791678772c94ff5f16a66535aae186e26aa0841fc8b" -dependencies = [ - "core-foundation", - "home", - "jni", - "log", - "ndk-context", - "objc", - "raw-window-handle 0.5.2", - "url", - "web-sys", -] - -[[package]] -name = "webkit2gtk" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76b1bc1e54c581da1e9f179d0b38512ba358fb1af2d634a1affe42e37172361a" -dependencies = [ - "bitflags 1.3.2", - "cairo-rs", - "gdk", - "gdk-sys", - "gio", - "gio-sys", - "glib", - "glib-sys", - "gobject-sys", - "gtk", - "gtk-sys", - "javascriptcore-rs", - "libc", - "once_cell", - "soup3", - "webkit2gtk-sys", -] - -[[package]] -name = "webkit2gtk-sys" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62daa38afc514d1f8f12b8693d30d5993ff77ced33ce30cd04deebc267a6d57c" -dependencies = [ - "bitflags 1.3.2", - "cairo-sys-rs", - "gdk-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "gtk-sys", - "javascriptcore-rs-sys", - "libc", - "pkg-config", - "soup3-sys", - "system-deps", -] - -[[package]] -name = "webpki-roots" -version = "0.25.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" - -[[package]] -name = "webview2-com" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0ae9c7e420783826cf769d2c06ac9ba462f450eca5893bb8c6c6529a4e5dd33" -dependencies = [ - "webview2-com-macros", - "webview2-com-sys", - "windows 0.52.0", - "windows-core 0.52.0", - "windows-implement 0.52.0", - "windows-interface 0.52.0", -] - -[[package]] -name = "webview2-com-macros" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1345798ecd8122468840bcdf1b95e5dc6d2206c5e4b0eafa078d061f59c9bc" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "webview2-com-sys" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6ad85fceee6c42fa3d61239eba5a11401bf38407a849ed5ea1b407df08cca72" -dependencies = [ - "thiserror", - "windows 0.52.0", - "windows-core 0.52.0", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" -dependencies = [ - "windows-sys 0.52.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.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" -dependencies = [ - "windows-core 0.52.0", - "windows-implement 0.52.0", - "windows-interface 0.52.0", - "windows-targets 0.52.5", -] - -[[package]] -name = "windows" -version = "0.54.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49" -dependencies = [ - "windows-core 0.54.0", - "windows-implement 0.53.0", - "windows-interface 0.53.0", - "windows-targets 0.52.5", -] - -[[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.5", -] - -[[package]] -name = "windows-core" -version = "0.54.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65" -dependencies = [ - "windows-result", - "windows-targets 0.52.5", -] - -[[package]] -name = "windows-implement" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12168c33176773b86799be25e2a2ba07c7aab9968b37541f1094dbd7a60c8946" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "windows-implement" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942ac266be9249c84ca862f0a164a39533dc2f6f33dc98ec89c8da99b82ea0bd" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "windows-interface" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d8dc32e0095a7eeccebd0e3f09e9509365ecb3fc6ac4d6f5f14a3f6392942d1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "windows-interface" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da33557140a288fae4e1d5f8873aaf9eb6613a9cf82c3e070223ff177f598b60" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "windows-result" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "749f0da9cc72d82e600d8d2e44cadd0b9eedb9038f71a1c58556ac1c5791813b" -dependencies = [ - "windows-targets 0.52.5", -] - -[[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.5", -] - -[[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.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" -dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", - "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", -] - -[[package]] -name = "windows-version" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6998aa457c9ba8ff2fb9f13e9d2a930dabcea28f1d0ab94d687d8b3654844515" -dependencies = [ - "windows-targets 0.52.5", -] - -[[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.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" - -[[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.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" - -[[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.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" - -[[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.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" - -[[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.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" - -[[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.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" - -[[package]] -name = "winnow" -version = "0.5.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" -dependencies = [ - "memchr", -] - -[[package]] -name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - -[[package]] -name = "wry" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b717040ba9771fd88eb428c6ea6b555f8e734ff8534f02c13e8f10d97f5935e" -dependencies = [ - "base64 0.21.7", - "block", - "cfg_aliases", - "cocoa", - "core-graphics", - "crossbeam-channel", - "dunce", - "gdkx11", - "gtk", - "html5ever", - "http 0.2.12", - "javascriptcore-rs", - "jni", - "kuchikiki", - "libc", - "log", - "ndk", - "ndk-context", - "ndk-sys", - "objc", - "objc_id", - "once_cell", - "percent-encoding", - "raw-window-handle 0.6.2", - "serde", - "serde_json", - "sha2", - "soup3", - "tao-macros", - "thiserror", - "webkit2gtk", - "webkit2gtk-sys", - "webview2-com", - "windows 0.52.0", - "windows-implement 0.52.0", - "windows-version", - "x11-dl", -] - -[[package]] -name = "x11" -version = "2.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" -dependencies = [ - "libc", - "pkg-config", -] - -[[package]] -name = "x11-dl" -version = "2.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" -dependencies = [ - "libc", - "once_cell", - "pkg-config", -] - -[[package]] -name = "xdg-home" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21e5a325c3cb8398ad6cf859c1135b25dd29e186679cf2da7581d9679f63b38e" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "xxhash-rust" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927da81e25be1e1a2901d59b81b37dd2efd1fc9c9345a55007f09bf5a2d3ee03" - -[[package]] -name = "yazi" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c94451ac9513335b5e23d7a8a2b61a7102398b8cca5160829d313e84c9d98be1" - -[[package]] -name = "zbus" -version = "4.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b8e3d6ae3342792a6cc2340e4394334c7402f3d793b390d2c5494a4032b3030" -dependencies = [ - "async-broadcast", - "async-executor", - "async-fs", - "async-io", - "async-lock", - "async-process", - "async-recursion", - "async-task", - "async-trait", - "blocking", - "derivative", - "enumflags2", - "event-listener", - "futures-core", - "futures-sink", - "futures-util", - "hex", - "nix", - "ordered-stream", - "rand 0.8.5", - "serde", - "serde_repr", - "sha1", - "static_assertions", - "tracing", - "uds_windows", - "windows-sys 0.52.0", - "xdg-home", - "zbus_macros", - "zbus_names", - "zvariant", -] - -[[package]] -name = "zbus_macros" -version = "4.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7a3e850ff1e7217a3b7a07eba90d37fe9bb9e89a310f718afcde5885ca9b6d7" -dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro2", - "quote", - "regex", - "syn 1.0.109", - "zvariant_utils", -] - -[[package]] -name = "zbus_names" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" -dependencies = [ - "serde", - "static_assertions", - "zvariant", -] - -[[package]] -name = "zerocopy" -version = "0.7.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "zeroize" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" - -[[package]] -name = "zvariant" -version = "4.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e09e8be97d44eeab994d752f341e67b3b0d80512a8b315a0671d47232ef1b65" -dependencies = [ - "endi", - "enumflags2", - "serde", - "static_assertions", - "url", - "zvariant_derive", -] - -[[package]] -name = "zvariant_derive" -version = "4.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72a5857e2856435331636a9fbb415b09243df4521a267c5bedcd5289b4d5799e" -dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro2", - "quote", - "syn 1.0.109", - "zvariant_utils", -] - -[[package]] -name = "zvariant_utils" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00bedb16a193cc12451873fee2a1bc6550225acece0e36f333e68326c73c8172" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] diff --git a/examples/openid_connect_demo/Cargo.toml b/examples/openid_connect_demo/Cargo.toml deleted file mode 100644 index 72ae74dc98..0000000000 --- a/examples/openid_connect_demo/Cargo.toml +++ /dev/null @@ -1,36 +0,0 @@ -[package] -name = "openid_auth_demo" -version = "0.1.0" -edition = "2021" -publish = false - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -anyhow = "1.0.86" -console_error_panic_hook = "0.1" -dioxus = { path = "../../packages/dioxus", default_features = true, features = [ - "router", - "signals", -], version = "*" } -dioxus-logger = "0.5.1" -dioxus-sdk = { git = "https://github.com/Dioxuslabs/sdk", features = [ - "storage", -] } -form_urlencoded = "1.2.1" -log = "0.4" -openidconnect = "3.5.0" -serde = { version = "1.0.203", features = ["derive"] } -uuid = "1.8" - -[features] -default = ["web"] -server = ["dioxus/axum"] -web = ["dioxus/web"] -desktop = ["dioxus/desktop"] -fullstack = ["dioxus/fullstack"] - -# since we're using dioxus from local path, inform dioxus-sdk to use it as well -[patch.crates-io] -dioxus = { path = "../../packages/dioxus" } -dioxus-signals = { path = "../../packages/signals" } diff --git a/examples/openid_connect_demo/Dioxus.toml b/examples/openid_connect_demo/Dioxus.toml deleted file mode 100644 index 3a130e2ea3..0000000000 --- a/examples/openid_connect_demo/Dioxus.toml +++ /dev/null @@ -1,35 +0,0 @@ -[application] - -# dioxus project name -name = "OpenID Connect authentication demo" - -# default platform -# you can also use `dioxus serve/build --platform XXX` to use other platform -# value: web | desktop -default_platform = "web" - -# Web `build` & `serve` dist path -out_dir = "dist" - -# resource (static) file folder -asset_dir = "public" - -# hot reload by default -hot_reload = true - -[web.app] - -# HTML title tag content -title = "OpenID Connect authentication demo" - -[web.watcher] - -index_on_404 = true - -watch_path = ["src"] - -[application.plugins] - -available = true - -required = [] diff --git a/examples/openid_connect_demo/README.md b/examples/openid_connect_demo/README.md deleted file mode 100644 index 7b0d100fd9..0000000000 --- a/examples/openid_connect_demo/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# OpenID Connect example to show how to authenticate an user - -The environment variables in [`.cargo/config.toml`](./.cargo/config.toml) must be set in order for this example to work. - -Once they are set, you can run `dx serve --platform web` or `dx serve --platform desktop`. - -### Environment variables summary - -- `DIOXUS_FRONT_ISSUER_URL`: The openid-connect's issuer url -- `DIOXUS_FRONT_CLIENT_ID`: The openid-connect's client id -- `DIOXUS_FRONT_CLIENT_SECRET`: The openid-connect's client secret -- `DIOXUS_FRONT_URL`: The url the frontend is supposed to be running on, it could be for example `http://localhost:8080` diff --git a/examples/openid_connect_demo/src/constants.rs b/examples/openid_connect_demo/src/constants.rs deleted file mode 100644 index 0a0b4950a6..0000000000 --- a/examples/openid_connect_demo/src/constants.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub const DIOXUS_FRONT_AUTH_TOKEN: &str = "auth_token"; -pub const DIOXUS_FRONT_AUTH_REQUEST: &str = "auth_request"; diff --git a/examples/openid_connect_demo/src/main.rs b/examples/openid_connect_demo/src/main.rs deleted file mode 100644 index 8487758595..0000000000 --- a/examples/openid_connect_demo/src/main.rs +++ /dev/null @@ -1,36 +0,0 @@ -#![allow(non_snake_case)] -use dioxus::prelude::*; -use dioxus_logger::tracing::Level; -use router::Route; - -use crate::oidc::ClientState; -use crate::storage::{use_auth_request_provider, use_auth_token_provider}; - -pub(crate) mod constants; -pub(crate) mod model; -pub(crate) mod oidc; -pub(crate) mod props; -pub(crate) mod router; -pub(crate) mod storage; -pub(crate) mod views; - -pub static CLIENT: GlobalSignal = Signal::global(ClientState::default); - -pub static DIOXUS_FRONT_ISSUER_URL: &str = env!("DIOXUS_FRONT_ISSUER_URL"); -pub static DIOXUS_FRONT_CLIENT_ID: &str = env!("DIOXUS_FRONT_CLIENT_ID"); -pub static DIOXUS_FRONT_CLIENT_SECRET: &str = env!("DIOXUS_FRONT_CLIENT_SECRET"); -pub static DIOXUS_FRONT_URL: &str = env!("DIOXUS_FRONT_URL"); - -fn App() -> Element { - use_auth_request_provider(); - use_auth_token_provider(); - rsx! { Router:: {} } -} - -fn main() { - dioxus_logger::init(Level::DEBUG).expect("failed to init logger"); - dioxus_sdk::set_dir!(); - console_error_panic_hook::set_once(); - log::info!("starting app"); - launch(App); -} diff --git a/examples/openid_connect_demo/src/model/mod.rs b/examples/openid_connect_demo/src/model/mod.rs deleted file mode 100644 index 68a79f5c0c..0000000000 --- a/examples/openid_connect_demo/src/model/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub(crate) mod user; diff --git a/examples/openid_connect_demo/src/model/user.rs b/examples/openid_connect_demo/src/model/user.rs deleted file mode 100644 index b1005fdd24..0000000000 --- a/examples/openid_connect_demo/src/model/user.rs +++ /dev/null @@ -1,7 +0,0 @@ -use uuid::Uuid; - -#[derive(PartialEq)] -pub struct User { - pub id: Uuid, - pub name: String, -} diff --git a/examples/openid_connect_demo/src/oidc.rs b/examples/openid_connect_demo/src/oidc.rs deleted file mode 100644 index 2c20c4e49b..0000000000 --- a/examples/openid_connect_demo/src/oidc.rs +++ /dev/null @@ -1,127 +0,0 @@ -use anyhow::Result; -use openidconnect::{ - core::{CoreClient, CoreIdToken, CoreResponseType, CoreTokenResponse}, - reqwest::async_http_client, - url::Url, - AuthenticationFlow, AuthorizationCode, ClaimsVerificationError, ClientId, ClientSecret, - CsrfToken, IssuerUrl, LogoutRequest, Nonce, ProviderMetadataWithLogout, RedirectUrl, - RefreshToken, -}; -use serde::{Deserialize, Serialize}; - -use crate::{props::client::ClientProps, DIOXUS_FRONT_CLIENT_ID}; - -#[derive(Clone, Debug, Default)] -pub struct ClientState { - pub oidc_client: Option, -} - -/// State that holds the nonce and authorization url and the nonce generated to log in an user -#[derive(Clone, PartialEq, Deserialize, Serialize, Default)] -pub struct AuthRequestState { - pub auth_request: Option, -} - -#[derive(Clone, PartialEq, Deserialize, Serialize)] -pub struct AuthRequest { - pub nonce: Nonce, - pub authorize_url: String, -} - -/// State the tokens returned once the user is authenticated -#[derive(Debug, Deserialize, Serialize, Default, Clone)] -pub struct AuthTokenState { - /// Token used to identify the user - pub id_token: Option, - /// Token used to refresh the tokens if they expire - pub refresh_token: Option, -} - -impl PartialEq for AuthTokenState { - fn eq(&self, other: &Self) -> bool { - self.id_token == other.id_token - && self.refresh_token.as_ref().map(|t| t.secret().clone()) - == other.refresh_token.as_ref().map(|t| t.secret().clone()) - } -} - -pub fn email( - client: CoreClient, - id_token: CoreIdToken, - nonce: Nonce, -) -> Result { - match id_token.claims(&client.id_token_verifier(), &nonce) { - Ok(claims) => Ok(claims.clone().email().unwrap().to_string()), - Err(error) => Err(error), - } -} - -pub fn authorize_url(client: CoreClient) -> AuthRequest { - let (authorize_url, _csrf_state, nonce) = client - .authorize_url( - AuthenticationFlow::::AuthorizationCode, - CsrfToken::new_random, - Nonce::new_random, - ) - .add_scope(openidconnect::Scope::new("email".to_string())) - .add_scope(openidconnect::Scope::new("profile".to_string())) - .url(); - AuthRequest { - authorize_url: authorize_url.to_string(), - nonce, - } -} - -pub async fn init_provider_metadata() -> Result { - let issuer_url = IssuerUrl::new(crate::DIOXUS_FRONT_ISSUER_URL.to_string())?; - Ok(ProviderMetadataWithLogout::discover_async(issuer_url, async_http_client).await?) -} - -pub async fn init_oidc_client() -> Result<(ClientId, CoreClient)> { - let client_id = ClientId::new(crate::DIOXUS_FRONT_CLIENT_ID.to_string()); - let provider_metadata = init_provider_metadata().await?; - let client_secret = Some(ClientSecret::new( - crate::DIOXUS_FRONT_CLIENT_SECRET.to_string(), - )); - let redirect_url = RedirectUrl::new(format!("{}/login", crate::DIOXUS_FRONT_URL))?; - - Ok(( - client_id.clone(), - CoreClient::from_provider_metadata(provider_metadata, client_id, client_secret) - .set_redirect_uri(redirect_url), - )) -} - -///TODO: Add pkce_pacifier -pub async fn token_response(oidc_client: CoreClient, code: String) -> Result { - // let (pkce_challenge, pkce_verifier) = PkceCodeChallenge::new_random_sha256(); - Ok(oidc_client - .exchange_code(AuthorizationCode::new(code.clone())) - // .set_pkce_verifier(pkce_verifier) - .request_async(async_http_client) - .await?) -} - -pub async fn exchange_refresh_token( - oidc_client: CoreClient, - refresh_token: RefreshToken, -) -> Result { - Ok(oidc_client - .exchange_refresh_token(&refresh_token) - .request_async(async_http_client) - .await?) -} - -pub async fn log_out_url(id_token_hint: CoreIdToken) -> Result { - let provider_metadata = init_provider_metadata().await?; - let end_session_url = provider_metadata - .additional_metadata() - .clone() - .end_session_endpoint - .unwrap(); - let logout_request: LogoutRequest = LogoutRequest::from(end_session_url); - Ok(logout_request - .set_client_id(ClientId::new(DIOXUS_FRONT_CLIENT_ID.to_string())) - .set_id_token_hint(&id_token_hint) - .http_get_url()) -} diff --git a/examples/openid_connect_demo/src/props/client.rs b/examples/openid_connect_demo/src/props/client.rs deleted file mode 100644 index 2e02111e16..0000000000 --- a/examples/openid_connect_demo/src/props/client.rs +++ /dev/null @@ -1,20 +0,0 @@ -use dioxus::prelude::*; -use openidconnect::{core::CoreClient, ClientId}; - -#[derive(Props, Clone, Debug)] -pub struct ClientProps { - pub client: CoreClient, - pub client_id: ClientId, -} - -impl PartialEq for ClientProps { - fn eq(&self, other: &Self) -> bool { - self.client_id == other.client_id - } -} - -impl ClientProps { - pub fn new(client_id: ClientId, client: CoreClient) -> Self { - ClientProps { client_id, client } - } -} diff --git a/examples/openid_connect_demo/src/props/mod.rs b/examples/openid_connect_demo/src/props/mod.rs deleted file mode 100644 index 1d33131531..0000000000 --- a/examples/openid_connect_demo/src/props/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub(crate) mod client; diff --git a/examples/openid_connect_demo/src/router.rs b/examples/openid_connect_demo/src/router.rs deleted file mode 100644 index 8f4b1b36a6..0000000000 --- a/examples/openid_connect_demo/src/router.rs +++ /dev/null @@ -1,16 +0,0 @@ -use crate::views::{header::AuthHeader, home::Home, login::Login, not_found::NotFound}; -use dioxus::prelude::*; - -#[derive(Routable, Clone)] -pub enum Route { - #[layout(AuthHeader)] - #[route("/")] - Home {}, - - // https://dioxuslabs.com/learn/0.4/router/reference/routes#query-segments - #[route("/login?:query_string")] - Login { query_string: String }, - #[end_layout] - #[route("/:..route")] - NotFound { route: Vec }, -} diff --git a/examples/openid_connect_demo/src/storage.rs b/examples/openid_connect_demo/src/storage.rs deleted file mode 100644 index c34819b965..0000000000 --- a/examples/openid_connect_demo/src/storage.rs +++ /dev/null @@ -1,35 +0,0 @@ -use dioxus::prelude::*; -use dioxus_sdk::storage::*; - -use crate::{ - constants::{DIOXUS_FRONT_AUTH_REQUEST, DIOXUS_FRONT_AUTH_TOKEN}, - oidc::{AuthRequestState, AuthTokenState}, -}; - -pub fn use_auth_token_provider() { - let stored_token = - use_storage::(DIOXUS_FRONT_AUTH_TOKEN.to_owned(), AuthTokenState::default); - - use_context_provider(move || stored_token); -} - -pub fn use_auth_token() -> Signal { - use_context() -} - -pub fn use_auth_request_provider() { - let stored_req = use_storage::( - DIOXUS_FRONT_AUTH_REQUEST.to_owned(), - AuthRequestState::default, - ); - - use_context_provider(move || stored_req); -} - -pub fn use_auth_request() -> Signal { - use_context() -} - -pub fn auth_request() -> Signal { - consume_context() -} diff --git a/examples/openid_connect_demo/src/views/header.rs b/examples/openid_connect_demo/src/views/header.rs deleted file mode 100644 index 274a0d22f0..0000000000 --- a/examples/openid_connect_demo/src/views/header.rs +++ /dev/null @@ -1,221 +0,0 @@ -use crate::storage::{auth_request, use_auth_request, use_auth_token}; -use crate::{ - oidc::{ - authorize_url, email, exchange_refresh_token, init_oidc_client, log_out_url, - AuthRequestState, AuthTokenState, ClientState, - }, - props::client::ClientProps, - router::Route, - CLIENT, -}; -use anyhow::Result; -use dioxus::prelude::*; -use dioxus::router::prelude::{Link, Outlet}; -use openidconnect::{url::Url, OAuth2TokenResponse, TokenResponse}; - -#[component] -pub fn LogOut() -> Element { - let mut auth_token = use_auth_token(); - let log_out_url_state = use_signal(|| None::>>); - match auth_token().id_token { - Some(id_token) => match &*log_out_url_state.read() { - Some(log_out_url_result) => match log_out_url_result { - Some(uri) => match uri { - Ok(uri) => { - rsx! { - Link { - onclick: move |_| { - auth_token.take(); - }, - to: uri.to_string(), - "Log out" - } - } - } - Err(error) => { - rsx! { div { "Failed to load disconnection url: {error:?}" } } - } - }, - None => { - rsx! { div { "Loading... Please wait" } } - } - }, - None => { - let logout_url_task = move || { - spawn({ - let mut log_out_url_state = log_out_url_state.to_owned(); - async move { - let logout_url = log_out_url(id_token).await; - let logout_url_option = Some(logout_url); - log_out_url_state.set(Some(logout_url_option)); - } - }) - }; - logout_url_task(); - rsx! { div { "Loading log out url... Please wait" } } - } - }, - None => { - rsx! {{}} - } - } -} - -#[component] -pub fn RefreshToken(props: ClientProps) -> Element { - let mut auth_token = use_auth_token(); - match auth_token().refresh_token { - Some(refresh_token) => { - rsx! { div { - onmounted: { - move |_| { - let client = props.client.clone(); - let refresh_token = refresh_token.clone(); - async move { - let exchange_refresh_token = - exchange_refresh_token(client, refresh_token).await; - match exchange_refresh_token { - Ok(response_token) => { - auth_token.set(AuthTokenState { - id_token: response_token.id_token().cloned(), - refresh_token: response_token.refresh_token().cloned(), - }); - } - Err(_error) => { - auth_token.take(); - auth_request().take(); - } - } - } - } - }, - "Refreshing session, please wait" - } } - } - None => { - rsx! { div { "Id token expired and no refresh token found" } } - } - } -} - -#[component] -pub fn LoadClient() -> Element { - let init_client_future = use_resource(move || async move { init_oidc_client().await }); - match &*init_client_future.read_unchecked() { - Some(Ok((client_id, client))) => rsx! { - div { - onmounted: { - let client_id = client_id.clone(); - let client = client.clone(); - move |_| { - *CLIENT.write() = ClientState { - oidc_client: Some(ClientProps::new(client_id.clone(), client.clone())), - }; - } - }, - "Client successfully loaded" - } - Outlet:: {} - }, - Some(Err(error)) => { - log::info! {"Failed to load client: {:?}", error}; - rsx! { - div { "Failed to load client: {error:?}" } - Outlet:: {} - } - } - None => { - rsx! { - div { - div { "Loading client, please wait" } - Outlet:: {} - } - } - } - } -} - -#[component] -pub fn AuthHeader() -> Element { - let client = CLIENT.read().oidc_client.clone(); - let mut auth_request = use_auth_request(); - let auth_token = use_auth_token(); - match (client, auth_request(), auth_token()) { - // We have everything we need to attempt to authenticate the user - (Some(client_props), current_auth_request, current_auth_token) => { - match current_auth_request.auth_request { - Some(new_auth_request) => { - match current_auth_token.id_token { - Some(id_token) => { - match email( - client_props.client.clone(), - id_token.clone(), - new_auth_request.nonce.clone(), - ) { - Ok(email) => { - rsx! { - div { - div { {email} } - LogOut {} - Outlet:: {} - } - } - } - // Id token failed to be decoded - Err(error) => match error { - // Id token failed to be decoded because it expired, we refresh it - openidconnect::ClaimsVerificationError::Expired(_message) => { - log::info!("Token expired"); - rsx! { - div { - RefreshToken { client_id: client_props.client_id, client: client_props.client } - Outlet:: {} - } - } - } - // Other issue with token decoding - _ => { - log::info!("Other issue with token"); - rsx! { - div { - div { "{error}" } - Outlet:: {} - } - } - } - }, - } - } - // User is not logged in - None => { - rsx! { - div { - Link { to: new_auth_request.authorize_url.clone(), "Log in" } - Outlet:: {} - } - } - } - } - } - None => { - rsx! { div { - onmounted: { - let client = client_props.client; - move |_| { - let new_auth_request = authorize_url(client.clone()); - auth_request.set(AuthRequestState { - auth_request: Some(new_auth_request), - }); - } - }, - "Loading nonce" - } } - } - } - } - // Client is not initialized yet, we need it for everything - (None, _, _) => { - rsx! { LoadClient {} } - } - } -} diff --git a/examples/openid_connect_demo/src/views/home.rs b/examples/openid_connect_demo/src/views/home.rs deleted file mode 100644 index d91b5c2037..0000000000 --- a/examples/openid_connect_demo/src/views/home.rs +++ /dev/null @@ -1,5 +0,0 @@ -use dioxus::prelude::*; - -pub fn Home() -> Element { - rsx! { div { "Hello world" } } -} diff --git a/examples/openid_connect_demo/src/views/login.rs b/examples/openid_connect_demo/src/views/login.rs deleted file mode 100644 index e3a4e0025b..0000000000 --- a/examples/openid_connect_demo/src/views/login.rs +++ /dev/null @@ -1,86 +0,0 @@ -use crate::{ - oidc::{token_response, AuthTokenState}, - router::Route, - storage::{auth_request, use_auth_token}, - CLIENT, -}; -use dioxus::prelude::*; -use dioxus::router::prelude::Link; -use openidconnect::{OAuth2TokenResponse, TokenResponse}; - -#[component] -pub fn Login(query_string: String) -> Element { - let client = CLIENT.read().oidc_client.clone(); - let mut auth_token = use_auth_token(); - let current_auth_token = auth_token(); - match client { - Some(client_props) => { - match ( - current_auth_token.id_token, - current_auth_token.refresh_token, - ) { - (Some(_id_token), Some(_refresh_token)) => { - rsx! { - div { "Sign in successful" } - Link { to: Route::Home {}, "Go back home" } - } - } - // If the refresh token is set but not the id_token, there was an error, we just go back home and reset their value - (None, Some(_)) | (Some(_), None) => { - rsx! { - div { "Error while attempting to log in" } - Link { - to: Route::Home {}, - onclick: move |_| { - auth_token.take(); - auth_request().take(); - }, - "Go back home" - } - } - } - (None, None) => { - let mut query_pairs = form_urlencoded::parse(query_string.as_bytes()); - let code_pair = query_pairs.find(|(key, _value)| key == "code"); - match code_pair { - Some((_key, code)) => { - let code = code.to_string(); - rsx! { div { - onmounted: { - move |_| { - let auth_code = code.to_string(); - let client_props = client_props.clone(); - async move { - let token_response_result = - token_response(client_props.client, auth_code).await; - match token_response_result { - Ok(token_response) => { - let id_token = token_response.id_token().unwrap(); - auth_token.set(AuthTokenState { - id_token: Some(id_token.clone()), - refresh_token: token_response - .refresh_token() - .cloned(), - }); - } - Err(error) => { - log::warn! {"{error}"}; - } - } - } - } - } - }} - } - None => { - rsx! { div { "No code provided" } } - } - } - } - } - } - _ => { - rsx! {{}} - } - } -} diff --git a/examples/openid_connect_demo/src/views/mod.rs b/examples/openid_connect_demo/src/views/mod.rs deleted file mode 100644 index 1b8e08a585..0000000000 --- a/examples/openid_connect_demo/src/views/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub(crate) mod header; -pub(crate) mod home; -pub(crate) mod login; -pub(crate) mod not_found; diff --git a/examples/openid_connect_demo/src/views/not_found.rs b/examples/openid_connect_demo/src/views/not_found.rs deleted file mode 100644 index 833c610674..0000000000 --- a/examples/openid_connect_demo/src/views/not_found.rs +++ /dev/null @@ -1,10 +0,0 @@ -use dioxus::prelude::*; - -#[component] -pub fn NotFound(route: Vec) -> Element { - rsx! { - div{ - {route.join("")} - } - } -} diff --git a/examples/optional_props.rs b/examples/optional_props.rs index ad54ed33cd..334852bea1 100644 --- a/examples/optional_props.rs +++ b/examples/optional_props.rs @@ -6,7 +6,7 @@ use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/overlay.rs b/examples/overlay.rs index e4e2e80592..59a9619d6a 100644 --- a/examples/overlay.rs +++ b/examples/overlay.rs @@ -11,7 +11,9 @@ use dioxus::desktop::{ use dioxus::prelude::*; fn main() { - LaunchBuilder::desktop().with_cfg(make_config()).launch(app); + dioxus::launch::builder() + .with_cfg(make_config()) + .launch(app); } fn app() -> Element { @@ -20,9 +22,9 @@ fn app() -> Element { _ = use_global_shortcut("cmd+g", move || show_overlay.toggle()); rsx! { - head::Link { + document::Link { rel: "stylesheet", - href: asset!("./examples/assets/overlay.css"), + href: asset!("/examples/assets/overlay.css"), } if show_overlay() { div { diff --git a/examples/popup.rs b/examples/popup.rs index 131d536edb..e708b9cd9f 100644 --- a/examples/popup.rs +++ b/examples/popup.rs @@ -5,7 +5,7 @@ use dioxus::prelude::*; use std::rc::Rc; fn main() { - launch_desktop(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/query_segment_search.rs b/examples/query_segment_search.rs index 018f475e45..4366cc0118 100644 --- a/examples/query_segment_search.rs +++ b/examples/query_segment_search.rs @@ -2,7 +2,7 @@ //! //! The enum router makes it easy to use your route as state in your app. This example shows how to use the router to encode search text into the url and decode it back into a string. //! -//! Run this example on desktop with +//! Run this example on desktop with //! ```sh //! dx serve --example query_segment_search //! ``` @@ -14,7 +14,7 @@ use dioxus::prelude::*; fn main() { - launch(|| { + dioxus::launch(|| { rsx! { Router:: {} } @@ -29,7 +29,7 @@ enum Route { // The each query segment must implement and Display. // You can use multiple query segments separated by `&`s. - #[route("/search?:query&:word_count")] + #[route("/search?:query&:word_count")] Search { query: String, word_count: usize, diff --git a/examples/read_size.rs b/examples/read_size.rs index 1965597cfa..dbde9353c7 100644 --- a/examples/read_size.rs +++ b/examples/read_size.rs @@ -9,7 +9,7 @@ use std::rc::Rc; use dioxus::{html::geometry::euclid::Rect, prelude::*}; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { @@ -28,7 +28,7 @@ fn app() -> Element { }; rsx!( - head::Link { rel: "stylesheet", href: asset!("./examples/assets/read_size.css") } + document::Stylesheet { href: asset!("/examples/assets/read_size.css") } div { width: "50%", height: "50%", diff --git a/examples/readme.rs b/examples/readme.rs index 8f412a749d..1c36c65c00 100644 --- a/examples/readme.rs +++ b/examples/readme.rs @@ -8,7 +8,7 @@ use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/reducer.rs b/examples/reducer.rs index 7c434e5858..32a15368e8 100644 --- a/examples/reducer.rs +++ b/examples/reducer.rs @@ -7,17 +7,17 @@ use dioxus::prelude::*; -const STYLE: &str = asset!("./examples/assets/radio.css"); +const STYLE: Asset = asset!("/examples/assets/radio.css"); fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { let mut state = use_signal(|| PlayerState { is_playing: false }); rsx!( - head::Link { rel: "stylesheet", href: STYLE } + document::Stylesheet { href: STYLE } h1 {"Select an option"} // Add some cute animations if the radio is playing! diff --git a/examples/resize.rs b/examples/resize.rs index eace774a0b..52b55bfb8c 100644 --- a/examples/resize.rs +++ b/examples/resize.rs @@ -8,14 +8,14 @@ use dioxus::prelude::*; use dioxus_elements::geometry::euclid::Size2D; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { let mut dimensions = use_signal(Size2D::zero); rsx!( - head::Link { rel: "stylesheet", href: asset!("./examples/assets/read_size.css") } + document::Stylesheet { href: asset!("/examples/assets/read_size.css") } div { width: "50%", height: "50%", diff --git a/examples/router.rs b/examples/router.rs index 13197f57e2..377c595418 100644 --- a/examples/router.rs +++ b/examples/router.rs @@ -8,12 +8,12 @@ use dioxus::prelude::*; -const STYLE: &str = asset!("./examples/assets/router.css"); +const STYLE: Asset = asset!("/examples/assets/router.css"); fn main() { - launch(|| { + dioxus::launch(|| { rsx! { - head::Link { rel: "stylesheet", href: STYLE } + document::Stylesheet { href: STYLE } Router:: {} } }); diff --git a/examples/router_resource.rs b/examples/router_resource.rs index de69f0e40b..6898585f3b 100644 --- a/examples/router_resource.rs +++ b/examples/router_resource.rs @@ -15,7 +15,7 @@ enum Route { } fn main() { - launch(App); + dioxus::launch(App); } #[component] diff --git a/examples/rsx_usage.rs b/examples/rsx_usage.rs index 3bffe8c700..b8ae72fd68 100644 --- a/examples/rsx_usage.rs +++ b/examples/rsx_usage.rs @@ -39,7 +39,7 @@ //! - Allow top-level fragments fn main() { - launch(app) + dioxus::launch(app) } use core::{fmt, str::FromStr}; @@ -228,6 +228,9 @@ fn app() -> Element { // Or we can shell out to a helper function {format_dollars(10, 50)} + + // some text? + {Some("hello world!")} } } } diff --git a/examples/scroll_to_top.rs b/examples/scroll_to_top.rs index 9f4f85955e..1cd91e4947 100644 --- a/examples/scroll_to_top.rs +++ b/examples/scroll_to_top.rs @@ -8,7 +8,7 @@ use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/shortcut.rs b/examples/shortcut.rs index d0192b9782..a6626c27b7 100644 --- a/examples/shortcut.rs +++ b/examples/shortcut.rs @@ -9,7 +9,7 @@ use dioxus::desktop::use_global_shortcut; use dioxus::prelude::*; fn main() { - launch_desktop(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/shorthand.rs b/examples/shorthand.rs index 33905d5710..bd2047a8e6 100644 --- a/examples/shorthand.rs +++ b/examples/shorthand.rs @@ -3,7 +3,7 @@ use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/signals.rs b/examples/signals.rs index 26db9685c1..6b881587bc 100644 --- a/examples/signals.rs +++ b/examples/signals.rs @@ -10,7 +10,7 @@ use async_std::task::sleep; use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/simple_list.rs b/examples/simple_list.rs index 3c1b9d71a1..32e1d3eb85 100644 --- a/examples/simple_list.rs +++ b/examples/simple_list.rs @@ -5,7 +5,7 @@ use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/simple_router.rs b/examples/simple_router.rs index e674f2f30e..0ec22a001e 100644 --- a/examples/simple_router.rs +++ b/examples/simple_router.rs @@ -5,7 +5,7 @@ use dioxus::prelude::*; fn main() { // Launch the router, using our `Route` component as the generic type // This will automatically boot the app to "/" unless otherwise specified - launch(|| rsx! { Router:: {} }); + dioxus::launch(|| rsx! { Router:: {} }); } /// By default, the Routable derive will use the name of the variant as the route diff --git a/examples/streams.rs b/examples/streams.rs index 6155cb35f9..a71b5b9c18 100644 --- a/examples/streams.rs +++ b/examples/streams.rs @@ -5,7 +5,7 @@ use dioxus::prelude::*; use futures_util::{future, stream, Stream, StreamExt}; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/suspense.rs b/examples/suspense.rs index 014bb3276a..14cd2573e9 100644 --- a/examples/suspense.rs +++ b/examples/suspense.rs @@ -15,7 +15,7 @@ use dioxus::desktop::{Config, LogicalSize, WindowBuilder}; use dioxus::prelude::*; fn main() { - LaunchBuilder::new() + dioxus::builder() .with_cfg(desktop! { Config::new().with_window( WindowBuilder::new() diff --git a/examples/svg.rs b/examples/svg.rs index d4c23d1ddb..b462f30288 100644 --- a/examples/svg.rs +++ b/examples/svg.rs @@ -10,7 +10,7 @@ use dioxus::prelude::*; use rand::{thread_rng, Rng}; fn main() { - launch(|| { + dioxus::launch(|| { rsx! { div { user_select: "none", webkit_user_select: "none", margin_left: "10%", margin_right: "10%", h1 { "Click die to generate a new value" } diff --git a/examples/tailwind/.gitignore b/examples/tailwind/.gitignore deleted file mode 100644 index 1521c8b765..0000000000 --- a/examples/tailwind/.gitignore +++ /dev/null @@ -1 +0,0 @@ -dist diff --git a/examples/tailwind/Cargo.toml b/examples/tailwind/Cargo.toml deleted file mode 100644 index 6660b87397..0000000000 --- a/examples/tailwind/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "dioxus-tailwind" -version = "0.0.0" -authors = [] -edition = "2021" -description = "A tailwindcss example using Dioxus" -license = "MIT OR Apache-2.0" -repository = "https://github.com/DioxusLabs/dioxus/" -homepage = "https://dioxuslabs.com" -documentation = "https://dioxuslabs.com" -publish = false - -[dependencies] -manganis = { workspace = true } - -[target.'cfg(not(target_arch = "wasm32"))'.dependencies] -dioxus = { path = "../../packages/dioxus", features = ["desktop"] } - -[target.'cfg(target_arch = "wasm32")'.dependencies] -dioxus = { path = "../../packages/dioxus", features = ["web"] } diff --git a/examples/tailwind/Dioxus.toml b/examples/tailwind/Dioxus.toml deleted file mode 100644 index af7fcf3b8f..0000000000 --- a/examples/tailwind/Dioxus.toml +++ /dev/null @@ -1,27 +0,0 @@ -[application] - -# App (Project) Name -name = "Tailwind CSS + Dioxus" - -# Dioxus App Default Platform -# desktop, web, mobile, ssr -default_platform = "web" - -# `build` & `serve` dist path -out_dir = "dist" - -# resource (public) file folder -asset_dir = "public" - -[web.app] - -# HTML title tag content -title = "dioxus | ⛺" - -[web.watcher] - -# when watcher trigger, regenerate the `index.html` -reload_html = true - -# which files or dirs will be watcher monitoring -watch_path = ["src", "public"] diff --git a/examples/tailwind/README.md b/examples/tailwind/README.md deleted file mode 100644 index 6dca4b775e..0000000000 --- a/examples/tailwind/README.md +++ /dev/null @@ -1,7 +0,0 @@ -Example: Basic Tailwind usage - -This example shows how an app might be styled with TailwindCSS. - -## Running - -Our [Tailwind](https://dioxuslabs.com/learn/0.5/cookbook/tailwind) guide explains how to setup and run Dioxus-Tailwind projects. \ No newline at end of file diff --git a/examples/tailwind/input.css b/examples/tailwind/input.css deleted file mode 100644 index bd6213e1df..0000000000 --- a/examples/tailwind/input.css +++ /dev/null @@ -1,3 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; \ No newline at end of file diff --git a/examples/tailwind/public/tailwind.css b/examples/tailwind/public/tailwind.css deleted file mode 100644 index 65b95316e1..0000000000 --- a/examples/tailwind/public/tailwind.css +++ /dev/null @@ -1,833 +0,0 @@ -/* -! tailwindcss v3.2.7 | MIT License | https://tailwindcss.com -*/ - -/* -1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) -2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) -*/ - -*, -::before, -::after { - box-sizing: border-box; - /* 1 */ - border-width: 0; - /* 2 */ - border-style: solid; - /* 2 */ - border-color: #e5e7eb; - /* 2 */ -} - -::before, -::after { - --tw-content: ''; -} - -/* -1. Use a consistent sensible line-height in all browsers. -2. Prevent adjustments of font size after orientation changes in iOS. -3. Use a more readable tab size. -4. Use the user's configured `sans` font-family by default. -5. Use the user's configured `sans` font-feature-settings by default. -*/ - -html { - line-height: 1.5; - /* 1 */ - -webkit-text-size-adjust: 100%; - /* 2 */ - -moz-tab-size: 4; - /* 3 */ - -o-tab-size: 4; - tab-size: 4; - /* 3 */ - font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; - /* 4 */ - font-feature-settings: normal; - /* 5 */ -} - -/* -1. Remove the margin in all browsers. -2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. -*/ - -body { - margin: 0; - /* 1 */ - line-height: inherit; - /* 2 */ -} - -/* -1. Add the correct height in Firefox. -2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) -3. Ensure horizontal rules are visible by default. -*/ - -hr { - height: 0; - /* 1 */ - color: inherit; - /* 2 */ - border-top-width: 1px; - /* 3 */ -} - -/* -Add the correct text decoration in Chrome, Edge, and Safari. -*/ - -abbr:where([title]) { - -webkit-text-decoration: underline dotted; - text-decoration: underline dotted; -} - -/* -Remove the default font size and weight for headings. -*/ - -h1, -h2, -h3, -h4, -h5, -h6 { - font-size: inherit; - font-weight: inherit; -} - -/* -Reset links to optimize for opt-in styling instead of opt-out. -*/ - -a { - color: inherit; - text-decoration: inherit; -} - -/* -Add the correct font weight in Edge and Safari. -*/ - -b, -strong { - font-weight: bolder; -} - -/* -1. Use the user's configured `mono` font family by default. -2. Correct the odd `em` font sizing in all browsers. -*/ - -code, -kbd, -samp, -pre { - font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; - /* 1 */ - font-size: 1em; - /* 2 */ -} - -/* -Add the correct font size in all browsers. -*/ - -small { - font-size: 80%; -} - -/* -Prevent `sub` and `sup` elements from affecting the line height in all browsers. -*/ - -sub, -sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; -} - -sub { - bottom: -0.25em; -} - -sup { - top: -0.5em; -} - -/* -1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) -2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) -3. Remove gaps between table borders by default. -*/ - -table { - text-indent: 0; - /* 1 */ - border-color: inherit; - /* 2 */ - border-collapse: collapse; - /* 3 */ -} - -/* -1. Change the font styles in all browsers. -2. Remove the margin in Firefox and Safari. -3. Remove default padding in all browsers. -*/ - -button, -input, -optgroup, -select, -textarea { - font-family: inherit; - /* 1 */ - font-size: 100%; - /* 1 */ - font-weight: inherit; - /* 1 */ - line-height: inherit; - /* 1 */ - color: inherit; - /* 1 */ - margin: 0; - /* 2 */ - padding: 0; - /* 3 */ -} - -/* -Remove the inheritance of text transform in Edge and Firefox. -*/ - -button, -select { - text-transform: none; -} - -/* -1. Correct the inability to style clickable types in iOS and Safari. -2. Remove default button styles. -*/ - -button, -[type='button'], -[type='reset'], -[type='submit'] { - -webkit-appearance: button; - /* 1 */ - background-color: transparent; - /* 2 */ - background-image: none; - /* 2 */ -} - -/* -Use the modern Firefox focus style for all focusable elements. -*/ - -:-moz-focusring { - outline: auto; -} - -/* -Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) -*/ - -:-moz-ui-invalid { - box-shadow: none; -} - -/* -Add the correct vertical alignment in Chrome and Firefox. -*/ - -progress { - vertical-align: baseline; -} - -/* -Correct the cursor style of increment and decrement buttons in Safari. -*/ - -::-webkit-inner-spin-button, -::-webkit-outer-spin-button { - height: auto; -} - -/* -1. Correct the odd appearance in Chrome and Safari. -2. Correct the outline style in Safari. -*/ - -[type='search'] { - -webkit-appearance: textfield; - /* 1 */ - outline-offset: -2px; - /* 2 */ -} - -/* -Remove the inner padding in Chrome and Safari on macOS. -*/ - -::-webkit-search-decoration { - -webkit-appearance: none; -} - -/* -1. Correct the inability to style clickable types in iOS and Safari. -2. Change font properties to `inherit` in Safari. -*/ - -::-webkit-file-upload-button { - -webkit-appearance: button; - /* 1 */ - font: inherit; - /* 2 */ -} - -/* -Add the correct display in Chrome and Safari. -*/ - -summary { - display: list-item; -} - -/* -Removes the default spacing and border for appropriate elements. -*/ - -blockquote, -dl, -dd, -h1, -h2, -h3, -h4, -h5, -h6, -hr, -figure, -p, -pre { - margin: 0; -} - -fieldset { - margin: 0; - padding: 0; -} - -legend { - padding: 0; -} - -ol, -ul, -menu { - list-style: none; - margin: 0; - padding: 0; -} - -/* -Prevent resizing textareas horizontally by default. -*/ - -textarea { - resize: vertical; -} - -/* -1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) -2. Set the default placeholder color to the user's configured gray 400 color. -*/ - -input::-moz-placeholder, textarea::-moz-placeholder { - opacity: 1; - /* 1 */ - color: #9ca3af; - /* 2 */ -} - -input::placeholder, -textarea::placeholder { - opacity: 1; - /* 1 */ - color: #9ca3af; - /* 2 */ -} - -/* -Set the default cursor for buttons. -*/ - -button, -[role="button"] { - cursor: pointer; -} - -/* -Make sure disabled buttons don't get the pointer cursor. -*/ - -:disabled { - cursor: default; -} - -/* -1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) -2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) - This can trigger a poorly considered lint error in some tools but is included by design. -*/ - -img, -svg, -video, -canvas, -audio, -iframe, -embed, -object { - display: block; - /* 1 */ - vertical-align: middle; - /* 2 */ -} - -/* -Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) -*/ - -img, -video { - max-width: 100%; - height: auto; -} - -/* Make elements with the HTML hidden attribute stay hidden by default */ - -[hidden] { - display: none; -} - -*, ::before, ::after { - --tw-border-spacing-x: 0; - --tw-border-spacing-y: 0; - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-rotate: 0; - --tw-skew-x: 0; - --tw-skew-y: 0; - --tw-scale-x: 1; - --tw-scale-y: 1; - --tw-pan-x: ; - --tw-pan-y: ; - --tw-pinch-zoom: ; - --tw-scroll-snap-strictness: proximity; - --tw-ordinal: ; - --tw-slashed-zero: ; - --tw-numeric-figure: ; - --tw-numeric-spacing: ; - --tw-numeric-fraction: ; - --tw-ring-inset: ; - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-color: rgb(59 130 246 / 0.5); - --tw-ring-offset-shadow: 0 0 #0000; - --tw-ring-shadow: 0 0 #0000; - --tw-shadow: 0 0 #0000; - --tw-shadow-colored: 0 0 #0000; - --tw-blur: ; - --tw-brightness: ; - --tw-contrast: ; - --tw-grayscale: ; - --tw-hue-rotate: ; - --tw-invert: ; - --tw-saturate: ; - --tw-sepia: ; - --tw-drop-shadow: ; - --tw-backdrop-blur: ; - --tw-backdrop-brightness: ; - --tw-backdrop-contrast: ; - --tw-backdrop-grayscale: ; - --tw-backdrop-hue-rotate: ; - --tw-backdrop-invert: ; - --tw-backdrop-opacity: ; - --tw-backdrop-saturate: ; - --tw-backdrop-sepia: ; -} - -::backdrop { - --tw-border-spacing-x: 0; - --tw-border-spacing-y: 0; - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-rotate: 0; - --tw-skew-x: 0; - --tw-skew-y: 0; - --tw-scale-x: 1; - --tw-scale-y: 1; - --tw-pan-x: ; - --tw-pan-y: ; - --tw-pinch-zoom: ; - --tw-scroll-snap-strictness: proximity; - --tw-ordinal: ; - --tw-slashed-zero: ; - --tw-numeric-figure: ; - --tw-numeric-spacing: ; - --tw-numeric-fraction: ; - --tw-ring-inset: ; - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-color: rgb(59 130 246 / 0.5); - --tw-ring-offset-shadow: 0 0 #0000; - --tw-ring-shadow: 0 0 #0000; - --tw-shadow: 0 0 #0000; - --tw-shadow-colored: 0 0 #0000; - --tw-blur: ; - --tw-brightness: ; - --tw-contrast: ; - --tw-grayscale: ; - --tw-hue-rotate: ; - --tw-invert: ; - --tw-saturate: ; - --tw-sepia: ; - --tw-drop-shadow: ; - --tw-backdrop-blur: ; - --tw-backdrop-brightness: ; - --tw-backdrop-contrast: ; - --tw-backdrop-grayscale: ; - --tw-backdrop-hue-rotate: ; - --tw-backdrop-invert: ; - --tw-backdrop-opacity: ; - --tw-backdrop-saturate: ; - --tw-backdrop-sepia: ; -} - -.container { - width: 100%; -} - -@media (min-width: 640px) { - .container { - max-width: 640px; - } -} - -@media (min-width: 768px) { - .container { - max-width: 768px; - } -} - -@media (min-width: 1024px) { - .container { - max-width: 1024px; - } -} - -@media (min-width: 1280px) { - .container { - max-width: 1280px; - } -} - -@media (min-width: 1536px) { - .container { - max-width: 1536px; - } -} - -.mx-auto { - margin-left: auto; - margin-right: auto; -} - -.mb-16 { - margin-bottom: 4rem; -} - -.mb-4 { - margin-bottom: 1rem; -} - -.mb-8 { - margin-bottom: 2rem; -} - -.ml-1 { - margin-left: 0.25rem; -} - -.ml-3 { - margin-left: 0.75rem; -} - -.ml-4 { - margin-left: 1rem; -} - -.mr-5 { - margin-right: 1.25rem; -} - -.mt-4 { - margin-top: 1rem; -} - -.flex { - display: flex; -} - -.inline-flex { - display: inline-flex; -} - -.hidden { - display: none; -} - -.h-10 { - height: 2.5rem; -} - -.h-4 { - height: 1rem; -} - -.w-10 { - width: 2.5rem; -} - -.w-4 { - width: 1rem; -} - -.w-5\/6 { - width: 83.333333%; -} - -.flex-col { - flex-direction: column; -} - -.flex-wrap { - flex-wrap: wrap; -} - -.items-center { - align-items: center; -} - -.justify-center { - justify-content: center; -} - -.rounded { - border-radius: 0.25rem; -} - -.rounded-full { - border-radius: 9999px; -} - -.border-0 { - border-width: 0px; -} - -.bg-gray-800 { - --tw-bg-opacity: 1; - background-color: rgb(31 41 55 / var(--tw-bg-opacity)); -} - -.bg-gray-900 { - --tw-bg-opacity: 1; - background-color: rgb(17 24 39 / var(--tw-bg-opacity)); -} - -.bg-indigo-500 { - --tw-bg-opacity: 1; - background-color: rgb(99 102 241 / var(--tw-bg-opacity)); -} - -.object-cover { - -o-object-fit: cover; - object-fit: cover; -} - -.object-center { - -o-object-position: center; - object-position: center; -} - -.p-2 { - padding: 0.5rem; -} - -.p-5 { - padding: 1.25rem; -} - -.px-3 { - padding-left: 0.75rem; - padding-right: 0.75rem; -} - -.px-5 { - padding-left: 1.25rem; - padding-right: 1.25rem; -} - -.px-6 { - padding-left: 1.5rem; - padding-right: 1.5rem; -} - -.py-1 { - padding-top: 0.25rem; - padding-bottom: 0.25rem; -} - -.py-2 { - padding-top: 0.5rem; - padding-bottom: 0.5rem; -} - -.py-24 { - padding-top: 6rem; - padding-bottom: 6rem; -} - -.text-center { - text-align: center; -} - -.text-3xl { - font-size: 1.875rem; - line-height: 2.25rem; -} - -.text-base { - font-size: 1rem; - line-height: 1.5rem; -} - -.text-lg { - font-size: 1.125rem; - line-height: 1.75rem; -} - -.text-xl { - font-size: 1.25rem; - line-height: 1.75rem; -} - -.font-medium { - font-weight: 500; -} - -.leading-relaxed { - line-height: 1.625; -} - -.text-gray-400 { - --tw-text-opacity: 1; - color: rgb(156 163 175 / var(--tw-text-opacity)); -} - -.text-white { - --tw-text-opacity: 1; - color: rgb(255 255 255 / var(--tw-text-opacity)); -} - -.hover\:bg-gray-700:hover { - --tw-bg-opacity: 1; - background-color: rgb(55 65 81 / var(--tw-bg-opacity)); -} - -.hover\:bg-indigo-600:hover { - --tw-bg-opacity: 1; - background-color: rgb(79 70 229 / var(--tw-bg-opacity)); -} - -.hover\:text-white:hover { - --tw-text-opacity: 1; - color: rgb(255 255 255 / var(--tw-text-opacity)); -} - -.focus\:outline-none:focus { - outline: 2px solid transparent; - outline-offset: 2px; -} - -@media (min-width: 640px) { - .sm\:text-4xl { - font-size: 2.25rem; - line-height: 2.5rem; - } -} - -@media (min-width: 768px) { - .md\:mb-0 { - margin-bottom: 0px; - } - - .md\:ml-auto { - margin-left: auto; - } - - .md\:mt-0 { - margin-top: 0px; - } - - .md\:w-1\/2 { - width: 50%; - } - - .md\:flex-row { - flex-direction: row; - } - - .md\:items-start { - align-items: flex-start; - } - - .md\:pr-16 { - padding-right: 4rem; - } - - .md\:text-left { - text-align: left; - } -} - -@media (min-width: 1024px) { - .lg\:inline-block { - display: inline-block; - } - - .lg\:w-full { - width: 100%; - } - - .lg\:max-w-lg { - max-width: 32rem; - } - - .lg\:flex-grow { - flex-grow: 1; - } - - .lg\:pr-24 { - padding-right: 6rem; - } -} \ No newline at end of file diff --git a/examples/tailwind/src/main.rs b/examples/tailwind/src/main.rs deleted file mode 100644 index 70ce484c38..0000000000 --- a/examples/tailwind/src/main.rs +++ /dev/null @@ -1,103 +0,0 @@ -#![allow(non_snake_case)] - -use dioxus::prelude::*; - -const _STYLE: &str = asset!("public/tailwind.css"); - -fn main() { - launch(app); -} - -pub fn app() -> Element { - let grey_background = true; - rsx!( - div { - header { - class: "text-gray-400 body-font", - // you can use optional attributes to optionally apply a tailwind class - class: if grey_background { - "bg-gray-900" - }, - div { class: "container mx-auto flex flex-wrap p-5 flex-col md:flex-row items-center", - a { class: "flex title-font font-medium items-center text-white mb-4 md:mb-0", - StacksIcon {} - span { class: "ml-3 text-xl", "Hello Dioxus!" } - } - nav { class: "md:ml-auto flex flex-wrap items-center text-base justify-center", - a { class: "mr-5 hover:text-white", "First Link" } - a { class: "mr-5 hover:text-white", "Second Link" } - a { class: "mr-5 hover:text-white", "Third Link" } - a { class: "mr-5 hover:text-white", "Fourth Link" } - } - button { class: "inline-flex items-center bg-gray-800 border-0 py-1 px-3 focus:outline-none hover:bg-gray-700 rounded text-base mt-4 md:mt-0", - "Button" - RightArrowIcon {} - } - } - } - - section { class: "text-gray-400 bg-gray-900 body-font", - div { class: "container mx-auto flex px-5 py-24 md:flex-row flex-col items-center", - div { class: "lg:flex-grow md:w-1/2 lg:pr-24 md:pr-16 flex flex-col md:items-start md:text-left mb-16 md:mb-0 items-center text-center", - h1 { class: "title-font sm:text-4xl text-3xl mb-4 font-medium text-white", - br { class: "hidden lg:inline-block" } - "Dioxus Sneak Peek" - } - p { class: "mb-8 leading-relaxed", - - "Dioxus is a new UI framework that makes it easy and simple to write cross-platform apps using web - technologies! It is functional, fast, and portable. Dioxus can run on the web, on the desktop, and - on mobile and embedded platforms." - } - div { class: "flex justify-center", - button { class: "inline-flex text-white bg-indigo-500 border-0 py-2 px-6 focus:outline-none hover:bg-indigo-600 rounded text-lg", - "Learn more" - } - button { class: "ml-4 inline-flex text-gray-400 bg-gray-800 border-0 py-2 px-6 focus:outline-none hover:bg-gray-700 hover:text-white rounded text-lg", - "Build an app" - } - } - } - div { class: "lg:max-w-lg lg:w-full md:w-1/2 w-5/6", - img { - class: "object-cover object-center rounded", - src: "https://i.imgur.com/oK6BLtw.png", - referrerpolicy: "no-referrer", - alt: "hero" - } - } - } - } - } - ) -} - -pub fn StacksIcon() -> Element { - rsx!( - svg { - fill: "none", - stroke: "currentColor", - stroke_linecap: "round", - stroke_linejoin: "round", - stroke_width: "2", - class: "w-10 h-10 text-white p-2 bg-indigo-500 rounded-full", - view_box: "0 0 24 24", - path { d: "M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5" } - } - ) -} - -pub fn RightArrowIcon() -> Element { - rsx!( - svg { - fill: "none", - stroke: "currentColor", - stroke_linecap: "round", - stroke_linejoin: "round", - stroke_width: "2", - class: "w-4 h-4 ml-1", - view_box: "0 0 24 24", - path { d: "M5 12h14M12 5l7 7-7 7" } - } - ) -} diff --git a/examples/tailwind/tailwind.config.js b/examples/tailwind/tailwind.config.js deleted file mode 100644 index 2a69d5803a..0000000000 --- a/examples/tailwind/tailwind.config.js +++ /dev/null @@ -1,9 +0,0 @@ -/** @type {import('tailwindcss').Config} */ -module.exports = { - mode: "all", - content: ["./src/**/*.{rs,html,css}", "./dist/**/*.html"], - theme: { - extend: {}, - }, - plugins: [], -}; diff --git a/examples/title.rs b/examples/title.rs index ebe258963c..f836a7f3d6 100644 --- a/examples/title.rs +++ b/examples/title.rs @@ -3,8 +3,7 @@ use dioxus::prelude::*; fn main() { - tracing_subscriber::fmt::init(); - launch(app); + dioxus::launch(app); } fn app() -> Element { @@ -14,7 +13,7 @@ fn app() -> Element { div { // You can set the title of the page with the Title component // In web applications, this sets the title in the head. On desktop, it sets the window title - Title { "My Application (Count {count})" } + document::Title { "My Application (Count {count})" } button { onclick: move |_| count += 1, "Up high!" } button { onclick: move |_| count -= 1, "Down low!" } } diff --git a/examples/todomvc.rs b/examples/todomvc.rs index ea2e528a12..a95fda0e2d 100644 --- a/examples/todomvc.rs +++ b/examples/todomvc.rs @@ -3,10 +3,10 @@ use dioxus::prelude::*; use std::collections::HashMap; -const STYLE: &str = asset!("./examples/assets/todomvc.css"); +const STYLE: Asset = asset!("/examples/assets/todomvc.css"); fn main() { - launch(app); + dioxus::launch(app); } #[derive(PartialEq, Eq, Clone, Copy)] @@ -63,7 +63,7 @@ fn app() -> Element { }; rsx! { - head::Link { rel: "stylesheet", href: STYLE } + document::Stylesheet { href: STYLE } section { class: "todoapp", TodoHeader { todos } section { class: "main", diff --git a/examples/weather_app.rs b/examples/weather_app.rs index 395fd81f19..7b56bbbe54 100644 --- a/examples/weather_app.rs +++ b/examples/weather_app.rs @@ -4,7 +4,7 @@ use dioxus::prelude::*; use serde::{Deserialize, Serialize}; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { @@ -19,7 +19,7 @@ fn app() -> Element { let current_weather = use_resource(move || async move { get_weather(&country()).await }); rsx! { - head::Link { rel: "stylesheet", href: "https://unpkg.com/tailwindcss@^2.0/dist/tailwind.min.css" } + document::Stylesheet { href: "https://unpkg.com/tailwindcss@^2.0/dist/tailwind.min.css" } div { class: "mx-auto p-4 bg-gray-100 h-screen flex justify-center", div { class: "flex items-center justify-center flex-row", div { class: "flex items-start justify-center flex-row", diff --git a/examples/web_component.rs b/examples/web_component.rs index df0515397b..a5f831912b 100644 --- a/examples/web_component.rs +++ b/examples/web_component.rs @@ -7,7 +7,7 @@ use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/window_event.rs b/examples/window_event.rs index cb91d2a5b2..ca126f6a1d 100644 --- a/examples/window_event.rs +++ b/examples/window_event.rs @@ -13,7 +13,7 @@ use dioxus::desktop::{window, Config, WindowBuilder}; use dioxus::prelude::*; fn main() { - LaunchBuilder::desktop() + dioxus::launch::builder() .with_cfg( Config::new().with_window( WindowBuilder::new() @@ -26,7 +26,7 @@ fn main() { fn app() -> Element { rsx!( - head::Link { href: "https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css", rel: "stylesheet" } + document::Link { href: "https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css", rel: "stylesheet" } Header {} div { class: "container mx-auto", div { class: "grid grid-cols-5", diff --git a/examples/window_focus.rs b/examples/window_focus.rs index 447544077b..1213b3e994 100644 --- a/examples/window_focus.rs +++ b/examples/window_focus.rs @@ -12,7 +12,7 @@ use dioxus::desktop::{Config, WindowCloseBehaviour}; use dioxus::prelude::*; fn main() { - LaunchBuilder::desktop() + dioxus::launch::builder() .with_cfg(Config::new().with_close_behaviour(WindowCloseBehaviour::CloseWindow)) .launch(app) } diff --git a/examples/window_zoom.rs b/examples/window_zoom.rs index 8b10d503b0..35d25f4fde 100644 --- a/examples/window_zoom.rs +++ b/examples/window_zoom.rs @@ -5,7 +5,7 @@ use dioxus::prelude::*; fn main() { - launch_desktop(app); + dioxus::launch(app); } fn app() -> Element { diff --git a/examples/xss_safety.rs b/examples/xss_safety.rs index cb8d414dd7..904a42b07b 100644 --- a/examples/xss_safety.rs +++ b/examples/xss_safety.rs @@ -5,7 +5,7 @@ use dioxus::prelude::*; fn main() { - launch(app); + dioxus::launch(app); } fn app() -> Element { From 6d315612a4d611ec5f733983ed610032712a4ed6 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Sun, 15 Sep 2024 18:12:42 -0700 Subject: [PATCH 111/139] revert changes to examples --- examples/PWA-example/Cargo.toml | 17 + examples/PWA-example/Dioxus.toml | 27 + examples/PWA-example/LICENSE | 21 + examples/PWA-example/README.md | 45 + examples/PWA-example/index.html | 30 + examples/PWA-example/public/favicon.ico | Bin 0 -> 23104 bytes examples/PWA-example/public/logo_192.png | Bin 0 -> 11198 bytes examples/PWA-example/public/logo_512.png | Bin 0 -> 48151 bytes examples/PWA-example/public/manifest.json | 34 + examples/PWA-example/public/sw.js | 198 + examples/PWA-example/src/main.rs | 21 + examples/all_events.rs | 6 +- examples/assets/purecss.css | 12 - examples/backgrounded_futures.rs | 2 +- examples/calculator.rs | 6 +- examples/calculator_mutable.rs | 4 +- examples/clock.rs | 10 +- examples/control_focus.rs | 9 +- examples/counters.rs | 6 +- examples/crm.rs | 11 +- examples/custom_assets.rs | 32 +- examples/custom_html.rs | 2 +- examples/custom_menu.rs | 2 +- examples/disabled.rs | 2 +- examples/dog_app.rs | 2 +- examples/dynamic_asset.rs | 6 +- examples/errors.rs | 36 +- examples/eval.rs | 29 +- examples/file_explorer.rs | 8 +- examples/file_upload.rs | 6 +- examples/flat_router.rs | 6 +- examples/form.rs | 2 +- examples/future.rs | 2 +- examples/generic_component.rs | 2 +- examples/global.rs | 6 +- examples/hash_fragment_state.rs | 4 +- examples/hello_world.rs | 2 +- examples/hydration.rs | 2 +- examples/image_generator_openai.rs | 4 +- examples/link.rs | 6 +- examples/login_form.rs | 2 +- examples/memo_chain.rs | 2 +- examples/meta.rs | 13 +- examples/mobile_demo/.gitignore | 10 + examples/mobile_demo/Cargo.lock | 4007 +++++++++++ examples/mobile_demo/Cargo.toml | 51 + examples/mobile_demo/README.md | 11 + examples/mobile_demo/mobile.toml | 8 + examples/mobile_demo/src/index.html | 12 + examples/mobile_demo/src/lib.rs | 90 + examples/multiwindow.rs | 2 +- examples/nested_listeners.rs | 2 +- .../openid_connect_demo/.cargo/config.toml | 5 + examples/openid_connect_demo/.gitignore | 3 + examples/openid_connect_demo/Cargo.lock | 6372 +++++++++++++++++ examples/openid_connect_demo/Cargo.toml | 36 + examples/openid_connect_demo/Dioxus.toml | 35 + examples/openid_connect_demo/README.md | 12 + examples/openid_connect_demo/src/constants.rs | 2 + examples/openid_connect_demo/src/main.rs | 36 + examples/openid_connect_demo/src/model/mod.rs | 1 + .../openid_connect_demo/src/model/user.rs | 7 + examples/openid_connect_demo/src/oidc.rs | 127 + .../openid_connect_demo/src/props/client.rs | 20 + examples/openid_connect_demo/src/props/mod.rs | 1 + examples/openid_connect_demo/src/router.rs | 16 + examples/openid_connect_demo/src/storage.rs | 35 + .../openid_connect_demo/src/views/header.rs | 221 + .../openid_connect_demo/src/views/home.rs | 5 + .../openid_connect_demo/src/views/login.rs | 86 + examples/openid_connect_demo/src/views/mod.rs | 4 + .../src/views/not_found.rs | 10 + examples/optional_props.rs | 2 +- examples/overlay.rs | 8 +- examples/popup.rs | 2 +- examples/query_segment_search.rs | 6 +- examples/read_size.rs | 4 +- examples/readme.rs | 2 +- examples/reducer.rs | 6 +- examples/resize.rs | 4 +- examples/router.rs | 6 +- examples/router_resource.rs | 2 +- examples/rsx_usage.rs | 5 +- examples/scroll_to_top.rs | 2 +- examples/shortcut.rs | 2 +- examples/shorthand.rs | 2 +- examples/signals.rs | 2 +- examples/simple_list.rs | 2 +- examples/simple_router.rs | 2 +- examples/streams.rs | 2 +- examples/suspense.rs | 2 +- examples/svg.rs | 2 +- examples/tailwind/.gitignore | 1 + examples/tailwind/Cargo.toml | 20 + examples/tailwind/Dioxus.toml | 27 + examples/tailwind/README.md | 7 + examples/tailwind/input.css | 3 + examples/tailwind/public/tailwind.css | 833 +++ examples/tailwind/src/main.rs | 103 + examples/tailwind/tailwind.config.js | 9 + examples/title.rs | 5 +- examples/todomvc.rs | 6 +- examples/weather_app.rs | 4 +- examples/web_component.rs | 2 +- examples/window_event.rs | 4 +- examples/window_focus.rs | 2 +- examples/window_zoom.rs | 2 +- examples/xss_safety.rs | 2 +- 108 files changed, 12795 insertions(+), 164 deletions(-) create mode 100644 examples/PWA-example/Cargo.toml create mode 100644 examples/PWA-example/Dioxus.toml create mode 100644 examples/PWA-example/LICENSE create mode 100644 examples/PWA-example/README.md create mode 100644 examples/PWA-example/index.html create mode 100644 examples/PWA-example/public/favicon.ico create mode 100644 examples/PWA-example/public/logo_192.png create mode 100644 examples/PWA-example/public/logo_512.png create mode 100644 examples/PWA-example/public/manifest.json create mode 100644 examples/PWA-example/public/sw.js create mode 100644 examples/PWA-example/src/main.rs delete mode 100644 examples/assets/purecss.css create mode 100644 examples/mobile_demo/.gitignore create mode 100644 examples/mobile_demo/Cargo.lock create mode 100644 examples/mobile_demo/Cargo.toml create mode 100644 examples/mobile_demo/README.md create mode 100644 examples/mobile_demo/mobile.toml create mode 100644 examples/mobile_demo/src/index.html create mode 100644 examples/mobile_demo/src/lib.rs create mode 100644 examples/openid_connect_demo/.cargo/config.toml create mode 100644 examples/openid_connect_demo/.gitignore create mode 100644 examples/openid_connect_demo/Cargo.lock create mode 100644 examples/openid_connect_demo/Cargo.toml create mode 100644 examples/openid_connect_demo/Dioxus.toml create mode 100644 examples/openid_connect_demo/README.md create mode 100644 examples/openid_connect_demo/src/constants.rs create mode 100644 examples/openid_connect_demo/src/main.rs create mode 100644 examples/openid_connect_demo/src/model/mod.rs create mode 100644 examples/openid_connect_demo/src/model/user.rs create mode 100644 examples/openid_connect_demo/src/oidc.rs create mode 100644 examples/openid_connect_demo/src/props/client.rs create mode 100644 examples/openid_connect_demo/src/props/mod.rs create mode 100644 examples/openid_connect_demo/src/router.rs create mode 100644 examples/openid_connect_demo/src/storage.rs create mode 100644 examples/openid_connect_demo/src/views/header.rs create mode 100644 examples/openid_connect_demo/src/views/home.rs create mode 100644 examples/openid_connect_demo/src/views/login.rs create mode 100644 examples/openid_connect_demo/src/views/mod.rs create mode 100644 examples/openid_connect_demo/src/views/not_found.rs create mode 100644 examples/tailwind/.gitignore create mode 100644 examples/tailwind/Cargo.toml create mode 100644 examples/tailwind/Dioxus.toml create mode 100644 examples/tailwind/README.md create mode 100644 examples/tailwind/input.css create mode 100644 examples/tailwind/public/tailwind.css create mode 100644 examples/tailwind/src/main.rs create mode 100644 examples/tailwind/tailwind.config.js diff --git a/examples/PWA-example/Cargo.toml b/examples/PWA-example/Cargo.toml new file mode 100644 index 0000000000..bee204f20a --- /dev/null +++ b/examples/PWA-example/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "dioxus-pwa-example" +version = "0.1.0" +authors = ["Antonio Curavalea "] +edition = "2021" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +dioxus = { workspace = true, features = ["web"] } + +log = "0.4.6" + +# WebAssembly Debug +wasm-logger = "0.2.0" +console_error_panic_hook = "0.1.7" diff --git a/examples/PWA-example/Dioxus.toml b/examples/PWA-example/Dioxus.toml new file mode 100644 index 0000000000..a831a28ff7 --- /dev/null +++ b/examples/PWA-example/Dioxus.toml @@ -0,0 +1,27 @@ +[application] + +# App (Project) Name +name = "dioxus-pwa-example" + +# Dioxus App Default Platform +# desktop, web, mobile, ssr +default_platform = "web" + +# `build` & `serve` dist path +out_dir = "dist" + +# resource (public) file folder +asset_dir = "public" + +[web.app] + +# HTML title tag content +title = "dioxus | ⛺" + +[web.watcher] + +# when watcher trigger, regenerate the `index.html` +reload_html = true + +# which files or dirs will be watcher monitoring +watch_path = ["src", "public"] diff --git a/examples/PWA-example/LICENSE b/examples/PWA-example/LICENSE new file mode 100644 index 0000000000..bcdd828e9c --- /dev/null +++ b/examples/PWA-example/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Dioxus + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/examples/PWA-example/README.md b/examples/PWA-example/README.md new file mode 100644 index 0000000000..d501df2126 --- /dev/null +++ b/examples/PWA-example/README.md @@ -0,0 +1,45 @@ +# Dioxus PWA example + +This is a basic example of a progressive web app (PWA) using Dioxus and Dioxus CLI. +Currently PWA functionality requires the use of a service worker and manifest file, so this isn't 100% Rust yet. + +It is also very much usable as a template for your projects, if you're aiming to create a PWA. + +## Try the example + +Make sure you have Dioxus CLI installed (if you're unsure, run `cargo install dioxus-cli --locked`). + +You can run `dx serve` in this directory to start the web server locally, or run +`dx build --release` to build the project so you can deploy it on a separate web-server. + +## Project Structure + +``` +├── Cargo.toml +├── Dioxus.toml +├── index.html // Custom HTML is needed for this, to load the SW and manifest. +├── LICENSE +├── public +│ ├── favicon.ico +│ ├── logo_192.png +│ ├── logo_512.png +│ ├── manifest.json // The manifest file - edit this as you need to. +│ └── sw.js // The service worker - you must edit this for actual projects. +├── README.md +└── src + └── main.rs +``` + +## Resources + +If you're just getting started with PWAs, here are some useful resources: + +- [PWABuilder docs](https://docs.pwabuilder.com/#/) +- [MDN article on PWAs](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps) + +For service worker scripting (in JavaScript): + +- [Service worker guide from PWABuilder](https://docs.pwabuilder.com/#/home/sw-intro) +- [Service worker examples, also from PWABuilder](https://github.com/pwa-builder/pwabuilder-serviceworkers) + +If you want to stay as close to 100% Rust as possible, you can try using [wasi-worker](https://github.com/dunnock/wasi-worker) to replace the JS service worker file. The JSON manifest will still be required though. diff --git a/examples/PWA-example/index.html b/examples/PWA-example/index.html new file mode 100644 index 0000000000..44bfa59cc4 --- /dev/null +++ b/examples/PWA-example/index.html @@ -0,0 +1,30 @@ + + + + {app_title} + + + + + + {style_include} + + +
      + + {script_include} + + \ No newline at end of file diff --git a/examples/PWA-example/public/favicon.ico b/examples/PWA-example/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..b11015bdbacdb3b99e03555edb85d78467b37c7d GIT binary patch literal 23104 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 W+`s-{ME)24jHW23DqAIO8vH-~t)grI literal 0 HcmV?d00001 diff --git a/examples/PWA-example/public/logo_192.png b/examples/PWA-example/public/logo_192.png new file mode 100644 index 0000000000000000000000000000000000000000..112eb5ee49ca4e69d742a99269d1ae08a970dd7a GIT binary patch literal 11198 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 literal 0 HcmV?d00001 diff --git a/examples/PWA-example/public/logo_512.png b/examples/PWA-example/public/logo_512.png new file mode 100644 index 0000000000000000000000000000000000000000..43129772a892a5ca82dfdc8bb4d9027cc830ddf5 GIT binary patch literal 48151 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 Unavailable', { + status: 503, + statusText: 'Service Unavailable', + headers: new Headers({ + 'Content-Type': 'text/html' + }) + }); + } + }) + ); +}); + +/* The activate event fires after a service worker has been successfully installed. + It is most useful when phasing out an older version of a service worker, as at + this point you know that the new worker was installed correctly. In this example, + we delete old caches that don't match the version in the worker we just finished + installing. +*/ +self.addEventListener("activate", function (event) { + /* Just like with the install event, event.waitUntil blocks activate on a promise. + Activation will fail unless the promise is fulfilled. + */ + //console.log('WORKER: activate event in progress.'); + + event.waitUntil( + caches + /* This method returns a promise which will resolve to an array of available + cache keys. + */ + .keys() + .then(function (keys) { + // We return a promise that settles when all outdated caches are deleted. + return Promise.all( + keys + .filter(function (key) { + // Filter by keys that don't start with the latest version prefix. + return !key.startsWith(version); + }) + .map(function (key) { + /* Return a promise that's fulfilled + when each outdated cache is deleted. + */ + return caches.delete(key); + }) + ); + }) + .then(function () { + //console.log('WORKER: activate completed.'); + }) + ); +}); diff --git a/examples/PWA-example/src/main.rs b/examples/PWA-example/src/main.rs new file mode 100644 index 0000000000..3e4d5b2361 --- /dev/null +++ b/examples/PWA-example/src/main.rs @@ -0,0 +1,21 @@ +use dioxus::prelude::*; + +fn main() { + // init debug tool for WebAssembly + wasm_logger::init(wasm_logger::Config::default()); + console_error_panic_hook::set_once(); + + launch(app); +} + +fn app() -> Element { + rsx! ( + div { style: "text-align: center;", + h1 { "🌗 Dioxus 🚀" } + h3 { "Frontend that scales." } + p { + "Dioxus is a portable, performant, and ergonomic framework for building cross-platform user interfaces in Rust." + } + } + ) +} diff --git a/examples/all_events.rs b/examples/all_events.rs index 4e4ca3d6d5..2a04ee2bb3 100644 --- a/examples/all_events.rs +++ b/examples/all_events.rs @@ -6,8 +6,10 @@ use dioxus::prelude::*; use std::{collections::VecDeque, fmt::Debug, rc::Rc}; +const STYLE: &str = asset!("./examples/assets/events.css"); + fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { @@ -24,7 +26,7 @@ fn app() -> Element { }; rsx! { - document::Stylesheet { href: asset!("/examples/assets/events.css") } + head::Link { rel: "stylesheet", href: STYLE } div { id: "container", // focusing is necessary to catch keyboard events div { id: "receiver", tabindex: 0, diff --git a/examples/assets/purecss.css b/examples/assets/purecss.css deleted file mode 100644 index dd301cc364..0000000000 --- a/examples/assets/purecss.css +++ /dev/null @@ -1,12 +0,0 @@ -/*! -Pure v2.0.6 -Copyright 2013 Yahoo! -Licensed under the BSD License. -https://github.com/pure-css/pure/blob/master/LICENSE -*/ -/*! -normalize.css v | MIT License | git.io/normalize -Copyright (c) Nicolas Gallagher and Jonathan Neal -*/ -/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ -html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{-webkit-box-sizing:content-box;box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{-webkit-box-sizing:border-box;box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{-webkit-box-sizing:border-box;box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}html{font-family:sans-serif}.hidden,[hidden]{display:none!important}.pure-img{max-width:100%;height:auto;display:block}.pure-g{letter-spacing:-.31em;text-rendering:optimizespeed;font-family:FreeSans,Arimo,"Droid Sans",Helvetica,Arial,sans-serif;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-line-pack:start;align-content:flex-start}@media all and (-ms-high-contrast:none),(-ms-high-contrast:active){table .pure-g{display:block}}.opera-only :-o-prefocus,.pure-g{word-spacing:-0.43em}.pure-u{display:inline-block;letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-g [class*=pure-u]{font-family:sans-serif}.pure-u-1,.pure-u-1-1,.pure-u-1-12,.pure-u-1-2,.pure-u-1-24,.pure-u-1-3,.pure-u-1-4,.pure-u-1-5,.pure-u-1-6,.pure-u-1-8,.pure-u-10-24,.pure-u-11-12,.pure-u-11-24,.pure-u-12-24,.pure-u-13-24,.pure-u-14-24,.pure-u-15-24,.pure-u-16-24,.pure-u-17-24,.pure-u-18-24,.pure-u-19-24,.pure-u-2-24,.pure-u-2-3,.pure-u-2-5,.pure-u-20-24,.pure-u-21-24,.pure-u-22-24,.pure-u-23-24,.pure-u-24-24,.pure-u-3-24,.pure-u-3-4,.pure-u-3-5,.pure-u-3-8,.pure-u-4-24,.pure-u-4-5,.pure-u-5-12,.pure-u-5-24,.pure-u-5-5,.pure-u-5-6,.pure-u-5-8,.pure-u-6-24,.pure-u-7-12,.pure-u-7-24,.pure-u-7-8,.pure-u-8-24,.pure-u-9-24{display:inline-block;letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-u-1-24{width:4.1667%}.pure-u-1-12,.pure-u-2-24{width:8.3333%}.pure-u-1-8,.pure-u-3-24{width:12.5%}.pure-u-1-6,.pure-u-4-24{width:16.6667%}.pure-u-1-5{width:20%}.pure-u-5-24{width:20.8333%}.pure-u-1-4,.pure-u-6-24{width:25%}.pure-u-7-24{width:29.1667%}.pure-u-1-3,.pure-u-8-24{width:33.3333%}.pure-u-3-8,.pure-u-9-24{width:37.5%}.pure-u-2-5{width:40%}.pure-u-10-24,.pure-u-5-12{width:41.6667%}.pure-u-11-24{width:45.8333%}.pure-u-1-2,.pure-u-12-24{width:50%}.pure-u-13-24{width:54.1667%}.pure-u-14-24,.pure-u-7-12{width:58.3333%}.pure-u-3-5{width:60%}.pure-u-15-24,.pure-u-5-8{width:62.5%}.pure-u-16-24,.pure-u-2-3{width:66.6667%}.pure-u-17-24{width:70.8333%}.pure-u-18-24,.pure-u-3-4{width:75%}.pure-u-19-24{width:79.1667%}.pure-u-4-5{width:80%}.pure-u-20-24,.pure-u-5-6{width:83.3333%}.pure-u-21-24,.pure-u-7-8{width:87.5%}.pure-u-11-12,.pure-u-22-24{width:91.6667%}.pure-u-23-24{width:95.8333%}.pure-u-1,.pure-u-1-1,.pure-u-24-24,.pure-u-5-5{width:100%}.pure-button{display:inline-block;line-height:normal;white-space:nowrap;vertical-align:middle;text-align:center;cursor:pointer;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-box-sizing:border-box;box-sizing:border-box}.pure-button::-moz-focus-inner{padding:0;border:0}.pure-button-group{letter-spacing:-.31em;text-rendering:optimizespeed}.opera-only :-o-prefocus,.pure-button-group{word-spacing:-0.43em}.pure-button-group .pure-button{letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-button{font-family:inherit;font-size:100%;padding:.5em 1em;color:rgba(0,0,0,.8);border:none transparent;background-color:#e6e6e6;text-decoration:none;border-radius:2px}.pure-button-hover,.pure-button:focus,.pure-button:hover{background-image:-webkit-gradient(linear,left top,left bottom,from(transparent),color-stop(40%,rgba(0,0,0,.05)),to(rgba(0,0,0,.1)));background-image:linear-gradient(transparent,rgba(0,0,0,.05) 40%,rgba(0,0,0,.1))}.pure-button:focus{outline:0}.pure-button-active,.pure-button:active{-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.15) inset,0 0 6px rgba(0,0,0,.2) inset;box-shadow:0 0 0 1px rgba(0,0,0,.15) inset,0 0 6px rgba(0,0,0,.2) inset;border-color:#000}.pure-button-disabled,.pure-button-disabled:active,.pure-button-disabled:focus,.pure-button-disabled:hover,.pure-button[disabled]{border:none;background-image:none;opacity:.4;cursor:not-allowed;-webkit-box-shadow:none;box-shadow:none;pointer-events:none}.pure-button-hidden{display:none}.pure-button-primary,.pure-button-selected,a.pure-button-primary,a.pure-button-selected{background-color:#0078e7;color:#fff}.pure-button-group .pure-button{margin:0;border-radius:0;border-right:1px solid rgba(0,0,0,.2)}.pure-button-group .pure-button:first-child{border-top-left-radius:2px;border-bottom-left-radius:2px}.pure-button-group .pure-button:last-child{border-top-right-radius:2px;border-bottom-right-radius:2px;border-right:none}.pure-form input[type=color],.pure-form input[type=date],.pure-form input[type=datetime-local],.pure-form input[type=datetime],.pure-form input[type=email],.pure-form input[type=month],.pure-form input[type=number],.pure-form input[type=password],.pure-form input[type=search],.pure-form input[type=tel],.pure-form input[type=text],.pure-form input[type=time],.pure-form input[type=url],.pure-form input[type=week],.pure-form select,.pure-form textarea{padding:.5em .6em;display:inline-block;border:1px solid #ccc;-webkit-box-shadow:inset 0 1px 3px #ddd;box-shadow:inset 0 1px 3px #ddd;border-radius:4px;vertical-align:middle;-webkit-box-sizing:border-box;box-sizing:border-box}.pure-form input:not([type]){padding:.5em .6em;display:inline-block;border:1px solid #ccc;-webkit-box-shadow:inset 0 1px 3px #ddd;box-shadow:inset 0 1px 3px #ddd;border-radius:4px;-webkit-box-sizing:border-box;box-sizing:border-box}.pure-form input[type=color]{padding:.2em .5em}.pure-form input[type=color]:focus,.pure-form input[type=date]:focus,.pure-form input[type=datetime-local]:focus,.pure-form input[type=datetime]:focus,.pure-form input[type=email]:focus,.pure-form input[type=month]:focus,.pure-form input[type=number]:focus,.pure-form input[type=password]:focus,.pure-form input[type=search]:focus,.pure-form input[type=tel]:focus,.pure-form input[type=text]:focus,.pure-form input[type=time]:focus,.pure-form input[type=url]:focus,.pure-form input[type=week]:focus,.pure-form select:focus,.pure-form textarea:focus{outline:0;border-color:#129fea}.pure-form input:not([type]):focus{outline:0;border-color:#129fea}.pure-form input[type=checkbox]:focus,.pure-form input[type=file]:focus,.pure-form input[type=radio]:focus{outline:thin solid #129FEA;outline:1px auto #129FEA}.pure-form .pure-checkbox,.pure-form .pure-radio{margin:.5em 0;display:block}.pure-form input[type=color][disabled],.pure-form input[type=date][disabled],.pure-form input[type=datetime-local][disabled],.pure-form input[type=datetime][disabled],.pure-form input[type=email][disabled],.pure-form input[type=month][disabled],.pure-form input[type=number][disabled],.pure-form input[type=password][disabled],.pure-form input[type=search][disabled],.pure-form input[type=tel][disabled],.pure-form input[type=text][disabled],.pure-form input[type=time][disabled],.pure-form input[type=url][disabled],.pure-form input[type=week][disabled],.pure-form select[disabled],.pure-form textarea[disabled]{cursor:not-allowed;background-color:#eaeded;color:#cad2d3}.pure-form input:not([type])[disabled]{cursor:not-allowed;background-color:#eaeded;color:#cad2d3}.pure-form input[readonly],.pure-form select[readonly],.pure-form textarea[readonly]{background-color:#eee;color:#777;border-color:#ccc}.pure-form input:focus:invalid,.pure-form select:focus:invalid,.pure-form textarea:focus:invalid{color:#b94a48;border-color:#e9322d}.pure-form input[type=checkbox]:focus:invalid:focus,.pure-form input[type=file]:focus:invalid:focus,.pure-form input[type=radio]:focus:invalid:focus{outline-color:#e9322d}.pure-form select{height:2.25em;border:1px solid #ccc;background-color:#fff}.pure-form select[multiple]{height:auto}.pure-form label{margin:.5em 0 .2em}.pure-form fieldset{margin:0;padding:.35em 0 .75em;border:0}.pure-form legend{display:block;width:100%;padding:.3em 0;margin-bottom:.3em;color:#333;border-bottom:1px solid #e5e5e5}.pure-form-stacked input[type=color],.pure-form-stacked input[type=date],.pure-form-stacked input[type=datetime-local],.pure-form-stacked input[type=datetime],.pure-form-stacked input[type=email],.pure-form-stacked input[type=file],.pure-form-stacked input[type=month],.pure-form-stacked input[type=number],.pure-form-stacked input[type=password],.pure-form-stacked input[type=search],.pure-form-stacked input[type=tel],.pure-form-stacked input[type=text],.pure-form-stacked input[type=time],.pure-form-stacked input[type=url],.pure-form-stacked input[type=week],.pure-form-stacked label,.pure-form-stacked select,.pure-form-stacked textarea{display:block;margin:.25em 0}.pure-form-stacked input:not([type]){display:block;margin:.25em 0}.pure-form-aligned input,.pure-form-aligned select,.pure-form-aligned textarea,.pure-form-message-inline{display:inline-block;vertical-align:middle}.pure-form-aligned textarea{vertical-align:top}.pure-form-aligned .pure-control-group{margin-bottom:.5em}.pure-form-aligned .pure-control-group label{text-align:right;display:inline-block;vertical-align:middle;width:10em;margin:0 1em 0 0}.pure-form-aligned .pure-controls{margin:1.5em 0 0 11em}.pure-form .pure-input-rounded,.pure-form input.pure-input-rounded{border-radius:2em;padding:.5em 1em}.pure-form .pure-group fieldset{margin-bottom:10px}.pure-form .pure-group input,.pure-form .pure-group textarea{display:block;padding:10px;margin:0 0 -1px;border-radius:0;position:relative;top:-1px}.pure-form .pure-group input:focus,.pure-form .pure-group textarea:focus{z-index:3}.pure-form .pure-group input:first-child,.pure-form .pure-group textarea:first-child{top:1px;border-radius:4px 4px 0 0;margin:0}.pure-form .pure-group input:first-child:last-child,.pure-form .pure-group textarea:first-child:last-child{top:1px;border-radius:4px;margin:0}.pure-form .pure-group input:last-child,.pure-form .pure-group textarea:last-child{top:-2px;border-radius:0 0 4px 4px;margin:0}.pure-form .pure-group button{margin:.35em 0}.pure-form .pure-input-1{width:100%}.pure-form .pure-input-3-4{width:75%}.pure-form .pure-input-2-3{width:66%}.pure-form .pure-input-1-2{width:50%}.pure-form .pure-input-1-3{width:33%}.pure-form .pure-input-1-4{width:25%}.pure-form-message-inline{display:inline-block;padding-left:.3em;color:#666;vertical-align:middle;font-size:.875em}.pure-form-message{display:block;color:#666;font-size:.875em}@media only screen and (max-width :480px){.pure-form button[type=submit]{margin:.7em 0 0}.pure-form input:not([type]),.pure-form input[type=color],.pure-form input[type=date],.pure-form input[type=datetime-local],.pure-form input[type=datetime],.pure-form input[type=email],.pure-form input[type=month],.pure-form input[type=number],.pure-form input[type=password],.pure-form input[type=search],.pure-form input[type=tel],.pure-form input[type=text],.pure-form input[type=time],.pure-form input[type=url],.pure-form input[type=week],.pure-form label{margin-bottom:.3em;display:block}.pure-group input:not([type]),.pure-group input[type=color],.pure-group input[type=date],.pure-group input[type=datetime-local],.pure-group input[type=datetime],.pure-group input[type=email],.pure-group input[type=month],.pure-group input[type=number],.pure-group input[type=password],.pure-group input[type=search],.pure-group input[type=tel],.pure-group input[type=text],.pure-group input[type=time],.pure-group input[type=url],.pure-group input[type=week]{margin-bottom:0}.pure-form-aligned .pure-control-group label{margin-bottom:.3em;text-align:left;display:block;width:100%}.pure-form-aligned .pure-controls{margin:1.5em 0 0 0}.pure-form-message,.pure-form-message-inline{display:block;font-size:.75em;padding:.2em 0 .8em}}.pure-menu{-webkit-box-sizing:border-box;box-sizing:border-box}.pure-menu-fixed{position:fixed;left:0;top:0;z-index:3}.pure-menu-item,.pure-menu-list{position:relative}.pure-menu-list{list-style:none;margin:0;padding:0}.pure-menu-item{padding:0;margin:0;height:100%}.pure-menu-heading,.pure-menu-link{display:block;text-decoration:none;white-space:nowrap}.pure-menu-horizontal{width:100%;white-space:nowrap}.pure-menu-horizontal .pure-menu-list{display:inline-block}.pure-menu-horizontal .pure-menu-heading,.pure-menu-horizontal .pure-menu-item,.pure-menu-horizontal .pure-menu-separator{display:inline-block;vertical-align:middle}.pure-menu-item .pure-menu-item{display:block}.pure-menu-children{display:none;position:absolute;left:100%;top:0;margin:0;padding:0;z-index:3}.pure-menu-horizontal .pure-menu-children{left:0;top:auto;width:inherit}.pure-menu-active>.pure-menu-children,.pure-menu-allow-hover:hover>.pure-menu-children{display:block;position:absolute}.pure-menu-has-children>.pure-menu-link:after{padding-left:.5em;content:"\25B8";font-size:small}.pure-menu-horizontal .pure-menu-has-children>.pure-menu-link:after{content:"\25BE"}.pure-menu-scrollable{overflow-y:scroll;overflow-x:hidden}.pure-menu-scrollable .pure-menu-list{display:block}.pure-menu-horizontal.pure-menu-scrollable .pure-menu-list{display:inline-block}.pure-menu-horizontal.pure-menu-scrollable{white-space:nowrap;overflow-y:hidden;overflow-x:auto;padding:.5em 0}.pure-menu-horizontal .pure-menu-children .pure-menu-separator,.pure-menu-separator{background-color:#ccc;height:1px;margin:.3em 0}.pure-menu-horizontal .pure-menu-separator{width:1px;height:1.3em;margin:0 .3em}.pure-menu-horizontal .pure-menu-children .pure-menu-separator{display:block;width:auto}.pure-menu-heading{text-transform:uppercase;color:#565d64}.pure-menu-link{color:#777}.pure-menu-children{background-color:#fff}.pure-menu-heading,.pure-menu-link{padding:.5em 1em}.pure-menu-disabled{opacity:.5}.pure-menu-disabled .pure-menu-link:hover{background-color:transparent;cursor:default}.pure-menu-active>.pure-menu-link,.pure-menu-link:focus,.pure-menu-link:hover{background-color:#eee}.pure-menu-selected>.pure-menu-link,.pure-menu-selected>.pure-menu-link:visited{color:#000}.pure-table{border-collapse:collapse;border-spacing:0;empty-cells:show;border:1px solid #cbcbcb}.pure-table caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.pure-table td,.pure-table th{border-left:1px solid #cbcbcb;border-width:0 0 0 1px;font-size:inherit;margin:0;overflow:visible;padding:.5em 1em}.pure-table thead{background-color:#e0e0e0;color:#000;text-align:left;vertical-align:bottom}.pure-table td{background-color:transparent}.pure-table-odd td{background-color:#f2f2f2}.pure-table-striped tr:nth-child(2n-1) td{background-color:#f2f2f2}.pure-table-bordered td{border-bottom:1px solid #cbcbcb}.pure-table-bordered tbody>tr:last-child>td{border-bottom-width:0}.pure-table-horizontal td,.pure-table-horizontal th{border-width:0 0 1px 0;border-bottom:1px solid #cbcbcb}.pure-table-horizontal tbody>tr:last-child>td{border-bottom-width:0} diff --git a/examples/backgrounded_futures.rs b/examples/backgrounded_futures.rs index 4fabef09f7..4cafad439e 100644 --- a/examples/backgrounded_futures.rs +++ b/examples/backgrounded_futures.rs @@ -11,7 +11,7 @@ use async_std::task::sleep; use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/calculator.rs b/examples/calculator.rs index b373923f6b..9bf2a04618 100644 --- a/examples/calculator.rs +++ b/examples/calculator.rs @@ -12,10 +12,10 @@ use dioxus::events::*; use dioxus::html::input_data::keyboard_types::Key; use dioxus::prelude::*; -const STYLE: Asset = asset!("/examples/assets/calculator.css"); +const STYLE: &str = asset!("./examples/assets/calculator.css"); fn main() { - dioxus::launch::builder() + LaunchBuilder::desktop() .with_cfg(desktop!({ use dioxus::desktop::{Config, LogicalSize, WindowBuilder}; Config::new().with_window( @@ -54,7 +54,7 @@ fn app() -> Element { }; rsx! { - document::Stylesheet { href: STYLE } + head::Link { rel: "stylesheet", href: STYLE } div { id: "wrapper", div { class: "app", div { class: "calculator", tabindex: "0", onkeydown: handle_key_down_event, diff --git a/examples/calculator_mutable.rs b/examples/calculator_mutable.rs index 9d1ec05fbc..2f933ebbea 100644 --- a/examples/calculator_mutable.rs +++ b/examples/calculator_mutable.rs @@ -13,7 +13,7 @@ use dioxus::html::MouseEvent; use dioxus::prelude::*; fn main() { - dioxus::launch::builder() + LaunchBuilder::desktop() .with_cfg( Config::new().with_window( WindowBuilder::new() @@ -29,7 +29,7 @@ fn app() -> Element { let mut state = use_signal(Calculator::new); rsx! { - document::Stylesheet { href: asset!("/examples/assets/calculator.css") } + head::Link { rel: "stylesheet", href: asset!("./examples/assets/calculator.css") } div { id: "wrapper", div { class: "app", div { diff --git a/examples/clock.rs b/examples/clock.rs index d344311fae..dc168a5ec2 100644 --- a/examples/clock.rs +++ b/examples/clock.rs @@ -5,18 +5,18 @@ use async_std::task::sleep; use dioxus::prelude::*; use web_time::Instant; -const STYLE: Asset = asset!("/examples/assets/clock.css"); +const STYLE: &str = asset!("./examples/assets/clock.css"); fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { let mut millis = use_signal(|| 0); use_future(move || async move { - // Save our initial time - let start = std::time::Instant::now(); + // Save our initial timea + let start = Instant::now(); loop { sleep(std::time::Duration::from_millis(27)).await; @@ -36,7 +36,7 @@ fn app() -> Element { ); rsx! { - document::Stylesheet { href: STYLE } + head::Link { rel: "stylesheet", href: STYLE } div { id: "app", div { id: "title", "Carpe diem 🎉" } div { id: "clock-display", "{time}" } diff --git a/examples/control_focus.rs b/examples/control_focus.rs index 6a383fa662..c69293c56d 100644 --- a/examples/control_focus.rs +++ b/examples/control_focus.rs @@ -5,8 +5,13 @@ use std::rc::Rc; +use async_std::task::sleep; +use dioxus::prelude::*; + +const STYLE: &str = asset!("./examples/assets/roulette.css"); + fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { @@ -35,7 +40,7 @@ fn app() -> Element { }); rsx! { - document::Stylesheet { href: asset!("/examples/assets/roulette.css") } + head::Link { rel: "stylesheet", href: STYLE } h1 { "Input Roulette" } button { onclick: move |_| running.toggle(), "Toggle roulette" } div { id: "roulette-grid", diff --git a/examples/counters.rs b/examples/counters.rs index 63013a1cc5..f2d8c4203c 100644 --- a/examples/counters.rs +++ b/examples/counters.rs @@ -2,8 +2,10 @@ use dioxus::prelude::*; +const STYLE: &str = asset!("./examples/assets/counter.css"); + fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { @@ -14,7 +16,7 @@ fn app() -> Element { let sum = use_memo(move || counters.read().iter().copied().sum::()); rsx! { - document::Stylesheet { href: asset!("/examples/assets/counter.css") } + head::Link { rel: "stylesheet", href: STYLE } div { id: "controls", button { onclick: move |_| counters.write().push(0), "Add counter" } diff --git a/examples/crm.rs b/examples/crm.rs index ca83f58d40..21f07466cd 100644 --- a/examples/crm.rs +++ b/examples/crm.rs @@ -12,7 +12,7 @@ use dioxus::prelude::*; fn main() { - dioxus::builder() + LaunchBuilder::new() .with_cfg(desktop!({ use dioxus::desktop::{LogicalSize, WindowBuilder}; dioxus::desktop::Config::default() @@ -20,8 +20,13 @@ fn main() { })) .launch(|| { rsx! { - document::Stylesheet { href: asset!("/examples/assets/crm.css") } - document::Link { href: asset!("/examples/assets/purecss.css") } + head::Link { + rel: "stylesheet", + href: asset!("https://unpkg.com/purecss@2.0.6/build/pure-min.css"), + integrity: "sha384-Uu6IeWbM+gzNVXJcM9XV3SohHtmWE+3VGi496jvgX1jyvDTXfdK+rfZc8C1Aehk5", + crossorigin: "anonymous" + } + head::Link { rel: "stylesheet", href: asset!("./examples/assets/crm.css") } h1 { "Dioxus CRM Example" } Router:: {} } diff --git a/examples/custom_assets.rs b/examples/custom_assets.rs index 8a56530110..6c927d21a4 100644 --- a/examples/custom_assets.rs +++ b/examples/custom_assets.rs @@ -1,37 +1,27 @@ //! A simple example on how to use assets loading from the filesystem. //! -//! Dioxus provides an asset!() macro which properly handles asset loading and bundling for you. -//! For bundling, asset!() must be paired with a tool that handles mangansis-link sections. The dioxus-cli -//! handles this for you, but this means you can't just simply `cargo build --release` to build and -//! distribute your app. +//! If the feature "collect-assets" is enabled, the assets will be collected via the dioxus CLI and embedded into the +//! final bundle. This lets you do various useful things like minify, compress, and optimize your assets. //! -//! You can run this example with `cargo run --example assets` or `dx serve --example assets`. -//! When manganis is not active, the asset!() macro will fallback to the path of the asset on -//! your filesystem. +//! We can still use assets without the CLI middleware, but generally larger apps will benefit from it. + use dioxus::prelude::*; -/// asset!() will mark this asset as a dependency of the app without actually including it in the -/// generated code. This is better than include_str!() or include_bytes!() since it works -/// for web apps as well as native and mobile apps. -/// -/// When used with web apps, manganis will detect the import of the image, optimize it, and put it -/// in the output dist folder in the right location, ensuring no two images have the same name. -static IMAGE: ImageAsset = asset!("/examples/assets/logo.png".image().format(ImageType::Avif)); +#[cfg(not(feature = "collect-assets"))] +static ASSET_PATH: &str = "examples/assets/logo.png"; + +#[cfg(feature = "collect-assets")] +static ASSET_PATH: &str = asset!("examples/assets/logo.png".format(ImageType::Avif)); fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { rsx! { div { h1 { "This should show an image:" } - img { src: IMAGE } - - // temporarily keep support for these too - img { src: "/Users/jonkelley/Development/dioxus/examples/assets/logo.png" } - img { src: "/examples/assets/logo.png" } - img { src: "examples/assets/logo.png" } + img { src: ASSET_PATH.to_string() } } } } diff --git a/examples/custom_html.rs b/examples/custom_html.rs index f1ea4b91ad..dbbe334f05 100644 --- a/examples/custom_html.rs +++ b/examples/custom_html.rs @@ -4,7 +4,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch::builder() + LaunchBuilder::new() .with_cfg( dioxus::desktop::Config::new().with_custom_index( r#" diff --git a/examples/custom_menu.rs b/examples/custom_menu.rs index da1085bfd5..f78f33c884 100644 --- a/examples/custom_menu.rs +++ b/examples/custom_menu.rs @@ -28,7 +28,7 @@ fn main() { let config = dioxus::desktop::Config::new().with_menu(menu); // Launch the app with the custom menu - dioxus::launch::builder().with_cfg(config).launch(app) + LaunchBuilder::new().with_cfg(config).launch(app) } fn app() -> Element { diff --git a/examples/disabled.rs b/examples/disabled.rs index b48b1a912e..6aaa70b9d4 100644 --- a/examples/disabled.rs +++ b/examples/disabled.rs @@ -5,7 +5,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/dog_app.rs b/examples/dog_app.rs index 09bf5f0a96..1c53c8731f 100644 --- a/examples/dog_app.rs +++ b/examples/dog_app.rs @@ -11,7 +11,7 @@ use dioxus::prelude::*; use std::collections::HashMap; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/dynamic_asset.rs b/examples/dynamic_asset.rs index b741ba57af..14a33c4809 100644 --- a/examples/dynamic_asset.rs +++ b/examples/dynamic_asset.rs @@ -7,8 +7,10 @@ use dioxus::desktop::{use_asset_handler, wry::http::Response}; use dioxus::prelude::*; +const STYLE: &str = asset!("./examples/assets/custom_assets.css"); + fn main() { - dioxus::launch(app); + launch_desktop(app); } fn app() -> Element { @@ -22,7 +24,7 @@ fn app() -> Element { }); rsx! { - document::Stylesheet { href: asset!("/examples/assets/custom_assets.css") } + head::Link { rel: "stylesheet", href: STYLE } h1 { "Dynamic Assets" } img { src: "/logos/logo.png" } } diff --git a/examples/errors.rs b/examples/errors.rs index fe16598e95..4727cb5e03 100644 --- a/examples/errors.rs +++ b/examples/errors.rs @@ -11,11 +11,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(|| { - rsx! { - Router:: {} - } - }); + launch(|| rsx! { Router:: {} }); } /// You can use an ErrorBoundary to catch errors in children and display a warning @@ -38,8 +34,11 @@ fn ParseNumber() -> Element { h1 { "Error handler demo" } button { onclick: move |_| { + // You can return a result from an event handler which lets you easily quit rendering early if something fails let data: i32 = "0.5".parse()?; + println!("parsed {data}"); + Ok(()) }, "Click to throw an error" @@ -59,7 +58,10 @@ fn Show() -> Element { if let Some(error) = error.show() { {error} } else { - pre { color: "red", "{error}" } + pre { + color: "red", + "{error}" + } } } } @@ -86,10 +88,15 @@ fn ParseNumberWithShow() -> Element { border_width: "2px", border_radius: "5px", p { "Failed to parse data" } - Link { to: Route::Home {}, "Go back to the homepage" } + Link { + to: Route::Home {}, + "Go back to the homepage" + } } })?; + println!("parsed {data}"); + Ok(()) }, "Click to throw an error" @@ -132,13 +139,22 @@ fn Home() -> Element { rsx! { ul { li { - Link { to: Route::Simple {}, "Simple errors" } + Link { + to: Route::Simple {}, + "Simple errors" + } } li { - Link { to: Route::Panic {}, "Capture panics" } + Link { + to: Route::Panic {}, + "Capture panics" + } } li { - Link { to: Route::Show {}, "Show errors" } + Link { + to: Route::Show {}, + "Show errors" + } } } } diff --git a/examples/eval.rs b/examples/eval.rs index 68d26c8b09..9cfd15e941 100644 --- a/examples/eval.rs +++ b/examples/eval.rs @@ -7,7 +7,7 @@ use async_std::task::sleep; use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { @@ -19,24 +19,29 @@ fn app() -> Element { // The `eval` is available in the prelude - and simply takes a block of JS. // Dioxus' eval is interesting since it allows sending messages to and from the JS code using the `await dioxus.recv()` // builtin function. This allows you to create a two-way communication channel between Rust and JS. - let mut eval = document::eval( + let mut eval = eval( r#" - return "hi from JS!"; + dioxus.send("Hi from JS!"); + let msg = await dioxus.recv(); + console.log(msg); + return "hi from JS!"; "#, ); - // This will print "Hi from JS!" and "Hi from Rust!". - let res = eval.await; + // Send a message to the JS code. + eval.send("Hi from Rust!".into()).unwrap(); + + // Our line on the JS side will log the message and then return "hello world". + let res = eval.recv().await.unwrap(); - println!("hello from js! {:?}", res); + // This will print "Hi from JS!" and "Hi from Rust!". + println!("{:?}", eval.await); res }); - todo!() - // future.read_unchecked().as_ref().map(|f| match f { - // Some(Ok(v)) => rsx!( p { "{v:?}" } ), - // Some(Err(e)) => rsx!( p { "{v:?}" } ), - // None => rsx!( p { "waiting.." } ), - // }) + match future.value().as_ref() { + Some(v) => rsx!( p { "{v}" } ), + _ => rsx!( p { "waiting.." } ), + } } diff --git a/examples/file_explorer.rs b/examples/file_explorer.rs index c5d5f31756..1ac571d2e8 100644 --- a/examples/file_explorer.rs +++ b/examples/file_explorer.rs @@ -9,7 +9,7 @@ use dioxus::desktop::{Config, WindowBuilder}; use dioxus::prelude::*; fn main() { - dioxus::launch::builder() + LaunchBuilder::desktop() .with_cfg(Config::new().with_window(WindowBuilder::new().with_resizable(true))) .launch(app) } @@ -18,12 +18,12 @@ fn app() -> Element { let mut files = use_signal(Files::new); rsx! { - document::Link { + head::Link { rel: "stylesheet", - href: asset!("/examples/assets/fileexplorer.css") + href: asset!("./examples/assets/fileexplorer.css") } div { - document::Link { href: "https://fonts.googleapis.com/icon?family=Material+Icons", rel: "stylesheet" } + head::Link { href: "https://fonts.googleapis.com/icon?family=Material+Icons", rel: "stylesheet" } header { i { class: "material-icons icon-menu", "menu" } h1 { "Files: " {files.read().current()} } diff --git a/examples/file_upload.rs b/examples/file_upload.rs index ffc1858238..9fb74072bb 100644 --- a/examples/file_upload.rs +++ b/examples/file_upload.rs @@ -8,10 +8,10 @@ use std::sync::Arc; use dioxus::prelude::*; use dioxus::{html::HasFileData, prelude::dioxus_elements::FileEngine}; -const STYLE: Asset = asset!("/examples/assets/file_upload.css"); +const STYLE: &str = asset!("./examples/assets/file_upload.css"); fn main() { - dioxus::launch(app); + launch(app); } struct UploadedFile { @@ -43,7 +43,7 @@ fn app() -> Element { }; rsx! { - document::Stylesheet { href: STYLE } + head::Link { rel: "stylesheet", href: STYLE } h1 { "File Upload Example" } p { "Drop a .txt, .rs, or .js file here to read it" } diff --git a/examples/flat_router.rs b/examples/flat_router.rs index d537ee4c1b..79a9d2a3f2 100644 --- a/examples/flat_router.rs +++ b/examples/flat_router.rs @@ -9,12 +9,12 @@ use dioxus::prelude::*; -const STYLE: Asset = asset!("/examples/assets/flat_router.css"); +const STYLE: &str = asset!("./examples/assets/flat_router.css"); fn main() { - dioxus::launch(|| { + launch(|| { rsx! { - document::Stylesheet { href: STYLE } + head::Link { rel: "stylesheet", href: STYLE } Router:: {} } }) diff --git a/examples/form.rs b/examples/form.rs index ff6154b9d4..91b09ecd34 100644 --- a/examples/form.rs +++ b/examples/form.rs @@ -7,7 +7,7 @@ use dioxus::prelude::*; use std::collections::HashMap; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/future.rs b/examples/future.rs index e32386fe83..f4ddc10468 100644 --- a/examples/future.rs +++ b/examples/future.rs @@ -7,7 +7,7 @@ use async_std::task::sleep; use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/generic_component.rs b/examples/generic_component.rs index 520f257787..6af34e542b 100644 --- a/examples/generic_component.rs +++ b/examples/generic_component.rs @@ -7,7 +7,7 @@ use dioxus::prelude::*; use std::fmt::Display; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/global.rs b/examples/global.rs index 693b912e93..0a08485b02 100644 --- a/examples/global.rs +++ b/examples/global.rs @@ -7,18 +7,18 @@ use dioxus::prelude::*; -const STYLE: Asset = asset!("/examples/assets/counter.css"); +const STYLE: &str = asset!("./examples/assets/counter.css"); static COUNT: GlobalSignal = Signal::global(|| 0); static DOUBLED_COUNT: GlobalMemo = Memo::global(|| COUNT() * 2); fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { rsx! { - document::Stylesheet { href: STYLE } + head::Link { rel: "stylesheet", href: STYLE } Increment {} Decrement {} Reset {} diff --git a/examples/hash_fragment_state.rs b/examples/hash_fragment_state.rs index bc0ec2cc88..ed77c8c46e 100644 --- a/examples/hash_fragment_state.rs +++ b/examples/hash_fragment_state.rs @@ -2,7 +2,7 @@ //! //! You can set up two way data binding between the url hash and signals. //! -//! Run this example on desktop with +//! Run this example on desktop with //! ```sh //! dx serve --example hash_fragment_state --features=ciborium,base64 //! ``` @@ -19,7 +19,7 @@ use dioxus::prelude::*; use serde::{Deserialize, Serialize}; fn main() { - dioxus::launch(|| { + launch(|| { rsx! { Router:: {} } diff --git a/examples/hello_world.rs b/examples/hello_world.rs index 20c9af934a..ee33c22309 100644 --- a/examples/hello_world.rs +++ b/examples/hello_world.rs @@ -12,7 +12,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/hydration.rs b/examples/hydration.rs index cffacc86aa..1e7de2706f 100644 --- a/examples/hydration.rs +++ b/examples/hydration.rs @@ -13,7 +13,7 @@ use dioxus::desktop::Config; use dioxus::prelude::*; fn main() { - dioxus::launch::builder() + LaunchBuilder::desktop() .with_cfg(Config::new().with_prerendered({ // We build the dom a first time, then pre-render it to HTML let pre_rendered_dom = VirtualDom::prebuilt(app); diff --git a/examples/image_generator_openai.rs b/examples/image_generator_openai.rs index 67c743c427..474c46de90 100644 --- a/examples/image_generator_openai.rs +++ b/examples/image_generator_openai.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; use serde_json::{json, Error}; fn main() { - dioxus::launch(app) + launch(app) } fn app() -> Element { @@ -36,7 +36,7 @@ fn app() -> Element { }); rsx! { - document::Stylesheet { href: "https://unpkg.com/bulma@0.9.0/css/bulma.min.css" } + head::Link { rel: "stylesheet", href: "https://unpkg.com/bulma@0.9.0/css/bulma.min.css" } div { class: "container", div { class: "columns", div { class: "column", diff --git a/examples/link.rs b/examples/link.rs index 7d1f4be609..ff29b65626 100644 --- a/examples/link.rs +++ b/examples/link.rs @@ -8,15 +8,15 @@ use dioxus::prelude::*; -const STYLE: Asset = asset!("/examples/assets/links.css"); +const STYLE: &str = asset!("./examples/assets/links.css"); fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { rsx! ( - document::Stylesheet { href: STYLE } + head::Link { rel: "stylesheet", href: STYLE } Router:: {} ) } diff --git a/examples/login_form.rs b/examples/login_form.rs index 617723466c..39e874cc99 100644 --- a/examples/login_form.rs +++ b/examples/login_form.rs @@ -9,7 +9,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/memo_chain.rs b/examples/memo_chain.rs index ab57089397..205fe87693 100644 --- a/examples/memo_chain.rs +++ b/examples/memo_chain.rs @@ -6,7 +6,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/meta.rs b/examples/meta.rs index 9d8e1a4ae0..fae916258f 100644 --- a/examples/meta.rs +++ b/examples/meta.rs @@ -3,7 +3,8 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + tracing_subscriber::fmt::init(); + launch(app); } fn app() -> Element { @@ -11,23 +12,23 @@ fn app() -> Element { // You can use the Meta component to render a meta tag into the head of the page // Meta tags are useful to provide information about the page to search engines and social media sites // This example sets up meta tags for the open graph protocol for social media previews - document::Meta { + Meta { property: "og:title", content: "My Site", } - document::Meta { + Meta { property: "og:type", content: "website", } - document::Meta { + Meta { property: "og:url", content: "https://www.example.com", } - document::Meta { + Meta { property: "og:image", content: "https://example.com/image.jpg", } - document::Meta { + Meta { name: "description", content: "My Site is a site", } diff --git a/examples/mobile_demo/.gitignore b/examples/mobile_demo/.gitignore new file mode 100644 index 0000000000..e1e084c4bb --- /dev/null +++ b/examples/mobile_demo/.gitignore @@ -0,0 +1,10 @@ +# Rust +target/ +**/*.rs.bk + +# cargo-mobile2 +.cargo/ +/gen + +# macOS +.DS_Store diff --git a/examples/mobile_demo/Cargo.lock b/examples/mobile_demo/Cargo.lock new file mode 100644 index 0000000000..7f50292263 --- /dev/null +++ b/examples/mobile_demo/Cargo.lock @@ -0,0 +1,4007 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + +[[package]] +name = "android_log-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85965b6739a430150bdd138e2374a98af0c3ee0d030b3bb7fc3bddff58d0102e" + +[[package]] +name = "android_logger" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ec2333c185d826313162cee39d3fcc6a84ba08114a839bebf53b961e7e75773" +dependencies = [ + "android_log-sys", + "env_logger 0.7.1", + "lazy_static", + "log", +] + +[[package]] +name = "anyhow" +version = "1.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" + +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + +[[package]] +name = "async-lock" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa24f727524730b077666307f2734b4a1a1c57acb79193127dcc8914d5242dd7" +dependencies = [ + "event-listener", +] + +[[package]] +name = "async-task" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae" + +[[package]] +name = "async-trait" +version = "0.1.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "atk" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4af014b17dd80e8af9fa689b2d4a211ddba6eb583c1622f35d0cb543f6b17e4" +dependencies = [ + "atk-sys", + "glib", + "libc", +] + +[[package]] +name = "atk-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "251e0b7d90e33e0ba930891a505a9a35ece37b2dd37a14f3ffc306c13b980009" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "atomic-waker" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +dependencies = [ + "serde", +] + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "blocking" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77231a1c8f801696fc0123ec6150ce92cffb8e164a02afb9c8ddee0e9b65ad65" +dependencies = [ + "async-channel", + "async-lock", + "async-task", + "atomic-waker", + "fastrand", + "futures-lite", + "log", +] + +[[package]] +name = "bumpalo" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" + +[[package]] +name = "bytemuck" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cairo-rs" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" +dependencies = [ + "bitflags 2.4.2", + "cairo-sys-rs", + "glib", + "libc", + "once_cell", + "thiserror", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfb" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" +dependencies = [ + "byteorder", + "fnv", + "uuid", +] + +[[package]] +name = "cfg-expr" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b40ccee03b5175c18cde8f37e7d2a33bcef6f8ec8f7cc0d81090d1bb380949c9" +dependencies = [ + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "cocoa" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" +dependencies = [ + "bitflags 1.3.2", + "block", + "cocoa-foundation", + "core-foundation", + "core-graphics", + "foreign-types 0.5.0", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "931d3837c286f56e3c58423ce4eba12d08db2374461a785c86f672b08b5650d6" +dependencies = [ + "bitflags 1.3.2", + "block", + "core-foundation", + "core-graphics-types", + "foreign-types 0.3.2", + "libc", + "objc", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "combine" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "concurrent-queue" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "const_format" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a214c7af3d04997541b18d432afaff4c455e79e2029079647e72fc2bd27673" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "constcat" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd7e35aee659887cbfb97aaf227ac12cad1a9d7c71e55ff3376839ed4e282d08" + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + +[[package]] +name = "core-graphics" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "970a29baf4110c26fedbc7f82107d42c23f7e88e404c4577ed73fe99ff85a212" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types 0.5.0", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bb142d41022986c1d8ff29103a1411c8a3dfad3552f87a4f8dc50d61d4f4e33" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[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 = "cssparser" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa 0.4.8", + "matches", + "phf 0.8.0", + "proc-macro2", + "quote", + "smallvec", + "syn 1.0.109", +] + +[[package]] +name = "cssparser-macros" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" +dependencies = [ + "quote", + "syn 2.0.52", +] + +[[package]] +name = "darling" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "darling_macro" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown 0.14.0", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case 0.4.0", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dioxus" +version = "0.5.0-alpha.0" +dependencies = [ + "dioxus-config-macro", + "dioxus-core", + "dioxus-core-macro", + "dioxus-desktop", + "dioxus-fullstack", + "dioxus-hooks", + "dioxus-hot-reload", + "dioxus-html", + "dioxus-mobile", + "dioxus-signals", +] + +[[package]] +name = "dioxus-cli-config" +version = "0.5.0-alpha.0" +dependencies = [ + "once_cell", + "serde", + "serde_json", + "tracing", +] + +[[package]] +name = "dioxus-config-macro" +version = "0.5.0-alpha.0" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "dioxus-core" +version = "0.5.0-alpha.0" +dependencies = [ + "futures-channel", + "futures-util", + "longest-increasing-subsequence", + "rustc-hash", + "serde", + "slab", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "dioxus-core-macro" +version = "0.5.0-alpha.0" +dependencies = [ + "constcat", + "convert_case 0.6.0", + "dioxus-rsx", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "dioxus-debug-cell" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ea539174bb236e0e7dc9c12b19b88eae3cb574dedbd0252a2d43ea7e6de13e2" + +[[package]] +name = "dioxus-desktop" +version = "0.5.0-alpha.0" +dependencies = [ + "async-trait", + "core-foundation", + "dioxus-cli-config", + "dioxus-core", + "dioxus-hooks", + "dioxus-hot-reload", + "dioxus-html", + "dioxus-interpreter-js", + "dunce", + "futures-channel", + "futures-util", + "generational-box", + "global-hotkey", + "infer", + "muda", + "objc", + "objc_id", + "rfd", + "rustc-hash", + "serde", + "serde_json", + "slab", + "tao", + "thiserror", + "tokio", + "tracing", + "urlencoding", + "webbrowser", + "wry 0.37.0", +] + +[[package]] +name = "dioxus-fullstack" +version = "0.5.0-alpha.0" +dependencies = [ + "async-trait", + "base64", + "bytes", + "ciborium", + "dioxus-hot-reload", + "dioxus-lib", + "dioxus-mobile", + "dioxus_server_macro", + "futures-util", + "once_cell", + "serde", + "serde_json", + "server_fn", + "tracing", +] + +[[package]] +name = "dioxus-hooks" +version = "0.5.0-alpha.0" +dependencies = [ + "dioxus-core", + "dioxus-debug-cell", + "dioxus-signals", + "futures-channel", + "futures-util", + "generational-box", + "slab", + "thiserror", + "tracing", +] + +[[package]] +name = "dioxus-hot-reload" +version = "0.5.0-alpha.0" +dependencies = [ + "dioxus-core", + "dioxus-html", + "dioxus-rsx", + "interprocess", + "serde", + "serde_json", +] + +[[package]] +name = "dioxus-html" +version = "0.5.0-alpha.0" +dependencies = [ + "async-trait", + "dioxus-core", + "dioxus-html-internal-macro", + "enumset", + "euclid", + "futures-channel", + "generational-box", + "keyboard-types", + "serde", + "serde-value", + "serde_json", + "serde_repr", + "tokio", + "web-sys", +] + +[[package]] +name = "dioxus-html-internal-macro" +version = "0.5.0-alpha.0" +dependencies = [ + "convert_case 0.6.0", + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "dioxus-interpreter-js" +version = "0.5.0-alpha.0" +dependencies = [ + "dioxus-core", + "dioxus-html", + "md5", + "sledgehammer_bindgen", + "sledgehammer_utils", +] + +[[package]] +name = "dioxus-lib" +version = "0.5.0-alpha.0" +dependencies = [ + "dioxus-core", + "dioxus-core-macro", + "dioxus-hooks", + "dioxus-html", + "dioxus-rsx", + "dioxus-signals", +] + +[[package]] +name = "dioxus-mobile" +version = "0.5.0-alpha.0" +dependencies = [ + "dioxus-desktop", +] + +[[package]] +name = "dioxus-rsx" +version = "0.5.0-alpha.0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", + "tracing", +] + +[[package]] +name = "dioxus-signals" +version = "0.5.0-alpha.0" +dependencies = [ + "dioxus-core", + "futures-channel", + "futures-util", + "generational-box", + "once_cell", + "parking_lot", + "rustc-hash", + "tracing", +] + +[[package]] +name = "dioxus_server_macro" +version = "0.5.0-alpha.0" +dependencies = [ + "convert_case 0.6.0", + "proc-macro2", + "quote", + "server_fn_macro", + "syn 2.0.52", +] + +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + +[[package]] +name = "dlopen2" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1297103d2bbaea85724fcee6294c2d50b1081f9ad47d0f6f6f61eda65315a6" +dependencies = [ + "dlopen2_derive", + "libc", + "once_cell", + "winapi", +] + +[[package]] +name = "dlopen2_derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "dtoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" + +[[package]] +name = "dtoa-short" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbaceec3c6e4211c79e7b1800fb9680527106beb2f9c51904a3210c03a448c74" +dependencies = [ + "dtoa", +] + +[[package]] +name = "dunce" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" + +[[package]] +name = "enumset" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e875f1719c16de097dee81ed675e2d9bb63096823ed3f0ca827b7dea3028bbbb" +dependencies = [ + "enumset_derive", +] + +[[package]] +name = "enumset_derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08b6c6ab82d70f08844964ba10c7babb716de2ecaeab9be5717918a5177d3af" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "env_logger" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "euclid" +version = "0.22.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f253bc5c813ca05792837a0ff4b3a580336b224512d48f7eda1d7dd9210787" +dependencies = [ + "num-traits", + "serde", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "fdeflate" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "field-offset" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" +dependencies = [ + "memoffset", + "rustc_version", +] + +[[package]] +name = "flate2" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "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.52", +] + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" +dependencies = [ + "mac", + "new_debug_unreachable", +] + +[[package]] +name = "futures" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-executor" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" + +[[package]] +name = "futures-lite" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + +[[package]] +name = "futures-macro" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" + +[[package]] +name = "futures-util" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "gdk" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5ba081bdef3b75ebcdbfc953699ed2d7417d6bd853347a42a37d76406a33646" +dependencies = [ + "cairo-rs", + "gdk-pixbuf", + "gdk-sys", + "gio", + "glib", + "libc", + "pango", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" +dependencies = [ + "gdk-pixbuf-sys", + "gio", + "glib", + "libc", + "once_cell", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gdk-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31ff856cb3386dae1703a920f803abafcc580e9b5f711ca62ed1620c25b51ff2" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkwayland-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a90fbf5c033c65d93792192a49a8efb5bb1e640c419682a58bb96f5ae77f3d4a" +dependencies = [ + "gdk-sys", + "glib-sys", + "gobject-sys", + "libc", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkx11" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2ea8a4909d530f79921290389cbd7c34cb9d623bfe970eaae65ca5f9cd9cce" +dependencies = [ + "gdk", + "gdkx11-sys", + "gio", + "glib", + "libc", + "x11", +] + +[[package]] +name = "gdkx11-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fee8f00f4ee46cad2939b8990f5c70c94ff882c3028f3cc5abf950fa4ab53043" +dependencies = [ + "gdk-sys", + "glib-sys", + "libc", + "system-deps", + "x11", +] + +[[package]] +name = "generational-box" +version = "0.5.0-alpha.0" +dependencies = [ + "parking_lot", +] + +[[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 = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "gimli" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" + +[[package]] +name = "gio" +version = "0.18.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys", + "glib", + "libc", + "once_cell", + "pin-project-lite", + "smallvec", + "thiserror", +] + +[[package]] +name = "gio-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", + "winapi", +] + +[[package]] +name = "glib" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" +dependencies = [ + "bitflags 2.4.2", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "memchr", + "once_cell", + "smallvec", + "thiserror", +] + +[[package]] +name = "glib-macros" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" +dependencies = [ + "heck", + "proc-macro-crate 2.0.2", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "glib-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" +dependencies = [ + "libc", + "system-deps", +] + +[[package]] +name = "global-hotkey" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0d37e95d3937251ee2019709389bb793c1237f16d45fc0fe7b2464b5f97c68" +dependencies = [ + "crossbeam-channel", + "keyboard-types", + "once_cell", + "thiserror", + "windows-sys 0.52.0", + "x11-dl", +] + +[[package]] +name = "gloo-net" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43aaa242d1239a8822c15c645f02166398da4f8b5c4bae795c1f5b44e9eee173" +dependencies = [ + "futures-channel", + "futures-core", + "futures-sink", + "gloo-utils", + "http 0.2.9", + "js-sys", + "pin-project", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-utils" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gobject-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gtk" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93c4f5e0e20b60e10631a5f06da7fe3dda744b05ad0ea71fee2f47adf865890c" +dependencies = [ + "atk", + "cairo-rs", + "field-offset", + "futures-channel", + "gdk", + "gdk-pixbuf", + "gio", + "glib", + "gtk-sys", + "gtk3-macros", + "libc", + "pango", + "pkg-config", +] + +[[package]] +name = "gtk-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771437bf1de2c1c0b496c11505bdf748e26066bbe942dfc8f614c9460f6d7722" +dependencies = [ + "atk-sys", + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "system-deps", +] + +[[package]] +name = "gtk3-macros" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6063efb63db582968fb7df72e1ae68aa6360dcfb0a75143f34fc7d616bad75e" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "half" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5eceaaeec696539ddaf7b333340f1af35a5aa87ae3e4f3ead0532f72affab2e" +dependencies = [ + "cfg-if", + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" + +[[package]] +name = "home" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "html5ever" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" +dependencies = [ + "log", + "mac", + "markup5ever", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "http" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes", + "fnv", + "itoa 1.0.9", +] + +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa 1.0.9", +] + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "image" +version = "0.24.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527909aa81e20ac3a44803521443a765550f09b5130c2c2fa1ea59c2f8f50a3a" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "num-rational", + "num-traits", +] + +[[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", +] + +[[package]] +name = "indexmap" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +dependencies = [ + "equivalent", + "hashbrown 0.14.0", +] + +[[package]] +name = "infer" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6c16b11a665b26aeeb9b1d7f954cdeb034be38dd00adab4f2ae921a8fee804" +dependencies = [ + "cfb", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "interprocess" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81f2533f3be42fffe3b5e63b71aeca416c1c3bc33e4e27be018521e76b1f38fb" +dependencies = [ + "blocking", + "cfg-if", + "futures-core", + "futures-io", + "intmap", + "libc", + "once_cell", + "rustc_version", + "spinning", + "thiserror", + "to_method", + "winapi", +] + +[[package]] +name = "intmap" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae52f28f45ac2bc96edb7714de995cffc174a395fb0abf5bff453587c980d7b9" + +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "javascriptcore-rs" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca5671e9ffce8ffba57afc24070e906da7fc4b1ba66f2cabebf61bf2ea257fcc" +dependencies = [ + "bitflags 1.3.2", + "glib", + "javascriptcore-rs-sys", +] + +[[package]] +name = "javascriptcore-rs-sys" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1be78d14ffa4b75b66df31840478fef72b51f8c2465d4ca7c194da9f7a5124" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "jni" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec" +dependencies = [ + "cesu8", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", +] + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror", + "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 = "js-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "keyboard-types" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" +dependencies = [ + "bitflags 2.4.2", + "serde", + "unicode-segmentation", +] + +[[package]] +name = "kuchikiki" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e4755b7b995046f510a7520c42b2fed58b77bd94d5a87a8eb43d2fd126da8" +dependencies = [ + "cssparser", + "html5ever", + "indexmap 1.9.3", + "matches", + "selectors", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "libxdo" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00333b8756a3d28e78def82067a377de7fa61b24909000aeaa2b446a948d14db" +dependencies = [ + "libxdo-sys", +] + +[[package]] +name = "libxdo-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db23b9e7e2b7831bbd8aac0bbeeeb7b68cbebc162b227e7052e8e55829a09212" +dependencies = [ + "libc", + "x11", +] + +[[package]] +name = "lock_api" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" + +[[package]] +name = "longest-increasing-subsequence" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3bd0dd2cd90571056fdb71f6275fada10131182f84899f4b2a916e565d81d86" + +[[package]] +name = "lru" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" +dependencies = [ + "hashbrown 0.14.0", +] + +[[package]] +name = "mac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "markup5ever" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" +dependencies = [ + "log", + "phf 0.10.1", + "phf_codegen 0.10.0", + "string_cache", + "string_cache_codegen", + "tendril", +] + +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + +[[package]] +name = "md5" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", + "simd-adler32", +] + +[[package]] +name = "mobile-demo" +version = "0.1.0" +dependencies = [ + "android_logger", + "anyhow", + "core-foundation", + "dioxus", + "env_logger 0.9.3", + "jni 0.19.0", + "log", + "paste", + "wry 0.35.2", +] + +[[package]] +name = "muda" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c47e7625990fc1af2226ea4f34fb2412b03c12639fcb91868581eb3a6893453" +dependencies = [ + "cocoa", + "crossbeam-channel", + "gtk", + "keyboard-types", + "libxdo", + "objc", + "once_cell", + "png", + "thiserror", + "windows-sys 0.52.0", +] + +[[package]] +name = "ndk" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" +dependencies = [ + "bitflags 1.3.2", + "jni-sys", + "ndk-sys", + "num_enum", + "raw-window-handle 0.5.2", + "thiserror", +] + +[[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.4.1+23.1.7779620" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cf2aae958bd232cac5069850591667ad422d263686d75b52a065f9badeee5a3" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi 0.3.2", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", + "objc_exception", +] + +[[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_exception" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" +dependencies = [ + "cc", +] + +[[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.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "ordered-float" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7940cf2ca942593318d07fcf2596cdca60a85c9e7fab408a5e21a4f9dcd40d87" +dependencies = [ + "num-traits", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "pango" +version = "0.18.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" +dependencies = [ + "gio", + "glib", + "libc", + "once_cell", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "parking" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.48.1", +] + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "percent-encoding" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" + +[[package]] +name = "phf" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" +dependencies = [ + "phf_macros", + "phf_shared 0.8.0", + "proc-macro-hack", +] + +[[package]] +name = "phf" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" +dependencies = [ + "phf_shared 0.10.0", +] + +[[package]] +name = "phf_codegen" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" +dependencies = [ + "phf_generator 0.8.0", + "phf_shared 0.8.0", +] + +[[package]] +name = "phf_codegen" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", +] + +[[package]] +name = "phf_generator" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" +dependencies = [ + "phf_shared 0.8.0", + "rand 0.7.3", +] + +[[package]] +name = "phf_generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +dependencies = [ + "phf_shared 0.10.0", + "rand 0.8.5", +] + +[[package]] +name = "phf_macros" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" +dependencies = [ + "phf_generator 0.8.0", + "phf_shared 0.8.0", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "phf_shared" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" +dependencies = [ + "siphasher", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + +[[package]] +name = "png" +version = "0.17.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59871cc5b6cce7eaccca5a802b4173377a1c2ba90654246789a8fa2334426d11" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "prettyplease" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" +dependencies = [ + "proc-macro2", + "syn 2.0.52", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.14", +] + +[[package]] +name = "proc-macro-crate" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" +dependencies = [ + "toml_datetime", + "toml_edit 0.20.2", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + +[[package]] +name = "proc-macro2" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", + "rand_pcg", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[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 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.10", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "raw-window-handle" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" + +[[package]] +name = "raw-window-handle" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42a9830a0e1b9fb145ebb365b8bc4ccd75f290f98c0247deafbbe2c75cefb544" + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "regex" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7b6d6190b7594385f61bd3911cd1be99dfddcfc365a4160cc2ab5bff4aed294" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" + +[[package]] +name = "rfd" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c9e7b57df6e8472152674607f6cc68aa14a748a3157a857a94f516e11aeacc2" +dependencies = [ + "block", + "dispatch", + "glib-sys", + "gobject-sys", + "gtk-sys", + "js-sys", + "log", + "objc", + "objc-foundation", + "objc_id", + "raw-window-handle 0.5.2", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[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 = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "selectors" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe" +dependencies = [ + "bitflags 1.3.2", + "cssparser", + "derive_more", + "fxhash", + "log", + "matches", + "phf 0.8.0", + "phf_codegen 0.8.0", + "precomputed-hash", + "servo_arc", + "smallvec", + "thin-slice", +] + +[[package]] +name = "semver" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" + +[[package]] +name = "send_wrapper" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" +dependencies = [ + "futures-core", +] + +[[package]] +name = "serde" +version = "1.0.180" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea67f183f058fe88a4e3ec6e2788e003840893b91bac4559cabedd00863b3ed" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float", + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.180" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24e744d7782b686ab3b73267ef05697159cc0e5abbed3f47f9933165e5219036" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "serde_json" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" +dependencies = [ + "itoa 1.0.9", + "ryu", + "serde", +] + +[[package]] +name = "serde_qs" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0431a35568651e363364210c91983c1da5eb29404d9f0928b67d4ebcfa7d330c" +dependencies = [ + "percent-encoding", + "serde", + "thiserror", +] + +[[package]] +name = "serde_repr" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "serde_spanned" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" +dependencies = [ + "serde", +] + +[[package]] +name = "server_fn" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2955da1dc5fcd970c182ebf1089af6c5f19051e1f286a21f7b96490a49b7a531" +dependencies = [ + "bytes", + "const_format", + "dashmap", + "futures", + "gloo-net", + "http 1.1.0", + "js-sys", + "once_cell", + "send_wrapper", + "serde", + "serde_json", + "serde_qs", + "server_fn_macro_default", + "thiserror", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "xxhash-rust", +] + +[[package]] +name = "server_fn_macro" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adfdd051ef905fdb3da20942b0c52d536158d7489a724e14cc2fd47323e7ca91" +dependencies = [ + "const_format", + "convert_case 0.6.0", + "proc-macro2", + "quote", + "syn 2.0.52", + "xxhash-rust", +] + +[[package]] +name = "server_fn_macro_default" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "060af1def72353a779fcc184c53e1965d3055a38b9e827f2259b2bff2d9c371e" +dependencies = [ + "server_fn_macro", + "syn 2.0.52", +] + +[[package]] +name = "servo_arc" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432" +dependencies = [ + "nodrop", + "stable_deref_trait", +] + +[[package]] +name = "sha2" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "siphasher" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" + +[[package]] +name = "slab" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +dependencies = [ + "autocfg", +] + +[[package]] +name = "sledgehammer_bindgen" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcfaf791ff02f48f3518ce825d32cf419c13a43c1d8b1232f74ac89f339c46d2" +dependencies = [ + "sledgehammer_bindgen_macro", +] + +[[package]] +name = "sledgehammer_bindgen_macro" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdd941cc539bd3dc694edaf9d0c4e1221d02baa67c6b45ec04fad1024d9e8139" +dependencies = [ + "quote", + "syn 2.0.52", +] + +[[package]] +name = "sledgehammer_utils" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f20798defa0e9d4eff9ca451c7f84774c7378a9c3b5a40112cfa2b3eadb97ae2" +dependencies = [ + "lru", + "once_cell", + "rustc-hash", +] + +[[package]] +name = "smallvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" + +[[package]] +name = "soup3" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "471f924a40f31251afc77450e781cb26d55c0b650842efafc9c6cbd2f7cc4f9f" +dependencies = [ + "futures-channel", + "gio", + "glib", + "libc", + "soup3-sys", +] + +[[package]] +name = "soup3-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebe8950a680a12f24f15ebe1bf70db7af98ad242d9db43596ad3108aab86c27" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "spinning" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d4f0e86297cad2658d92a707320d87bf4e6ae1050287f51d19b67ef3f153a7b" +dependencies = [ + "lock_api", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "string_cache" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" +dependencies = [ + "new_debug_unreachable", + "once_cell", + "parking_lot", + "phf_shared 0.10.0", + "precomputed-hash", + "serde", +] + +[[package]] +name = "string_cache_codegen" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", + "proc-macro2", + "quote", +] + +[[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.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "system-deps" +version = "6.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30c2de8a4d8f4b823d634affc9cd2a74ec98c53a756f317e529a48046cbf71f3" +dependencies = [ + "cfg-expr", + "heck", + "pkg-config", + "toml", + "version-compare", +] + +[[package]] +name = "tao" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccba570365293ca309d60f30fdac2c5271b732dc762e6154e59c85d2c762a0a1" +dependencies = [ + "bitflags 1.3.2", + "cocoa", + "core-foundation", + "core-graphics", + "crossbeam-channel", + "dispatch", + "dlopen2", + "gdkwayland-sys", + "gdkx11-sys", + "gtk", + "image", + "instant", + "jni 0.21.1", + "lazy_static", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "objc", + "once_cell", + "parking_lot", + "png", + "raw-window-handle 0.5.2", + "raw-window-handle 0.6.0", + "scopeguard", + "tao-macros", + "unicode-segmentation", + "url", + "windows", + "windows-implement", + "windows-version", + "x11-dl", +] + +[[package]] +name = "tao-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b27a4bcc5eb524658234589bdffc7e7bfb996dbae6ce9393bfd39cb4159b445" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "target-lexicon" +version = "0.12.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a" + +[[package]] +name = "tendril" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" +dependencies = [ + "futf", + "mac", + "utf-8", +] + +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thin-slice" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" + +[[package]] +name = "thiserror" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "to_method" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c4ceeeca15c8384bbc3e011dbd8fccb7f068a440b752b7d9b32ceb0ca0e2e8" + +[[package]] +name = "tokio" +version = "1.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" +dependencies = [ + "autocfg", + "backtrace", + "bytes", + "num_cpus", + "pin-project-lite", + "tokio-macros", +] + +[[package]] +name = "tokio-macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "toml" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.19.14", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" +dependencies = [ + "indexmap 2.0.0", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "toml_edit" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +dependencies = [ + "indexmap 2.0.0", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "nu-ansi-term", + "sharded-slab", + "smallvec", + "thread_local", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unicode-bidi" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + +[[package]] +name = "unicode-ident" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "url" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[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 = "uuid" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version-compare" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "waker-fn" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" + +[[package]] +name = "walkdir" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[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.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.52", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" + +[[package]] +name = "wasm-streams" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webbrowser" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd222aa310eb7532e3fd427a5d7db7e44bc0b0cf1c1e21139c345325511a85b6" +dependencies = [ + "core-foundation", + "home", + "jni 0.21.1", + "log", + "ndk-context", + "objc", + "raw-window-handle 0.5.2", + "url", + "web-sys", +] + +[[package]] +name = "webkit2gtk" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76b1bc1e54c581da1e9f179d0b38512ba358fb1af2d634a1affe42e37172361a" +dependencies = [ + "bitflags 1.3.2", + "cairo-rs", + "gdk", + "gdk-sys", + "gio", + "gio-sys", + "glib", + "glib-sys", + "gobject-sys", + "gtk", + "gtk-sys", + "javascriptcore-rs", + "libc", + "once_cell", + "soup3", + "webkit2gtk-sys", +] + +[[package]] +name = "webkit2gtk-sys" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62daa38afc514d1f8f12b8693d30d5993ff77ced33ce30cd04deebc267a6d57c" +dependencies = [ + "bitflags 1.3.2", + "cairo-sys-rs", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "gtk-sys", + "javascriptcore-rs-sys", + "libc", + "pkg-config", + "soup3-sys", + "system-deps", +] + +[[package]] +name = "webview2-com" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0ae9c7e420783826cf769d2c06ac9ba462f450eca5893bb8c6c6529a4e5dd33" +dependencies = [ + "webview2-com-macros", + "webview2-com-sys", + "windows", + "windows-core", + "windows-implement", + "windows-interface", +] + +[[package]] +name = "webview2-com-macros" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac1345798ecd8122468840bcdf1b95e5dc6d2206c5e4b0eafa078d061f59c9bc" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "webview2-com-sys" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6ad85fceee6c42fa3d61239eba5a11401bf38407a849ed5ea1b407df08cca72" +dependencies = [ + "thiserror", + "windows", + "windows-core", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[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.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core", + "windows-implement", + "windows-interface", + "windows-targets 0.52.4", +] + +[[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.4", +] + +[[package]] +name = "windows-implement" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12168c33176773b86799be25e2a2ba07c7aab9968b37541f1094dbd7a60c8946" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "windows-interface" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d8dc32e0095a7eeccebd0e3f09e9509365ecb3fc6ac4d6f5f14a3f6392942d1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[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.1", +] + +[[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.4", +] + +[[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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + +[[package]] +name = "windows-targets" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +dependencies = [ + "windows_aarch64_gnullvm 0.52.4", + "windows_aarch64_msvc 0.52.4", + "windows_i686_gnu 0.52.4", + "windows_i686_msvc 0.52.4", + "windows_x86_64_gnu 0.52.4", + "windows_x86_64_gnullvm 0.52.4", + "windows_x86_64_msvc 0.52.4", +] + +[[package]] +name = "windows-version" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75aa004c988e080ad34aff5739c39d0312f4684699d6d71fc8a198d057b8b9b4" +dependencies = [ + "windows-targets 0.52.4", +] + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" + +[[package]] +name = "winnow" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bd122eb777186e60c3fdf765a58ac76e41c582f1f535fbf3314434c6b58f3f7" +dependencies = [ + "memchr", +] + +[[package]] +name = "wry" +version = "0.35.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3016c47c9b6f7029a9da7cd48af8352327226bba0e955f3c92e2966651365a9" +dependencies = [ + "base64", + "block", + "cfg_aliases", + "cocoa", + "core-graphics", + "crossbeam-channel", + "dunce", + "gdkx11", + "gtk", + "html5ever", + "http 0.2.9", + "javascriptcore-rs", + "jni 0.21.1", + "kuchikiki", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "objc", + "objc_id", + "once_cell", + "raw-window-handle 0.5.2", + "serde", + "serde_json", + "sha2", + "soup3", + "tao-macros", + "thiserror", + "url", + "webkit2gtk", + "webkit2gtk-sys", + "webview2-com", + "windows", + "windows-implement", + "windows-version", + "x11-dl", +] + +[[package]] +name = "wry" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b717040ba9771fd88eb428c6ea6b555f8e734ff8534f02c13e8f10d97f5935e" +dependencies = [ + "base64", + "block", + "cfg_aliases", + "cocoa", + "core-graphics", + "crossbeam-channel", + "dunce", + "gdkx11", + "gtk", + "html5ever", + "http 0.2.9", + "javascriptcore-rs", + "jni 0.21.1", + "kuchikiki", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "objc", + "objc_id", + "once_cell", + "percent-encoding", + "raw-window-handle 0.6.0", + "serde", + "serde_json", + "sha2", + "soup3", + "tao-macros", + "thiserror", + "webkit2gtk", + "webkit2gtk-sys", + "webview2-com", + "windows", + "windows-implement", + "windows-version", + "x11-dl", +] + +[[package]] +name = "x11" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "xxhash-rust" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927da81e25be1e1a2901d59b81b37dd2efd1fc9c9345a55007f09bf5a2d3ee03" + +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] diff --git a/examples/mobile_demo/Cargo.toml b/examples/mobile_demo/Cargo.toml new file mode 100644 index 0000000000..4d4155750b --- /dev/null +++ b/examples/mobile_demo/Cargo.toml @@ -0,0 +1,51 @@ +[package] +name = "mobile-demo" +version = "0.1.0" +authors = ["Jonathan Kelley "] +edition = "2021" + +[lib] +crate-type = ["staticlib", "cdylib", "rlib"] + +[[bin]] +name = "mobile-demo-desktop" +path = "gen/bin/desktop.rs" + +[package.metadata.cargo-android] +app-activity-name = "com.example.mobile_demo.MainActivity" +app-dependencies = [ + "androidx.webkit:webkit:1.6.1", + "androidx.appcompat:appcompat:1.6.1", + "com.google.android.material:material:1.8.0", +] +project-dependencies = ["org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21"] +app-plugins = ["org.jetbrains.kotlin.android"] +app-permissions = ["android.permission.INTERNET"] +app-theme-parent = "Theme.MaterialComponents.DayNight.DarkActionBar" +vulkan-validation = false + +[package.metadata.cargo-android.env-vars] +WRY_ANDROID_PACKAGE = "com.example.mobile_demo" +WRY_ANDROID_LIBRARY = "mobile_demo" +WRY_ANDROID_KOTLIN_FILES_OUT_DIR = "/app/src/main/kotlin/com/example/mobile_demo" + +[package.metadata.cargo-apple.ios] +frameworks = ["WebKit"] + +[dependencies] +anyhow = "1.0.56" +log = "0.4.11" +wry = "0.35.0" +dioxus = { path = "../../packages/dioxus", features = ["mobile"]} + + +[target.'cfg(target_os = "android")'.dependencies] +android_logger = "0.9.0" +jni = "0.19.0" +paste = "1.0" + +[target.'cfg(not(target_os = "android"))'.dependencies] +env_logger = "0.9.0" + +[target.'cfg(target_os = "ios")'.dependencies] +core-foundation = "0.9.3" diff --git a/examples/mobile_demo/README.md b/examples/mobile_demo/README.md new file mode 100644 index 0000000000..4d5ea46ed0 --- /dev/null +++ b/examples/mobile_demo/README.md @@ -0,0 +1,11 @@ +# Dioxus Mobile demo + +## How this project was generated + +Right now, Dioxus supports mobile targets including iOS and Android. However, our tooling is not mature enough to include the build commands directly. + +This project was generated using [cargo-mobile2](https://github.com/tauri-apps/cargo-mobile2). We have yet to integrate this generation into the Dioxus-CLI. The open issue for this is [#1157](https://github.com/DioxusLabs/dioxus/issues/1157). + +## Running this project + +Because the tooling and ecosystem is still young, Dioxus mobile can be difficult to setup and run. We have [detailed guides](https://dioxuslabs.com/learn/0.5/getting_started) for creating, building, and running on both iOS and Android. diff --git a/examples/mobile_demo/mobile.toml b/examples/mobile_demo/mobile.toml new file mode 100644 index 0000000000..3b87772508 --- /dev/null +++ b/examples/mobile_demo/mobile.toml @@ -0,0 +1,8 @@ +[app] +name = "mobile-demo" +stylized-name = "Mobile Demo" +domain = "example.com" +template-pack = "wry" + +[apple] +development-team = "34U4FG9TJ8" diff --git a/examples/mobile_demo/src/index.html b/examples/mobile_demo/src/index.html new file mode 100644 index 0000000000..b0e6290e12 --- /dev/null +++ b/examples/mobile_demo/src/index.html @@ -0,0 +1,12 @@ + + + + Dioxus app + + + + +
      + + + diff --git a/examples/mobile_demo/src/lib.rs b/examples/mobile_demo/src/lib.rs new file mode 100644 index 0000000000..6fc142d9a3 --- /dev/null +++ b/examples/mobile_demo/src/lib.rs @@ -0,0 +1,90 @@ +use anyhow::Result; +use dioxus::mobile::Config; +use dioxus::prelude::*; + +#[cfg(target_os = "android")] +use dioxus::mobile::wry::android_binding; + +#[cfg(target_os = "android")] +fn init_logging() { + android_logger::init_once( + android_logger::Config::default() + .with_min_level(log::Level::Trace) + .with_tag("mobile-demo"), + ); +} + +#[cfg(not(target_os = "android"))] +fn init_logging() { + env_logger::init(); +} + +#[cfg(any(target_os = "android", target_os = "ios"))] +fn stop_unwind T, T>(f: F) -> T { + match std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)) { + Ok(t) => t, + Err(err) => { + eprintln!("attempt to unwind out of `rust` with err: {:?}", err); + std::process::abort() + } + } +} + +#[cfg(any(target_os = "android", target_os = "ios"))] +fn _start_app() { + stop_unwind(|| main().unwrap()); +} + +#[no_mangle] +#[inline(never)] +#[cfg(any(target_os = "android", target_os = "ios"))] +pub extern "C" fn start_app() { + #[cfg(target_os = "android")] + android_binding!(com_example, mobile_demo, _start_app); + #[cfg(target_os = "ios")] + _start_app() +} + +pub fn main() -> Result<()> { + init_logging(); + + // Right now we're going through dioxus-desktop but we'd like to go through dioxus-mobile + // That will seed the index.html with some fixes that prevent the page from scrolling/zooming etc + LaunchBuilder::mobile() + .with_cfg( + // Note that we have to disable the viewport goofiness of the browser. + // Dioxus_mobile should do this for us + Config::default().with_custom_index(include_str!("index.html").to_string()), + ) + .launch(app); + + Ok(()) +} + +fn app() -> Element { + let mut items = use_signal(|| vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); + + log::debug!("Hello from the app"); + + rsx! { + div { + h1 { "Hello, Mobile" } + div { + margin_left: "auto", + margin_right: "auto", + width: "200px", + padding: "10px", + border: "1px solid black", + button { + onclick: move |_| { + items.push(items.len()); + }, + "Add item" + } + for item in items.iter() { + div { "- {item}" } + } + } + } + } +} diff --git a/examples/multiwindow.rs b/examples/multiwindow.rs index d89a78ee6d..6858f65896 100644 --- a/examples/multiwindow.rs +++ b/examples/multiwindow.rs @@ -7,7 +7,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch_desktop(app); } fn app() -> Element { diff --git a/examples/nested_listeners.rs b/examples/nested_listeners.rs index c2316aed37..33cf3d4043 100644 --- a/examples/nested_listeners.rs +++ b/examples/nested_listeners.rs @@ -7,7 +7,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/openid_connect_demo/.cargo/config.toml b/examples/openid_connect_demo/.cargo/config.toml new file mode 100644 index 0000000000..fcb494816a --- /dev/null +++ b/examples/openid_connect_demo/.cargo/config.toml @@ -0,0 +1,5 @@ +[env] +DIOXUS_FRONT_ISSUER_URL = "TODO" +DIOXUS_FRONT_CLIENT_ID = "TODO" +DIOXUS_FRONT_CLIENT_SECRET = "TODO" +DIOXUS_FRONT_URL = "http://localhost:8080" diff --git a/examples/openid_connect_demo/.gitignore b/examples/openid_connect_demo/.gitignore new file mode 100644 index 0000000000..919c8ee550 --- /dev/null +++ b/examples/openid_connect_demo/.gitignore @@ -0,0 +1,3 @@ +/target +/dist +.env diff --git a/examples/openid_connect_demo/Cargo.lock b/examples/openid_connect_demo/Cargo.lock new file mode 100644 index 0000000000..b7d513c11e --- /dev/null +++ b/examples/openid_connect_demo/Cargo.lock @@ -0,0 +1,6372 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + +[[package]] +name = "anymap" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33954243bd79057c2de7338850b85983a44588021f8a5fee574a8888c6de4344" + +[[package]] +name = "anymap2" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c" + +[[package]] +name = "ashpd" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd884d7c72877a94102c3715f3b1cd09ff4fac28221add3e57cfbe25c236d093" +dependencies = [ + "async-fs", + "async-net", + "enumflags2", + "futures-channel", + "futures-util", + "rand 0.8.5", + "serde", + "serde_repr", + "url", + "zbus", +] + +[[package]] +name = "askama_escape" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341" + +[[package]] +name = "async-broadcast" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cd0e2e25ea8e5f7e9df04578dc6cf5c83577fd09b1a46aaf5c85e1c33f2a7e" +dependencies = [ + "event-listener", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-channel" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8828ec6e544c02b0d6691d21ed9f9218d0384a82542855073c2a3f58304aaf0" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "slab", +] + +[[package]] +name = "async-fs" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" +dependencies = [ + "async-lock", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-io" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d6baa8f0178795da0e71bc42c9e5d13261aac7ee549853162e66a241ba17964" +dependencies = [ + "async-lock", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "async-lock" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-net" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" +dependencies = [ + "async-io", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-process" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7eda79bbd84e29c2b308d1dc099d7de8dcc7035e48f4bf5dc4a531a44ff5e2a" +dependencies = [ + "async-channel", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "async-recursion" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "async-signal" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "329972aa325176e89114919f2a80fdae4f4c040f66a370b1a1159c6c0f94e7aa" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix", + "signal-hook-registry", + "slab", + "windows-sys 0.52.0", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "async-trait" +version = "0.1.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "atk" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4af014b17dd80e8af9fa689b2d4a211ddba6eb583c1622f35d0cb543f6b17e4" +dependencies = [ + "atk-sys", + "glib", + "libc", +] + +[[package]] +name = "atk-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "251e0b7d90e33e0ba930891a505a9a35ece37b2dd37a14f3ffc306c13b980009" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "atomic-polyfill" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" +dependencies = [ + "critical-section", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "axum" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" +dependencies = [ + "async-trait", + "axum-core", + "axum-macros", + "base64 0.21.7", + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", + "hyper 1.3.1", + "hyper-util", + "itoa 1.0.11", + "matchit", + "memchr", + "mime", + "multer", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sha1", + "sync_wrapper 1.0.1", + "tokio", + "tokio-tungstenite", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper 0.1.2", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00c055ee2d014ae5981ce1016374e8213682aa14d9bf40e48ab48b5f3ef20eaa" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "backtrace" +version = "0.3.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17c6a35df3749d2e8bb1b7b21a976d82b15548788d2735b9d82f329268f71a11" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +dependencies = [ + "serde", +] + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "blocking" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + +[[package]] +name = "bstr" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" + +[[package]] +name = "cairo-rs" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" +dependencies = [ + "bitflags 2.5.0", + "cairo-sys-rs", + "glib", + "libc", + "once_cell", + "thiserror", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "camino" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "cc" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfb" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" +dependencies = [ + "byteorder", + "fnv", + "uuid", +] + +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-targets 0.52.5", +] + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "cobs" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" + +[[package]] +name = "cocoa" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" +dependencies = [ + "bitflags 1.3.2", + "block", + "cocoa-foundation", + "core-foundation", + "core-graphics", + "foreign-types", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" +dependencies = [ + "bitflags 1.3.2", + "block", + "core-foundation", + "core-graphics-types", + "libc", + "objc", +] + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "const_format" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a214c7af3d04997541b18d432afaff4c455e79e2029079647e72fc2bd27673" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[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-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "core-graphics" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "critical-section" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" + +[[package]] +name = "crossbeam-channel" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +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.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array 0.14.7", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array 0.14.7", + "typenum", +] + +[[package]] +name = "cssparser" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa 0.4.8", + "matches", + "phf 0.8.0", + "proc-macro2", + "quote", + "smallvec", + "syn 1.0.109", +] + +[[package]] +name = "cssparser-macros" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" +dependencies = [ + "quote", + "syn 2.0.66", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "platforms", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "darling" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.66", +] + +[[package]] +name = "darling_macro" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "data-encoding" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" + +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", + "serde", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case 0.4.0", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "dioxus" +version = "0.5.2" +dependencies = [ + "dioxus-config-macro", + "dioxus-core", + "dioxus-core-macro", + "dioxus-desktop", + "dioxus-fullstack", + "dioxus-hooks", + "dioxus-hot-reload", + "dioxus-html", + "dioxus-liveview", + "dioxus-router", + "dioxus-signals", + "dioxus-ssr", + "dioxus-static-site-generation", + "dioxus-web", + "serde", +] + +[[package]] +name = "dioxus-cli-config" +version = "0.5.2" +dependencies = [ + "once_cell", + "serde", + "serde_json", + "tracing", +] + +[[package]] +name = "dioxus-config-macro" +version = "0.5.2" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "dioxus-core" +version = "0.5.2" +dependencies = [ + "futures-channel", + "futures-util", + "generational-box", + "longest-increasing-subsequence", + "rustc-hash", + "serde", + "slab", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "dioxus-core-macro" +version = "0.5.2" +dependencies = [ + "convert_case 0.6.0", + "dioxus-rsx", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "dioxus-debug-cell" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ea539174bb236e0e7dc9c12b19b88eae3cb574dedbd0252a2d43ea7e6de13e2" + +[[package]] +name = "dioxus-desktop" +version = "0.5.2" +dependencies = [ + "async-trait", + "cocoa", + "core-foundation", + "dioxus-cli-config", + "dioxus-core", + "dioxus-hooks", + "dioxus-hot-reload", + "dioxus-html", + "dioxus-interpreter-js", + "dunce", + "futures-channel", + "futures-util", + "generational-box", + "global-hotkey", + "infer", + "muda", + "objc", + "objc_id", + "rfd", + "rustc-hash", + "serde", + "serde_json", + "signal-hook", + "slab", + "tao", + "thiserror", + "tokio", + "tracing", + "urlencoding", + "webbrowser", + "wry", +] + +[[package]] +name = "dioxus-fullstack" +version = "0.5.2" +dependencies = [ + "anymap", + "async-trait", + "axum", + "base64 0.21.7", + "bytes", + "ciborium", + "dioxus-cli-config", + "dioxus-desktop", + "dioxus-hot-reload", + "dioxus-lib", + "dioxus-ssr", + "dioxus-web", + "dioxus_server_macro", + "futures-util", + "http 1.1.0", + "hyper 1.3.1", + "once_cell", + "pin-project", + "serde", + "serde_json", + "server_fn", + "thiserror", + "tokio", + "tokio-stream", + "tokio-util", + "tower", + "tower-http", + "tower-layer", + "tracing", + "tracing-futures", + "web-sys", +] + +[[package]] +name = "dioxus-hooks" +version = "0.5.2" +dependencies = [ + "dioxus-core", + "dioxus-debug-cell", + "dioxus-signals", + "futures-channel", + "futures-util", + "generational-box", + "slab", + "thiserror", + "tracing", +] + +[[package]] +name = "dioxus-hot-reload" +version = "0.5.2" +dependencies = [ + "axum", + "chrono", + "dioxus-core", + "dioxus-html", + "dioxus-rsx", + "execute", + "futures-util", + "ignore", + "interprocess-docfix", + "notify", + "once_cell", + "serde", + "serde_json", + "tokio", + "tokio-stream", + "tracing", +] + +[[package]] +name = "dioxus-html" +version = "0.5.2" +dependencies = [ + "async-trait", + "dioxus-core", + "dioxus-html-internal-macro", + "dioxus-rsx", + "enumset", + "euclid", + "futures-channel", + "generational-box", + "keyboard-types", + "serde", + "serde-value", + "serde_json", + "serde_repr", + "tokio", + "tracing", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "dioxus-html-internal-macro" +version = "0.5.2" +dependencies = [ + "convert_case 0.6.0", + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "dioxus-interpreter-js" +version = "0.5.2" +dependencies = [ + "dioxus-core", + "dioxus-html", + "js-sys", + "md5", + "sledgehammer_bindgen", + "sledgehammer_utils", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "dioxus-lib" +version = "0.5.2" +dependencies = [ + "dioxus-config-macro", + "dioxus-core", + "dioxus-core-macro", + "dioxus-hooks", + "dioxus-html", + "dioxus-rsx", + "dioxus-signals", +] + +[[package]] +name = "dioxus-liveview" +version = "0.5.2" +dependencies = [ + "axum", + "dioxus-cli-config", + "dioxus-core", + "dioxus-hot-reload", + "dioxus-html", + "dioxus-interpreter-js", + "futures-channel", + "futures-util", + "generational-box", + "rustc-hash", + "serde", + "serde_json", + "slab", + "thiserror", + "tokio", + "tokio-stream", + "tokio-util", + "tracing", +] + +[[package]] +name = "dioxus-logger" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe09dc9773dc1f1bb0d38529203d6555d08f67aadca5cf955ac3d1a9e69880" +dependencies = [ + "console_error_panic_hook", + "tracing", + "tracing-subscriber", + "tracing-wasm", +] + +[[package]] +name = "dioxus-router" +version = "0.5.2" +dependencies = [ + "dioxus-cli-config", + "dioxus-fullstack", + "dioxus-lib", + "dioxus-router-macro", + "dioxus-ssr", + "gloo", + "gloo-utils 0.1.7", + "http 1.1.0", + "js-sys", + "tokio", + "tracing", + "url", + "urlencoding", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "dioxus-router-macro" +version = "0.5.2" +dependencies = [ + "proc-macro2", + "quote", + "slab", + "syn 2.0.66", +] + +[[package]] +name = "dioxus-rsx" +version = "0.5.2" +dependencies = [ + "dioxus-core", + "internment", + "krates", + "proc-macro2", + "quote", + "syn 2.0.66", + "tracing", +] + +[[package]] +name = "dioxus-sdk" +version = "0.5.0" +source = "git+https://github.com/Dioxuslabs/sdk#d49812fbc9d4506bd3b1ec994f45ef4447f34c79" +dependencies = [ + "cfg-if", + "dioxus", + "dioxus-signals", + "directories", + "futures-util", + "js-sys", + "once_cell", + "postcard", + "rustc-hash", + "serde", + "tokio", + "tracing", + "uuid", + "wasm-bindgen", + "web-sys", + "yazi", +] + +[[package]] +name = "dioxus-signals" +version = "0.5.2" +dependencies = [ + "dioxus-core", + "futures-channel", + "futures-util", + "generational-box", + "once_cell", + "parking_lot", + "rustc-hash", + "serde", + "tracing", +] + +[[package]] +name = "dioxus-ssr" +version = "0.5.2" +dependencies = [ + "askama_escape", + "async-trait", + "chrono", + "dioxus-cli-config", + "dioxus-core", + "dioxus-html", + "generational-box", + "http 1.1.0", + "lru", + "rustc-hash", + "serde_json", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "dioxus-static-site-generation" +version = "0.5.2" +dependencies = [ + "axum", + "dioxus-cli-config", + "dioxus-fullstack", + "dioxus-hot-reload", + "dioxus-lib", + "dioxus-router", + "dioxus-ssr", + "dioxus-web", + "http 1.1.0", + "tokio", + "tower", + "tower-http", + "tracing", +] + +[[package]] +name = "dioxus-web" +version = "0.5.2" +dependencies = [ + "async-trait", + "console_error_panic_hook", + "dioxus-core", + "dioxus-html", + "dioxus-interpreter-js", + "futures-channel", + "futures-util", + "generational-box", + "js-sys", + "rustc-hash", + "serde", + "serde-wasm-bindgen", + "serde_json", + "tracing", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "dioxus_server_macro" +version = "0.5.2" +dependencies = [ + "convert_case 0.6.0", + "proc-macro2", + "quote", + "server_fn_macro", + "syn 2.0.66", +] + +[[package]] +name = "directories" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + +[[package]] +name = "dlopen2" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1297103d2bbaea85724fcee6294c2d50b1081f9ad47d0f6f6f61eda65315a6" +dependencies = [ + "dlopen2_derive", + "libc", + "once_cell", + "winapi", +] + +[[package]] +name = "dlopen2_derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "dtoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" + +[[package]] +name = "dtoa-short" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbaceec3c6e4211c79e7b1800fb9680527106beb2f9c51904a3210c03a448c74" +dependencies = [ + "dtoa", +] + +[[package]] +name = "dunce" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" + +[[package]] +name = "dyn-clone" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +dependencies = [ + "curve25519-dalek", + "ed25519", + "serde", + "sha2", + "subtle", + "zeroize", +] + +[[package]] +name = "either" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array 0.14.7", + "group", + "hkdf", + "pem-rfc7468", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" + +[[package]] +name = "encoding_rs" +version = "0.8.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +dependencies = [ + "cfg-if", +] + +[[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.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3278c9d5fb675e0a51dabcf4c0d355f692b064171535ba72361be1528a9d8e8d" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c785274071b1b420972453b306eeca06acf4633829db4223b58a2a8c5953bc4" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "enumset" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "226c0da7462c13fb57e5cc9e0dc8f0635e7d27f276a3a7fd30054647f669007d" +dependencies = [ + "enumset_derive", +] + +[[package]] +name = "enumset_derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08b6c6ab82d70f08844964ba10c7babb716de2ecaeab9be5717918a5177d3af" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[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.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "euclid" +version = "0.22.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0f0eb73b934648cd7a4a61f1b15391cd95dab0b4da6e2e66c2a072c144b4a20" +dependencies = [ + "num-traits", + "serde", +] + +[[package]] +name = "event-listener" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +dependencies = [ + "event-listener", + "pin-project-lite", +] + +[[package]] +name = "execute" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a82608ee96ce76aeab659e9b8d3c2b787bffd223199af88c674923d861ada10" +dependencies = [ + "execute-command-macro", + "execute-command-tokens", + "generic-array 1.0.0", +] + +[[package]] +name = "execute-command-macro" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90dec53d547564e911dc4ff3ecb726a64cf41a6fa01a2370ebc0d95175dd08bd" +dependencies = [ + "execute-command-macro-impl", +] + +[[package]] +name = "execute-command-macro-impl" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce8cd46a041ad005ab9c71263f9a0ff5b529eac0fe4cc9b4a20f4f0765d8cf4b" +dependencies = [ + "execute-command-tokens", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "execute-command-tokens" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69dc321eb6be977f44674620ca3aa21703cb20ffbe560e1ae97da08401ffbcad" + +[[package]] +name = "fastrand" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" + +[[package]] +name = "fdeflate" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "field-offset" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" +dependencies = [ + "memoffset", + "rustc_version", +] + +[[package]] +name = "filetime" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.4.1", + "windows-sys 0.52.0", +] + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "flate2" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[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 = "fsevent-sys" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" +dependencies = [ + "libc", +] + +[[package]] +name = "futf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" +dependencies = [ + "mac", + "new_debug_unreachable", +] + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-lite" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "gdk" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5ba081bdef3b75ebcdbfc953699ed2d7417d6bd853347a42a37d76406a33646" +dependencies = [ + "cairo-rs", + "gdk-pixbuf", + "gdk-sys", + "gio", + "glib", + "libc", + "pango", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" +dependencies = [ + "gdk-pixbuf-sys", + "gio", + "glib", + "libc", + "once_cell", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gdk-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31ff856cb3386dae1703a920f803abafcc580e9b5f711ca62ed1620c25b51ff2" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkwayland-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a90fbf5c033c65d93792192a49a8efb5bb1e640c419682a58bb96f5ae77f3d4a" +dependencies = [ + "gdk-sys", + "glib-sys", + "gobject-sys", + "libc", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkx11" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2ea8a4909d530f79921290389cbd7c34cb9d623bfe970eaae65ca5f9cd9cce" +dependencies = [ + "gdk", + "gdkx11-sys", + "gio", + "glib", + "libc", + "x11", +] + +[[package]] +name = "gdkx11-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fee8f00f4ee46cad2939b8990f5c70c94ff882c3028f3cc5abf950fa4ab53043" +dependencies = [ + "gdk-sys", + "glib-sys", + "libc", + "system-deps", + "x11", +] + +[[package]] +name = "generational-box" +version = "0.5.2" +dependencies = [ + "parking_lot", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "generic-array" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe739944a5406424e080edccb6add95685130b9f160d5407c639c7df0c5836b0" +dependencies = [ + "typenum", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "gimli" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" + +[[package]] +name = "gio" +version = "0.18.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys", + "glib", + "libc", + "once_cell", + "pin-project-lite", + "smallvec", + "thiserror", +] + +[[package]] +name = "gio-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", + "winapi", +] + +[[package]] +name = "glib" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" +dependencies = [ + "bitflags 2.5.0", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "memchr", + "once_cell", + "smallvec", + "thiserror", +] + +[[package]] +name = "glib-macros" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" +dependencies = [ + "heck 0.4.1", + "proc-macro-crate 2.0.2", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "glib-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" +dependencies = [ + "libc", + "system-deps", +] + +[[package]] +name = "global-hotkey" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89cb13e8c52c87e28a46eae3e5e65b8f0cd465c4c9e67b13d56c70412e792bc3" +dependencies = [ + "bitflags 2.5.0", + "cocoa", + "crossbeam-channel", + "keyboard-types", + "objc", + "once_cell", + "thiserror", + "windows-sys 0.52.0", + "x11-dl", +] + +[[package]] +name = "globset" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "gloo" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28999cda5ef6916ffd33fb4a7b87e1de633c47c0dc6d97905fee1cdaa142b94d" +dependencies = [ + "gloo-console", + "gloo-dialogs", + "gloo-events", + "gloo-file", + "gloo-history", + "gloo-net 0.3.1", + "gloo-render", + "gloo-storage", + "gloo-timers", + "gloo-utils 0.1.7", + "gloo-worker", +] + +[[package]] +name = "gloo-console" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b7ce3c05debe147233596904981848862b068862e9ec3e34be446077190d3f" +dependencies = [ + "gloo-utils 0.1.7", + "js-sys", + "serde", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-dialogs" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67062364ac72d27f08445a46cab428188e2e224ec9e37efdba48ae8c289002e6" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-events" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b107f8abed8105e4182de63845afcc7b69c098b7852a813ea7462a320992fc" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-file" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d5564e570a38b43d78bdc063374a0c3098c4f0d64005b12f9bbe87e869b6d7" +dependencies = [ + "gloo-events", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-history" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85725d90bf0ed47063b3930ef28e863658a7905989e9929a8708aab74a1d5e7f" +dependencies = [ + "gloo-events", + "gloo-utils 0.1.7", + "serde", + "serde-wasm-bindgen", + "serde_urlencoded", + "thiserror", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-net" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a66b4e3c7d9ed8d315fd6b97c8b1f74a7c6ecbbc2320e65ae7ed38b7068cc620" +dependencies = [ + "futures-channel", + "futures-core", + "futures-sink", + "gloo-utils 0.1.7", + "http 0.2.12", + "js-sys", + "pin-project", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-net" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43aaa242d1239a8822c15c645f02166398da4f8b5c4bae795c1f5b44e9eee173" +dependencies = [ + "futures-channel", + "futures-core", + "futures-sink", + "gloo-utils 0.2.0", + "http 0.2.12", + "js-sys", + "pin-project", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-render" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd9306aef67cfd4449823aadcd14e3958e0800aa2183955a309112a84ec7764" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-storage" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6ab60bf5dbfd6f0ed1f7843da31b41010515c745735c970e821945ca91e480" +dependencies = [ + "gloo-utils 0.1.7", + "js-sys", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-timers" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "gloo-utils" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037fcb07216cb3a30f7292bd0176b050b7b9a052ba830ef7d5d65f6dc64ba58e" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-utils" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-worker" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13471584da78061a28306d1359dd0178d8d6fc1c7c80e5e35d27260346e0516a" +dependencies = [ + "anymap2", + "bincode", + "gloo-console", + "gloo-utils 0.1.7", + "js-sys", + "serde", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gobject-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "gtk" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93c4f5e0e20b60e10631a5f06da7fe3dda744b05ad0ea71fee2f47adf865890c" +dependencies = [ + "atk", + "cairo-rs", + "field-offset", + "futures-channel", + "gdk", + "gdk-pixbuf", + "gio", + "glib", + "gtk-sys", + "gtk3-macros", + "libc", + "pango", + "pkg-config", +] + +[[package]] +name = "gtk-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771437bf1de2c1c0b496c11505bdf748e26066bbe942dfc8f614c9460f6d7722" +dependencies = [ + "atk-sys", + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "system-deps", +] + +[[package]] +name = "gtk3-macros" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6063efb63db582968fb7df72e1ae68aa6360dcfb0a75143f34fc7d616bad75e" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap 2.2.6", + "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", + "crunchy", +] + +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "heapless" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" +dependencies = [ + "atomic-polyfill", + "hash32", + "rustc_version", + "serde", + "spin 0.9.8", + "stable_deref_trait", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "html5ever" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" +dependencies = [ + "log", + "mac", + "markup5ever", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa 1.0.11", +] + +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa 1.0.11", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + +[[package]] +name = "http-body" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +dependencies = [ + "bytes", + "http 1.1.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" +dependencies = [ + "bytes", + "futures-core", + "http 1.1.0", + "http-body 1.0.0", + "pin-project-lite", +] + +[[package]] +name = "http-range-header" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08a397c49fec283e3d6211adbe480be95aae5f304cfb923e9970e08956d5168a" + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "0.14.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http 0.2.12", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa 1.0.11", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "httparse", + "httpdate", + "itoa 1.0.11", + "pin-project-lite", + "smallvec", + "tokio", +] + +[[package]] +name = "hyper-rustls" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +dependencies = [ + "futures-util", + "http 0.2.12", + "hyper 0.14.29", + "rustls", + "tokio", + "tokio-rustls", +] + +[[package]] +name = "hyper-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b875924a60b96e5d7b9ae7b066540b1dd1cbd90d1828f54c92e02a283351c56" +dependencies = [ + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "hyper 1.3.1", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +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 = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "ignore" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata", + "same-file", + "walkdir", + "winapi-util", +] + +[[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.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown 0.14.5", + "serde", +] + +[[package]] +name = "infer" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6c16b11a665b26aeeb9b1d7f954cdeb034be38dd00adab4f2ae921a8fee804" +dependencies = [ + "cfb", +] + +[[package]] +name = "inotify" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" +dependencies = [ + "bitflags 1.3.2", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "internment" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04e8e537b529b8674e97e9fb82c10ff168a290ac3867a0295f112061ffbca1ef" +dependencies = [ + "hashbrown 0.14.5", + "parking_lot", +] + +[[package]] +name = "interprocess-docfix" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b84ee245c606aeb0841649a9288e3eae8c61b853a8cd5c0e14450e96d53d28f" +dependencies = [ + "blocking", + "cfg-if", + "futures-core", + "futures-io", + "intmap", + "libc", + "once_cell", + "rustc_version", + "spinning", + "thiserror", + "to_method", + "winapi", +] + +[[package]] +name = "intmap" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae52f28f45ac2bc96edb7714de995cffc174a395fb0abf5bff453587c980d7b9" + +[[package]] +name = "inventory" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f958d3d68f4167080a18141e10381e7634563984a537f2a49a30fd8e53ac5767" + +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "javascriptcore-rs" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca5671e9ffce8ffba57afc24070e906da7fc4b1ba66f2cabebf61bf2ea257fcc" +dependencies = [ + "bitflags 1.3.2", + "glib", + "javascriptcore-rs-sys", +] + +[[package]] +name = "javascriptcore-rs-sys" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1be78d14ffa4b75b66df31840478fef72b51f8c2465d4ca7c194da9f7a5124" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror", + "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 = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "keyboard-types" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" +dependencies = [ + "bitflags 2.5.0", + "serde", + "unicode-segmentation", +] + +[[package]] +name = "kqueue" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +dependencies = [ + "bitflags 1.3.2", + "libc", +] + +[[package]] +name = "krates" +version = "0.16.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcb3baf2360eb25ad31f0ada3add63927ada6db457791979b82ac199f835cb9" +dependencies = [ + "cargo-platform", + "cargo_metadata", + "cfg-expr", + "petgraph", + "semver", +] + +[[package]] +name = "kuchikiki" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e4755b7b995046f510a7520c42b2fed58b77bd94d5a87a8eb43d2fd126da8" +dependencies = [ + "cssparser", + "html5ever", + "indexmap 1.9.3", + "matches", + "selectors", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin 0.5.2", +] + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.5.0", + "libc", +] + +[[package]] +name = "libxdo" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00333b8756a3d28e78def82067a377de7fa61b24909000aeaa2b446a948d14db" +dependencies = [ + "libxdo-sys", +] + +[[package]] +name = "libxdo-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db23b9e7e2b7831bbd8aac0bbeeeb7b68cbebc162b227e7052e8e55829a09212" +dependencies = [ + "libc", + "x11", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[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 = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "longest-increasing-subsequence" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3bd0dd2cd90571056fdb71f6275fada10131182f84899f4b2a916e565d81d86" + +[[package]] +name = "lru" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" +dependencies = [ + "hashbrown 0.14.5", +] + +[[package]] +name = "mac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "markup5ever" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" +dependencies = [ + "log", + "phf 0.10.1", + "phf_codegen 0.10.0", + "string_cache", + "string_cache_codegen", + "tendril", +] + +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + +[[package]] +name = "md5" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" + +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[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 = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "miniz_oxide" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" +dependencies = [ + "adler", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.48.0", +] + +[[package]] +name = "muda" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c47e7625990fc1af2226ea4f34fb2412b03c12639fcb91868581eb3a6893453" +dependencies = [ + "cocoa", + "crossbeam-channel", + "gtk", + "keyboard-types", + "libxdo", + "objc", + "once_cell", + "png", + "thiserror", + "windows-sys 0.52.0", +] + +[[package]] +name = "multer" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b" +dependencies = [ + "bytes", + "encoding_rs", + "futures-util", + "http 1.1.0", + "httparse", + "memchr", + "mime", + "spin 0.9.8", + "version_check", +] + +[[package]] +name = "ndk" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" +dependencies = [ + "bitflags 1.3.2", + "jni-sys", + "ndk-sys", + "num_enum", + "raw-window-handle 0.5.2", + "thiserror", +] + +[[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.4.1+23.1.7779620" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cf2aae958bd232cac5069850591667ad422d263686d75b52a065f9badeee5a3" +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.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags 2.5.0", + "cfg-if", + "libc", + "memoffset", +] + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + +[[package]] +name = "notify" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "729f63e1ca555a43fe3efa4f3efdf4801c479da85b432242a7b726f353c88486" +dependencies = [ + "bitflags 1.3.2", + "crossbeam-channel", + "filetime", + "fsevent-sys", + "inotify", + "kqueue", + "libc", + "mio", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand 0.8.5", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[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-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "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", + "libm", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "oauth2" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c38841cdd844847e3e7c8d29cef9dcfed8877f8f56f9071f77843ecf3baf937f" +dependencies = [ + "base64 0.13.1", + "chrono", + "getrandom 0.2.15", + "http 0.2.12", + "rand 0.8.5", + "reqwest", + "serde", + "serde_json", + "serde_path_to_error", + "sha2", + "thiserror", + "url", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", + "objc_exception", +] + +[[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_exception" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" +dependencies = [ + "cc", +] + +[[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.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "openid_auth_demo" +version = "0.1.0" +dependencies = [ + "anyhow", + "console_error_panic_hook", + "dioxus", + "dioxus-logger", + "dioxus-sdk", + "form_urlencoded", + "log", + "openidconnect", + "serde", + "uuid", +] + +[[package]] +name = "openidconnect" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f47e80a9cfae4462dd29c41e987edd228971d6565553fbc14b8a11e666d91590" +dependencies = [ + "base64 0.13.1", + "chrono", + "dyn-clone", + "ed25519-dalek", + "hmac", + "http 0.2.12", + "itertools", + "log", + "oauth2", + "p256", + "p384", + "rand 0.8.5", + "rsa", + "serde", + "serde-value", + "serde_derive", + "serde_json", + "serde_path_to_error", + "serde_plain", + "serde_with", + "sha2", + "subtle", + "thiserror", + "url", +] + +[[package]] +name = "ordered-float" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +dependencies = [ + "num-traits", +] + +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "p384" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "pango" +version = "0.18.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" +dependencies = [ + "gio", + "glib", + "libc", + "once_cell", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "parking" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" + +[[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", + "libc", + "redox_syscall 0.5.1", + "smallvec", + "windows-targets 0.52.5", +] + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "petgraph" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset", + "indexmap 2.2.6", +] + +[[package]] +name = "phf" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" +dependencies = [ + "phf_macros", + "phf_shared 0.8.0", + "proc-macro-hack", +] + +[[package]] +name = "phf" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" +dependencies = [ + "phf_shared 0.10.0", +] + +[[package]] +name = "phf_codegen" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" +dependencies = [ + "phf_generator 0.8.0", + "phf_shared 0.8.0", +] + +[[package]] +name = "phf_codegen" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", +] + +[[package]] +name = "phf_generator" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" +dependencies = [ + "phf_shared 0.8.0", + "rand 0.7.3", +] + +[[package]] +name = "phf_generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +dependencies = [ + "phf_shared 0.10.0", + "rand 0.8.5", +] + +[[package]] +name = "phf_macros" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" +dependencies = [ + "phf_generator 0.8.0", + "phf_shared 0.8.0", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "phf_shared" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" +dependencies = [ + "siphasher", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[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.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1d5c74c9876f070d3e8fd503d748c7d974c3e48da8f41350fa5222ef9b4391" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "platforms" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7" + +[[package]] +name = "png" +version = "0.17.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "polling" +version = "3.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6a007746f34ed64099e88783b0ae369eaa3da6392868ba262e2af9b8fbaea1" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "pollster" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2" + +[[package]] +name = "postcard" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a55c51ee6c0db07e68448e336cf8ea4131a620edefebf9893e759b2d793420f8" +dependencies = [ + "cobs", + "embedded-io", + "heapless", + "serde", +] + +[[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.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "prettyplease" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" +dependencies = [ + "proc-macro2", + "syn 2.0.66", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" +dependencies = [ + "toml_datetime", + "toml_edit 0.20.2", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + +[[package]] +name = "proc-macro2" +version = "1.0.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", + "rand_pcg", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[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 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.15", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "raw-window-handle" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" + +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +dependencies = [ + "bitflags 2.5.0", +] + +[[package]] +name = "redox_users" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +dependencies = [ + "getrandom 0.2.15", + "libredox", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" + +[[package]] +name = "reqwest" +version = "0.11.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +dependencies = [ + "base64 0.21.7", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.29", + "hyper-rustls", + "ipnet", + "js-sys", + "log", + "mime", + "mime_guess", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 0.1.2", + "system-configuration", + "tokio", + "tokio-rustls", + "tokio-util", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "webpki-roots", + "winreg", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "rfd" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a73a7337fc24366edfca76ec521f51877b114e42dab584008209cca6719251" +dependencies = [ + "ashpd", + "block", + "dispatch", + "js-sys", + "log", + "objc", + "objc-foundation", + "objc_id", + "pollster", + "raw-window-handle 0.6.2", + "urlencoding", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-sys 0.48.0", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.15", + "libc", + "spin 0.9.8", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rsa" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" +dependencies = [ + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core 0.6.4", + "signature", + "spki", + "subtle", + "zeroize", +] + +[[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_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags 2.5.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.21.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + +[[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 = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array 0.14.7", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "selectors" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe" +dependencies = [ + "bitflags 1.3.2", + "cssparser", + "derive_more", + "fxhash", + "log", + "matches", + "phf 0.8.0", + "phf_codegen 0.8.0", + "precomputed-hash", + "servo_arc", + "smallvec", + "thin-slice", +] + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +dependencies = [ + "serde", +] + +[[package]] +name = "send_wrapper" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" +dependencies = [ + "futures-core", +] + +[[package]] +name = "serde" +version = "1.0.203" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float", + "serde", +] + +[[package]] +name = "serde-wasm-bindgen" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3b143e2833c57ab9ad3ea280d21fd34e285a42837aeb0ee301f4f41890fa00e" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "serde_derive" +version = "1.0.203" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "serde_json" +version = "1.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +dependencies = [ + "itoa 1.0.11", + "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 1.0.11", + "serde", +] + +[[package]] +name = "serde_plain" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1fc6db65a611022b23a0dec6975d63fb80a302cb3388835ff02c097258d50" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_qs" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0431a35568651e363364210c91983c1da5eb29404d9f0928b67d4ebcfa7d330c" +dependencies = [ + "percent-encoding", + "serde", + "thiserror", +] + +[[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.66", +] + +[[package]] +name = "serde_spanned" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" +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 1.0.11", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "3.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ad483d2ab0149d5a5ebcd9972a3852711e0153d863bf5a5d0391d28883c4a20" +dependencies = [ + "base64 0.22.1", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.2.6", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65569b702f41443e8bc8bbb1c5779bd0450bbe723b56198980e80ec45780bce2" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "server_fn" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b06e6e5467a2cd93ce1accfdfd8b859404f0b3b2041131ffd774fabf666b8219" +dependencies = [ + "axum", + "bytes", + "const_format", + "dashmap", + "futures", + "gloo-net 0.5.0", + "http 1.1.0", + "http-body-util", + "hyper 1.3.1", + "inventory", + "js-sys", + "once_cell", + "reqwest", + "send_wrapper", + "serde", + "serde_json", + "serde_qs", + "server_fn_macro_default", + "thiserror", + "tower", + "tower-layer", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "xxhash-rust", +] + +[[package]] +name = "server_fn_macro" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09c216bb1c1ac890151397643c663c875a1836adf0b269be4e389cb1b48c173c" +dependencies = [ + "const_format", + "convert_case 0.6.0", + "proc-macro2", + "quote", + "syn 2.0.66", + "xxhash-rust", +] + +[[package]] +name = "server_fn_macro_default" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00783df297ec85ea605779f2fef9cbec98981dffe2e01e1a9845c102ee1f1ae6" +dependencies = [ + "server_fn_macro", + "syn 2.0.66", +] + +[[package]] +name = "servo_arc" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432" +dependencies = [ + "nodrop", + "stable_deref_trait", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[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 = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core 0.6.4", +] + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "sledgehammer_bindgen" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcfaf791ff02f48f3518ce825d32cf419c13a43c1d8b1232f74ac89f339c46d2" +dependencies = [ + "sledgehammer_bindgen_macro", + "wasm-bindgen", +] + +[[package]] +name = "sledgehammer_bindgen_macro" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdd941cc539bd3dc694edaf9d0c4e1221d02baa67c6b45ec04fad1024d9e8139" +dependencies = [ + "quote", + "syn 2.0.66", +] + +[[package]] +name = "sledgehammer_utils" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f20798defa0e9d4eff9ca451c7f84774c7378a9c3b5a40112cfa2b3eadb97ae2" +dependencies = [ + "lru", + "once_cell", + "rustc-hash", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "soup3" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "471f924a40f31251afc77450e781cb26d55c0b650842efafc9c6cbd2f7cc4f9f" +dependencies = [ + "futures-channel", + "gio", + "glib", + "libc", + "soup3-sys", +] + +[[package]] +name = "soup3-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebe8950a680a12f24f15ebe1bf70db7af98ad242d9db43596ad3108aab86c27" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spinning" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d4f0e86297cad2658d92a707320d87bf4e6ae1050287f51d19b67ef3f153a7b" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[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 = "string_cache" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" +dependencies = [ + "new_debug_unreachable", + "once_cell", + "parking_lot", + "phf_shared 0.10.0", + "precomputed-hash", + "serde", +] + +[[package]] +name = "string_cache_codegen" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", + "proc-macro2", + "quote", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[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.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "sync_wrapper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +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 0.5.0", + "pkg-config", + "toml", + "version-compare", +] + +[[package]] +name = "tao" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69ebbccb78deb5a36744c079eea2981b4a48ecbbe6b1b2ffbaa528bea3f5e5db" +dependencies = [ + "bitflags 1.3.2", + "cocoa", + "core-foundation", + "core-graphics", + "crossbeam-channel", + "dispatch", + "dlopen2", + "gdkwayland-sys", + "gdkx11-sys", + "gtk", + "instant", + "jni", + "lazy_static", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "objc", + "once_cell", + "parking_lot", + "raw-window-handle 0.5.2", + "raw-window-handle 0.6.2", + "scopeguard", + "tao-macros", + "unicode-segmentation", + "url", + "windows 0.54.0", + "windows-version", + "x11-dl", +] + +[[package]] +name = "tao-macros" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec114582505d158b669b136e6851f85840c109819d77c42bb7c0709f727d18c2" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "target-lexicon" +version = "0.12.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" + +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys 0.52.0", +] + +[[package]] +name = "tendril" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" +dependencies = [ + "futf", + "mac", + "utf-8", +] + +[[package]] +name = "thin-slice" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" + +[[package]] +name = "thiserror" +version = "1.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "itoa 1.0.11", + "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.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "to_method" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c4ceeeca15c8384bbc3e011dbd8fccb7f068a440b752b7d9b32ceb0ca0e2e8" + +[[package]] +name = "tokio" +version = "1.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", + "tokio-util", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite", +] + +[[package]] +name = "tokio-util" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "futures-util", + "hashbrown 0.14.5", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.20.2", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.2.6", + "toml_datetime", + "winnow", +] + +[[package]] +name = "toml_edit" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +dependencies = [ + "indexmap 2.2.6", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" +dependencies = [ + "bitflags 2.5.0", + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", + "http-range-header", + "httpdate", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project", + "tracing", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "nu-ansi-term", + "sharded-slab", + "smallvec", + "thread_local", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "tracing-wasm" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4575c663a174420fa2d78f4108ff68f65bf2fbb7dd89f33749b6e826b3626e07" +dependencies = [ + "tracing", + "tracing-subscriber", + "wasm-bindgen", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "tungstenite" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http 1.1.0", + "httparse", + "log", + "rand 0.8.5", + "sha1", + "thiserror", + "url", + "utf-8", +] + +[[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", +] + +[[package]] +name = "unicase" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +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 = "uuid" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" +dependencies = [ + "getrandom 0.2.15", + "wasm-bindgen", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[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.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[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 = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[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.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.66", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "wasm-streams" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webbrowser" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db67ae75a9405634f5882791678772c94ff5f16a66535aae186e26aa0841fc8b" +dependencies = [ + "core-foundation", + "home", + "jni", + "log", + "ndk-context", + "objc", + "raw-window-handle 0.5.2", + "url", + "web-sys", +] + +[[package]] +name = "webkit2gtk" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76b1bc1e54c581da1e9f179d0b38512ba358fb1af2d634a1affe42e37172361a" +dependencies = [ + "bitflags 1.3.2", + "cairo-rs", + "gdk", + "gdk-sys", + "gio", + "gio-sys", + "glib", + "glib-sys", + "gobject-sys", + "gtk", + "gtk-sys", + "javascriptcore-rs", + "libc", + "once_cell", + "soup3", + "webkit2gtk-sys", +] + +[[package]] +name = "webkit2gtk-sys" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62daa38afc514d1f8f12b8693d30d5993ff77ced33ce30cd04deebc267a6d57c" +dependencies = [ + "bitflags 1.3.2", + "cairo-sys-rs", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "gtk-sys", + "javascriptcore-rs-sys", + "libc", + "pkg-config", + "soup3-sys", + "system-deps", +] + +[[package]] +name = "webpki-roots" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + +[[package]] +name = "webview2-com" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0ae9c7e420783826cf769d2c06ac9ba462f450eca5893bb8c6c6529a4e5dd33" +dependencies = [ + "webview2-com-macros", + "webview2-com-sys", + "windows 0.52.0", + "windows-core 0.52.0", + "windows-implement 0.52.0", + "windows-interface 0.52.0", +] + +[[package]] +name = "webview2-com-macros" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac1345798ecd8122468840bcdf1b95e5dc6d2206c5e4b0eafa078d061f59c9bc" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "webview2-com-sys" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6ad85fceee6c42fa3d61239eba5a11401bf38407a849ed5ea1b407df08cca72" +dependencies = [ + "thiserror", + "windows 0.52.0", + "windows-core 0.52.0", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +dependencies = [ + "windows-sys 0.52.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.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core 0.52.0", + "windows-implement 0.52.0", + "windows-interface 0.52.0", + "windows-targets 0.52.5", +] + +[[package]] +name = "windows" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49" +dependencies = [ + "windows-core 0.54.0", + "windows-implement 0.53.0", + "windows-interface 0.53.0", + "windows-targets 0.52.5", +] + +[[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.5", +] + +[[package]] +name = "windows-core" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65" +dependencies = [ + "windows-result", + "windows-targets 0.52.5", +] + +[[package]] +name = "windows-implement" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12168c33176773b86799be25e2a2ba07c7aab9968b37541f1094dbd7a60c8946" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "windows-implement" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942ac266be9249c84ca862f0a164a39533dc2f6f33dc98ec89c8da99b82ea0bd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "windows-interface" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d8dc32e0095a7eeccebd0e3f09e9509365ecb3fc6ac4d6f5f14a3f6392942d1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "windows-interface" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da33557140a288fae4e1d5f8873aaf9eb6613a9cf82c3e070223ff177f598b60" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "windows-result" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "749f0da9cc72d82e600d8d2e44cadd0b9eedb9038f71a1c58556ac1c5791813b" +dependencies = [ + "windows-targets 0.52.5", +] + +[[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.5", +] + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", +] + +[[package]] +name = "windows-version" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6998aa457c9ba8ff2fb9f13e9d2a930dabcea28f1d0ab94d687d8b3654844515" +dependencies = [ + "windows-targets 0.52.5", +] + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "wry" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b717040ba9771fd88eb428c6ea6b555f8e734ff8534f02c13e8f10d97f5935e" +dependencies = [ + "base64 0.21.7", + "block", + "cfg_aliases", + "cocoa", + "core-graphics", + "crossbeam-channel", + "dunce", + "gdkx11", + "gtk", + "html5ever", + "http 0.2.12", + "javascriptcore-rs", + "jni", + "kuchikiki", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "objc", + "objc_id", + "once_cell", + "percent-encoding", + "raw-window-handle 0.6.2", + "serde", + "serde_json", + "sha2", + "soup3", + "tao-macros", + "thiserror", + "webkit2gtk", + "webkit2gtk-sys", + "webview2-com", + "windows 0.52.0", + "windows-implement 0.52.0", + "windows-version", + "x11-dl", +] + +[[package]] +name = "x11" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "xdg-home" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21e5a325c3cb8398ad6cf859c1135b25dd29e186679cf2da7581d9679f63b38e" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "xxhash-rust" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927da81e25be1e1a2901d59b81b37dd2efd1fc9c9345a55007f09bf5a2d3ee03" + +[[package]] +name = "yazi" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c94451ac9513335b5e23d7a8a2b61a7102398b8cca5160829d313e84c9d98be1" + +[[package]] +name = "zbus" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b8e3d6ae3342792a6cc2340e4394334c7402f3d793b390d2c5494a4032b3030" +dependencies = [ + "async-broadcast", + "async-executor", + "async-fs", + "async-io", + "async-lock", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "derivative", + "enumflags2", + "event-listener", + "futures-core", + "futures-sink", + "futures-util", + "hex", + "nix", + "ordered-stream", + "rand 0.8.5", + "serde", + "serde_repr", + "sha1", + "static_assertions", + "tracing", + "uds_windows", + "windows-sys 0.52.0", + "xdg-home", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7a3e850ff1e7217a3b7a07eba90d37fe9bb9e89a310f718afcde5885ca9b6d7" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "regex", + "syn 1.0.109", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" +dependencies = [ + "serde", + "static_assertions", + "zvariant", +] + +[[package]] +name = "zerocopy" +version = "0.7.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zvariant" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e09e8be97d44eeab994d752f341e67b3b0d80512a8b315a0671d47232ef1b65" +dependencies = [ + "endi", + "enumflags2", + "serde", + "static_assertions", + "url", + "zvariant_derive", +] + +[[package]] +name = "zvariant_derive" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72a5857e2856435331636a9fbb415b09243df4521a267c5bedcd5289b4d5799e" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00bedb16a193cc12451873fee2a1bc6550225acece0e36f333e68326c73c8172" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] diff --git a/examples/openid_connect_demo/Cargo.toml b/examples/openid_connect_demo/Cargo.toml new file mode 100644 index 0000000000..72ae74dc98 --- /dev/null +++ b/examples/openid_connect_demo/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "openid_auth_demo" +version = "0.1.0" +edition = "2021" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1.0.86" +console_error_panic_hook = "0.1" +dioxus = { path = "../../packages/dioxus", default_features = true, features = [ + "router", + "signals", +], version = "*" } +dioxus-logger = "0.5.1" +dioxus-sdk = { git = "https://github.com/Dioxuslabs/sdk", features = [ + "storage", +] } +form_urlencoded = "1.2.1" +log = "0.4" +openidconnect = "3.5.0" +serde = { version = "1.0.203", features = ["derive"] } +uuid = "1.8" + +[features] +default = ["web"] +server = ["dioxus/axum"] +web = ["dioxus/web"] +desktop = ["dioxus/desktop"] +fullstack = ["dioxus/fullstack"] + +# since we're using dioxus from local path, inform dioxus-sdk to use it as well +[patch.crates-io] +dioxus = { path = "../../packages/dioxus" } +dioxus-signals = { path = "../../packages/signals" } diff --git a/examples/openid_connect_demo/Dioxus.toml b/examples/openid_connect_demo/Dioxus.toml new file mode 100644 index 0000000000..3a130e2ea3 --- /dev/null +++ b/examples/openid_connect_demo/Dioxus.toml @@ -0,0 +1,35 @@ +[application] + +# dioxus project name +name = "OpenID Connect authentication demo" + +# default platform +# you can also use `dioxus serve/build --platform XXX` to use other platform +# value: web | desktop +default_platform = "web" + +# Web `build` & `serve` dist path +out_dir = "dist" + +# resource (static) file folder +asset_dir = "public" + +# hot reload by default +hot_reload = true + +[web.app] + +# HTML title tag content +title = "OpenID Connect authentication demo" + +[web.watcher] + +index_on_404 = true + +watch_path = ["src"] + +[application.plugins] + +available = true + +required = [] diff --git a/examples/openid_connect_demo/README.md b/examples/openid_connect_demo/README.md new file mode 100644 index 0000000000..7b0d100fd9 --- /dev/null +++ b/examples/openid_connect_demo/README.md @@ -0,0 +1,12 @@ +# OpenID Connect example to show how to authenticate an user + +The environment variables in [`.cargo/config.toml`](./.cargo/config.toml) must be set in order for this example to work. + +Once they are set, you can run `dx serve --platform web` or `dx serve --platform desktop`. + +### Environment variables summary + +- `DIOXUS_FRONT_ISSUER_URL`: The openid-connect's issuer url +- `DIOXUS_FRONT_CLIENT_ID`: The openid-connect's client id +- `DIOXUS_FRONT_CLIENT_SECRET`: The openid-connect's client secret +- `DIOXUS_FRONT_URL`: The url the frontend is supposed to be running on, it could be for example `http://localhost:8080` diff --git a/examples/openid_connect_demo/src/constants.rs b/examples/openid_connect_demo/src/constants.rs new file mode 100644 index 0000000000..0a0b4950a6 --- /dev/null +++ b/examples/openid_connect_demo/src/constants.rs @@ -0,0 +1,2 @@ +pub const DIOXUS_FRONT_AUTH_TOKEN: &str = "auth_token"; +pub const DIOXUS_FRONT_AUTH_REQUEST: &str = "auth_request"; diff --git a/examples/openid_connect_demo/src/main.rs b/examples/openid_connect_demo/src/main.rs new file mode 100644 index 0000000000..8487758595 --- /dev/null +++ b/examples/openid_connect_demo/src/main.rs @@ -0,0 +1,36 @@ +#![allow(non_snake_case)] +use dioxus::prelude::*; +use dioxus_logger::tracing::Level; +use router::Route; + +use crate::oidc::ClientState; +use crate::storage::{use_auth_request_provider, use_auth_token_provider}; + +pub(crate) mod constants; +pub(crate) mod model; +pub(crate) mod oidc; +pub(crate) mod props; +pub(crate) mod router; +pub(crate) mod storage; +pub(crate) mod views; + +pub static CLIENT: GlobalSignal = Signal::global(ClientState::default); + +pub static DIOXUS_FRONT_ISSUER_URL: &str = env!("DIOXUS_FRONT_ISSUER_URL"); +pub static DIOXUS_FRONT_CLIENT_ID: &str = env!("DIOXUS_FRONT_CLIENT_ID"); +pub static DIOXUS_FRONT_CLIENT_SECRET: &str = env!("DIOXUS_FRONT_CLIENT_SECRET"); +pub static DIOXUS_FRONT_URL: &str = env!("DIOXUS_FRONT_URL"); + +fn App() -> Element { + use_auth_request_provider(); + use_auth_token_provider(); + rsx! { Router:: {} } +} + +fn main() { + dioxus_logger::init(Level::DEBUG).expect("failed to init logger"); + dioxus_sdk::set_dir!(); + console_error_panic_hook::set_once(); + log::info!("starting app"); + launch(App); +} diff --git a/examples/openid_connect_demo/src/model/mod.rs b/examples/openid_connect_demo/src/model/mod.rs new file mode 100644 index 0000000000..68a79f5c0c --- /dev/null +++ b/examples/openid_connect_demo/src/model/mod.rs @@ -0,0 +1 @@ +pub(crate) mod user; diff --git a/examples/openid_connect_demo/src/model/user.rs b/examples/openid_connect_demo/src/model/user.rs new file mode 100644 index 0000000000..b1005fdd24 --- /dev/null +++ b/examples/openid_connect_demo/src/model/user.rs @@ -0,0 +1,7 @@ +use uuid::Uuid; + +#[derive(PartialEq)] +pub struct User { + pub id: Uuid, + pub name: String, +} diff --git a/examples/openid_connect_demo/src/oidc.rs b/examples/openid_connect_demo/src/oidc.rs new file mode 100644 index 0000000000..2c20c4e49b --- /dev/null +++ b/examples/openid_connect_demo/src/oidc.rs @@ -0,0 +1,127 @@ +use anyhow::Result; +use openidconnect::{ + core::{CoreClient, CoreIdToken, CoreResponseType, CoreTokenResponse}, + reqwest::async_http_client, + url::Url, + AuthenticationFlow, AuthorizationCode, ClaimsVerificationError, ClientId, ClientSecret, + CsrfToken, IssuerUrl, LogoutRequest, Nonce, ProviderMetadataWithLogout, RedirectUrl, + RefreshToken, +}; +use serde::{Deserialize, Serialize}; + +use crate::{props::client::ClientProps, DIOXUS_FRONT_CLIENT_ID}; + +#[derive(Clone, Debug, Default)] +pub struct ClientState { + pub oidc_client: Option, +} + +/// State that holds the nonce and authorization url and the nonce generated to log in an user +#[derive(Clone, PartialEq, Deserialize, Serialize, Default)] +pub struct AuthRequestState { + pub auth_request: Option, +} + +#[derive(Clone, PartialEq, Deserialize, Serialize)] +pub struct AuthRequest { + pub nonce: Nonce, + pub authorize_url: String, +} + +/// State the tokens returned once the user is authenticated +#[derive(Debug, Deserialize, Serialize, Default, Clone)] +pub struct AuthTokenState { + /// Token used to identify the user + pub id_token: Option, + /// Token used to refresh the tokens if they expire + pub refresh_token: Option, +} + +impl PartialEq for AuthTokenState { + fn eq(&self, other: &Self) -> bool { + self.id_token == other.id_token + && self.refresh_token.as_ref().map(|t| t.secret().clone()) + == other.refresh_token.as_ref().map(|t| t.secret().clone()) + } +} + +pub fn email( + client: CoreClient, + id_token: CoreIdToken, + nonce: Nonce, +) -> Result { + match id_token.claims(&client.id_token_verifier(), &nonce) { + Ok(claims) => Ok(claims.clone().email().unwrap().to_string()), + Err(error) => Err(error), + } +} + +pub fn authorize_url(client: CoreClient) -> AuthRequest { + let (authorize_url, _csrf_state, nonce) = client + .authorize_url( + AuthenticationFlow::::AuthorizationCode, + CsrfToken::new_random, + Nonce::new_random, + ) + .add_scope(openidconnect::Scope::new("email".to_string())) + .add_scope(openidconnect::Scope::new("profile".to_string())) + .url(); + AuthRequest { + authorize_url: authorize_url.to_string(), + nonce, + } +} + +pub async fn init_provider_metadata() -> Result { + let issuer_url = IssuerUrl::new(crate::DIOXUS_FRONT_ISSUER_URL.to_string())?; + Ok(ProviderMetadataWithLogout::discover_async(issuer_url, async_http_client).await?) +} + +pub async fn init_oidc_client() -> Result<(ClientId, CoreClient)> { + let client_id = ClientId::new(crate::DIOXUS_FRONT_CLIENT_ID.to_string()); + let provider_metadata = init_provider_metadata().await?; + let client_secret = Some(ClientSecret::new( + crate::DIOXUS_FRONT_CLIENT_SECRET.to_string(), + )); + let redirect_url = RedirectUrl::new(format!("{}/login", crate::DIOXUS_FRONT_URL))?; + + Ok(( + client_id.clone(), + CoreClient::from_provider_metadata(provider_metadata, client_id, client_secret) + .set_redirect_uri(redirect_url), + )) +} + +///TODO: Add pkce_pacifier +pub async fn token_response(oidc_client: CoreClient, code: String) -> Result { + // let (pkce_challenge, pkce_verifier) = PkceCodeChallenge::new_random_sha256(); + Ok(oidc_client + .exchange_code(AuthorizationCode::new(code.clone())) + // .set_pkce_verifier(pkce_verifier) + .request_async(async_http_client) + .await?) +} + +pub async fn exchange_refresh_token( + oidc_client: CoreClient, + refresh_token: RefreshToken, +) -> Result { + Ok(oidc_client + .exchange_refresh_token(&refresh_token) + .request_async(async_http_client) + .await?) +} + +pub async fn log_out_url(id_token_hint: CoreIdToken) -> Result { + let provider_metadata = init_provider_metadata().await?; + let end_session_url = provider_metadata + .additional_metadata() + .clone() + .end_session_endpoint + .unwrap(); + let logout_request: LogoutRequest = LogoutRequest::from(end_session_url); + Ok(logout_request + .set_client_id(ClientId::new(DIOXUS_FRONT_CLIENT_ID.to_string())) + .set_id_token_hint(&id_token_hint) + .http_get_url()) +} diff --git a/examples/openid_connect_demo/src/props/client.rs b/examples/openid_connect_demo/src/props/client.rs new file mode 100644 index 0000000000..2e02111e16 --- /dev/null +++ b/examples/openid_connect_demo/src/props/client.rs @@ -0,0 +1,20 @@ +use dioxus::prelude::*; +use openidconnect::{core::CoreClient, ClientId}; + +#[derive(Props, Clone, Debug)] +pub struct ClientProps { + pub client: CoreClient, + pub client_id: ClientId, +} + +impl PartialEq for ClientProps { + fn eq(&self, other: &Self) -> bool { + self.client_id == other.client_id + } +} + +impl ClientProps { + pub fn new(client_id: ClientId, client: CoreClient) -> Self { + ClientProps { client_id, client } + } +} diff --git a/examples/openid_connect_demo/src/props/mod.rs b/examples/openid_connect_demo/src/props/mod.rs new file mode 100644 index 0000000000..1d33131531 --- /dev/null +++ b/examples/openid_connect_demo/src/props/mod.rs @@ -0,0 +1 @@ +pub(crate) mod client; diff --git a/examples/openid_connect_demo/src/router.rs b/examples/openid_connect_demo/src/router.rs new file mode 100644 index 0000000000..8f4b1b36a6 --- /dev/null +++ b/examples/openid_connect_demo/src/router.rs @@ -0,0 +1,16 @@ +use crate::views::{header::AuthHeader, home::Home, login::Login, not_found::NotFound}; +use dioxus::prelude::*; + +#[derive(Routable, Clone)] +pub enum Route { + #[layout(AuthHeader)] + #[route("/")] + Home {}, + + // https://dioxuslabs.com/learn/0.4/router/reference/routes#query-segments + #[route("/login?:query_string")] + Login { query_string: String }, + #[end_layout] + #[route("/:..route")] + NotFound { route: Vec }, +} diff --git a/examples/openid_connect_demo/src/storage.rs b/examples/openid_connect_demo/src/storage.rs new file mode 100644 index 0000000000..c34819b965 --- /dev/null +++ b/examples/openid_connect_demo/src/storage.rs @@ -0,0 +1,35 @@ +use dioxus::prelude::*; +use dioxus_sdk::storage::*; + +use crate::{ + constants::{DIOXUS_FRONT_AUTH_REQUEST, DIOXUS_FRONT_AUTH_TOKEN}, + oidc::{AuthRequestState, AuthTokenState}, +}; + +pub fn use_auth_token_provider() { + let stored_token = + use_storage::(DIOXUS_FRONT_AUTH_TOKEN.to_owned(), AuthTokenState::default); + + use_context_provider(move || stored_token); +} + +pub fn use_auth_token() -> Signal { + use_context() +} + +pub fn use_auth_request_provider() { + let stored_req = use_storage::( + DIOXUS_FRONT_AUTH_REQUEST.to_owned(), + AuthRequestState::default, + ); + + use_context_provider(move || stored_req); +} + +pub fn use_auth_request() -> Signal { + use_context() +} + +pub fn auth_request() -> Signal { + consume_context() +} diff --git a/examples/openid_connect_demo/src/views/header.rs b/examples/openid_connect_demo/src/views/header.rs new file mode 100644 index 0000000000..274a0d22f0 --- /dev/null +++ b/examples/openid_connect_demo/src/views/header.rs @@ -0,0 +1,221 @@ +use crate::storage::{auth_request, use_auth_request, use_auth_token}; +use crate::{ + oidc::{ + authorize_url, email, exchange_refresh_token, init_oidc_client, log_out_url, + AuthRequestState, AuthTokenState, ClientState, + }, + props::client::ClientProps, + router::Route, + CLIENT, +}; +use anyhow::Result; +use dioxus::prelude::*; +use dioxus::router::prelude::{Link, Outlet}; +use openidconnect::{url::Url, OAuth2TokenResponse, TokenResponse}; + +#[component] +pub fn LogOut() -> Element { + let mut auth_token = use_auth_token(); + let log_out_url_state = use_signal(|| None::>>); + match auth_token().id_token { + Some(id_token) => match &*log_out_url_state.read() { + Some(log_out_url_result) => match log_out_url_result { + Some(uri) => match uri { + Ok(uri) => { + rsx! { + Link { + onclick: move |_| { + auth_token.take(); + }, + to: uri.to_string(), + "Log out" + } + } + } + Err(error) => { + rsx! { div { "Failed to load disconnection url: {error:?}" } } + } + }, + None => { + rsx! { div { "Loading... Please wait" } } + } + }, + None => { + let logout_url_task = move || { + spawn({ + let mut log_out_url_state = log_out_url_state.to_owned(); + async move { + let logout_url = log_out_url(id_token).await; + let logout_url_option = Some(logout_url); + log_out_url_state.set(Some(logout_url_option)); + } + }) + }; + logout_url_task(); + rsx! { div { "Loading log out url... Please wait" } } + } + }, + None => { + rsx! {{}} + } + } +} + +#[component] +pub fn RefreshToken(props: ClientProps) -> Element { + let mut auth_token = use_auth_token(); + match auth_token().refresh_token { + Some(refresh_token) => { + rsx! { div { + onmounted: { + move |_| { + let client = props.client.clone(); + let refresh_token = refresh_token.clone(); + async move { + let exchange_refresh_token = + exchange_refresh_token(client, refresh_token).await; + match exchange_refresh_token { + Ok(response_token) => { + auth_token.set(AuthTokenState { + id_token: response_token.id_token().cloned(), + refresh_token: response_token.refresh_token().cloned(), + }); + } + Err(_error) => { + auth_token.take(); + auth_request().take(); + } + } + } + } + }, + "Refreshing session, please wait" + } } + } + None => { + rsx! { div { "Id token expired and no refresh token found" } } + } + } +} + +#[component] +pub fn LoadClient() -> Element { + let init_client_future = use_resource(move || async move { init_oidc_client().await }); + match &*init_client_future.read_unchecked() { + Some(Ok((client_id, client))) => rsx! { + div { + onmounted: { + let client_id = client_id.clone(); + let client = client.clone(); + move |_| { + *CLIENT.write() = ClientState { + oidc_client: Some(ClientProps::new(client_id.clone(), client.clone())), + }; + } + }, + "Client successfully loaded" + } + Outlet:: {} + }, + Some(Err(error)) => { + log::info! {"Failed to load client: {:?}", error}; + rsx! { + div { "Failed to load client: {error:?}" } + Outlet:: {} + } + } + None => { + rsx! { + div { + div { "Loading client, please wait" } + Outlet:: {} + } + } + } + } +} + +#[component] +pub fn AuthHeader() -> Element { + let client = CLIENT.read().oidc_client.clone(); + let mut auth_request = use_auth_request(); + let auth_token = use_auth_token(); + match (client, auth_request(), auth_token()) { + // We have everything we need to attempt to authenticate the user + (Some(client_props), current_auth_request, current_auth_token) => { + match current_auth_request.auth_request { + Some(new_auth_request) => { + match current_auth_token.id_token { + Some(id_token) => { + match email( + client_props.client.clone(), + id_token.clone(), + new_auth_request.nonce.clone(), + ) { + Ok(email) => { + rsx! { + div { + div { {email} } + LogOut {} + Outlet:: {} + } + } + } + // Id token failed to be decoded + Err(error) => match error { + // Id token failed to be decoded because it expired, we refresh it + openidconnect::ClaimsVerificationError::Expired(_message) => { + log::info!("Token expired"); + rsx! { + div { + RefreshToken { client_id: client_props.client_id, client: client_props.client } + Outlet:: {} + } + } + } + // Other issue with token decoding + _ => { + log::info!("Other issue with token"); + rsx! { + div { + div { "{error}" } + Outlet:: {} + } + } + } + }, + } + } + // User is not logged in + None => { + rsx! { + div { + Link { to: new_auth_request.authorize_url.clone(), "Log in" } + Outlet:: {} + } + } + } + } + } + None => { + rsx! { div { + onmounted: { + let client = client_props.client; + move |_| { + let new_auth_request = authorize_url(client.clone()); + auth_request.set(AuthRequestState { + auth_request: Some(new_auth_request), + }); + } + }, + "Loading nonce" + } } + } + } + } + // Client is not initialized yet, we need it for everything + (None, _, _) => { + rsx! { LoadClient {} } + } + } +} diff --git a/examples/openid_connect_demo/src/views/home.rs b/examples/openid_connect_demo/src/views/home.rs new file mode 100644 index 0000000000..d91b5c2037 --- /dev/null +++ b/examples/openid_connect_demo/src/views/home.rs @@ -0,0 +1,5 @@ +use dioxus::prelude::*; + +pub fn Home() -> Element { + rsx! { div { "Hello world" } } +} diff --git a/examples/openid_connect_demo/src/views/login.rs b/examples/openid_connect_demo/src/views/login.rs new file mode 100644 index 0000000000..e3a4e0025b --- /dev/null +++ b/examples/openid_connect_demo/src/views/login.rs @@ -0,0 +1,86 @@ +use crate::{ + oidc::{token_response, AuthTokenState}, + router::Route, + storage::{auth_request, use_auth_token}, + CLIENT, +}; +use dioxus::prelude::*; +use dioxus::router::prelude::Link; +use openidconnect::{OAuth2TokenResponse, TokenResponse}; + +#[component] +pub fn Login(query_string: String) -> Element { + let client = CLIENT.read().oidc_client.clone(); + let mut auth_token = use_auth_token(); + let current_auth_token = auth_token(); + match client { + Some(client_props) => { + match ( + current_auth_token.id_token, + current_auth_token.refresh_token, + ) { + (Some(_id_token), Some(_refresh_token)) => { + rsx! { + div { "Sign in successful" } + Link { to: Route::Home {}, "Go back home" } + } + } + // If the refresh token is set but not the id_token, there was an error, we just go back home and reset their value + (None, Some(_)) | (Some(_), None) => { + rsx! { + div { "Error while attempting to log in" } + Link { + to: Route::Home {}, + onclick: move |_| { + auth_token.take(); + auth_request().take(); + }, + "Go back home" + } + } + } + (None, None) => { + let mut query_pairs = form_urlencoded::parse(query_string.as_bytes()); + let code_pair = query_pairs.find(|(key, _value)| key == "code"); + match code_pair { + Some((_key, code)) => { + let code = code.to_string(); + rsx! { div { + onmounted: { + move |_| { + let auth_code = code.to_string(); + let client_props = client_props.clone(); + async move { + let token_response_result = + token_response(client_props.client, auth_code).await; + match token_response_result { + Ok(token_response) => { + let id_token = token_response.id_token().unwrap(); + auth_token.set(AuthTokenState { + id_token: Some(id_token.clone()), + refresh_token: token_response + .refresh_token() + .cloned(), + }); + } + Err(error) => { + log::warn! {"{error}"}; + } + } + } + } + } + }} + } + None => { + rsx! { div { "No code provided" } } + } + } + } + } + } + _ => { + rsx! {{}} + } + } +} diff --git a/examples/openid_connect_demo/src/views/mod.rs b/examples/openid_connect_demo/src/views/mod.rs new file mode 100644 index 0000000000..1b8e08a585 --- /dev/null +++ b/examples/openid_connect_demo/src/views/mod.rs @@ -0,0 +1,4 @@ +pub(crate) mod header; +pub(crate) mod home; +pub(crate) mod login; +pub(crate) mod not_found; diff --git a/examples/openid_connect_demo/src/views/not_found.rs b/examples/openid_connect_demo/src/views/not_found.rs new file mode 100644 index 0000000000..833c610674 --- /dev/null +++ b/examples/openid_connect_demo/src/views/not_found.rs @@ -0,0 +1,10 @@ +use dioxus::prelude::*; + +#[component] +pub fn NotFound(route: Vec) -> Element { + rsx! { + div{ + {route.join("")} + } + } +} diff --git a/examples/optional_props.rs b/examples/optional_props.rs index 334852bea1..ad54ed33cd 100644 --- a/examples/optional_props.rs +++ b/examples/optional_props.rs @@ -6,7 +6,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/overlay.rs b/examples/overlay.rs index 59a9619d6a..e4e2e80592 100644 --- a/examples/overlay.rs +++ b/examples/overlay.rs @@ -11,9 +11,7 @@ use dioxus::desktop::{ use dioxus::prelude::*; fn main() { - dioxus::launch::builder() - .with_cfg(make_config()) - .launch(app); + LaunchBuilder::desktop().with_cfg(make_config()).launch(app); } fn app() -> Element { @@ -22,9 +20,9 @@ fn app() -> Element { _ = use_global_shortcut("cmd+g", move || show_overlay.toggle()); rsx! { - document::Link { + head::Link { rel: "stylesheet", - href: asset!("/examples/assets/overlay.css"), + href: asset!("./examples/assets/overlay.css"), } if show_overlay() { div { diff --git a/examples/popup.rs b/examples/popup.rs index e708b9cd9f..131d536edb 100644 --- a/examples/popup.rs +++ b/examples/popup.rs @@ -5,7 +5,7 @@ use dioxus::prelude::*; use std::rc::Rc; fn main() { - dioxus::launch(app); + launch_desktop(app); } fn app() -> Element { diff --git a/examples/query_segment_search.rs b/examples/query_segment_search.rs index 4366cc0118..018f475e45 100644 --- a/examples/query_segment_search.rs +++ b/examples/query_segment_search.rs @@ -2,7 +2,7 @@ //! //! The enum router makes it easy to use your route as state in your app. This example shows how to use the router to encode search text into the url and decode it back into a string. //! -//! Run this example on desktop with +//! Run this example on desktop with //! ```sh //! dx serve --example query_segment_search //! ``` @@ -14,7 +14,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(|| { + launch(|| { rsx! { Router:: {} } @@ -29,7 +29,7 @@ enum Route { // The each query segment must implement and Display. // You can use multiple query segments separated by `&`s. - #[route("/search?:query&:word_count")] + #[route("/search?:query&:word_count")] Search { query: String, word_count: usize, diff --git a/examples/read_size.rs b/examples/read_size.rs index dbde9353c7..1965597cfa 100644 --- a/examples/read_size.rs +++ b/examples/read_size.rs @@ -9,7 +9,7 @@ use std::rc::Rc; use dioxus::{html::geometry::euclid::Rect, prelude::*}; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { @@ -28,7 +28,7 @@ fn app() -> Element { }; rsx!( - document::Stylesheet { href: asset!("/examples/assets/read_size.css") } + head::Link { rel: "stylesheet", href: asset!("./examples/assets/read_size.css") } div { width: "50%", height: "50%", diff --git a/examples/readme.rs b/examples/readme.rs index 1c36c65c00..8f412a749d 100644 --- a/examples/readme.rs +++ b/examples/readme.rs @@ -8,7 +8,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/reducer.rs b/examples/reducer.rs index 32a15368e8..7c434e5858 100644 --- a/examples/reducer.rs +++ b/examples/reducer.rs @@ -7,17 +7,17 @@ use dioxus::prelude::*; -const STYLE: Asset = asset!("/examples/assets/radio.css"); +const STYLE: &str = asset!("./examples/assets/radio.css"); fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { let mut state = use_signal(|| PlayerState { is_playing: false }); rsx!( - document::Stylesheet { href: STYLE } + head::Link { rel: "stylesheet", href: STYLE } h1 {"Select an option"} // Add some cute animations if the radio is playing! diff --git a/examples/resize.rs b/examples/resize.rs index 52b55bfb8c..eace774a0b 100644 --- a/examples/resize.rs +++ b/examples/resize.rs @@ -8,14 +8,14 @@ use dioxus::prelude::*; use dioxus_elements::geometry::euclid::Size2D; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { let mut dimensions = use_signal(Size2D::zero); rsx!( - document::Stylesheet { href: asset!("/examples/assets/read_size.css") } + head::Link { rel: "stylesheet", href: asset!("./examples/assets/read_size.css") } div { width: "50%", height: "50%", diff --git a/examples/router.rs b/examples/router.rs index 377c595418..13197f57e2 100644 --- a/examples/router.rs +++ b/examples/router.rs @@ -8,12 +8,12 @@ use dioxus::prelude::*; -const STYLE: Asset = asset!("/examples/assets/router.css"); +const STYLE: &str = asset!("./examples/assets/router.css"); fn main() { - dioxus::launch(|| { + launch(|| { rsx! { - document::Stylesheet { href: STYLE } + head::Link { rel: "stylesheet", href: STYLE } Router:: {} } }); diff --git a/examples/router_resource.rs b/examples/router_resource.rs index 6898585f3b..de69f0e40b 100644 --- a/examples/router_resource.rs +++ b/examples/router_resource.rs @@ -15,7 +15,7 @@ enum Route { } fn main() { - dioxus::launch(App); + launch(App); } #[component] diff --git a/examples/rsx_usage.rs b/examples/rsx_usage.rs index b8ae72fd68..3bffe8c700 100644 --- a/examples/rsx_usage.rs +++ b/examples/rsx_usage.rs @@ -39,7 +39,7 @@ //! - Allow top-level fragments fn main() { - dioxus::launch(app) + launch(app) } use core::{fmt, str::FromStr}; @@ -228,9 +228,6 @@ fn app() -> Element { // Or we can shell out to a helper function {format_dollars(10, 50)} - - // some text? - {Some("hello world!")} } } } diff --git a/examples/scroll_to_top.rs b/examples/scroll_to_top.rs index 1cd91e4947..9f4f85955e 100644 --- a/examples/scroll_to_top.rs +++ b/examples/scroll_to_top.rs @@ -8,7 +8,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/shortcut.rs b/examples/shortcut.rs index a6626c27b7..d0192b9782 100644 --- a/examples/shortcut.rs +++ b/examples/shortcut.rs @@ -9,7 +9,7 @@ use dioxus::desktop::use_global_shortcut; use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch_desktop(app); } fn app() -> Element { diff --git a/examples/shorthand.rs b/examples/shorthand.rs index bd2047a8e6..33905d5710 100644 --- a/examples/shorthand.rs +++ b/examples/shorthand.rs @@ -3,7 +3,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/signals.rs b/examples/signals.rs index 6b881587bc..26db9685c1 100644 --- a/examples/signals.rs +++ b/examples/signals.rs @@ -10,7 +10,7 @@ use async_std::task::sleep; use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/simple_list.rs b/examples/simple_list.rs index 32e1d3eb85..3c1b9d71a1 100644 --- a/examples/simple_list.rs +++ b/examples/simple_list.rs @@ -5,7 +5,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/simple_router.rs b/examples/simple_router.rs index 0ec22a001e..e674f2f30e 100644 --- a/examples/simple_router.rs +++ b/examples/simple_router.rs @@ -5,7 +5,7 @@ use dioxus::prelude::*; fn main() { // Launch the router, using our `Route` component as the generic type // This will automatically boot the app to "/" unless otherwise specified - dioxus::launch(|| rsx! { Router:: {} }); + launch(|| rsx! { Router:: {} }); } /// By default, the Routable derive will use the name of the variant as the route diff --git a/examples/streams.rs b/examples/streams.rs index a71b5b9c18..6155cb35f9 100644 --- a/examples/streams.rs +++ b/examples/streams.rs @@ -5,7 +5,7 @@ use dioxus::prelude::*; use futures_util::{future, stream, Stream, StreamExt}; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/suspense.rs b/examples/suspense.rs index 14cd2573e9..014bb3276a 100644 --- a/examples/suspense.rs +++ b/examples/suspense.rs @@ -15,7 +15,7 @@ use dioxus::desktop::{Config, LogicalSize, WindowBuilder}; use dioxus::prelude::*; fn main() { - dioxus::builder() + LaunchBuilder::new() .with_cfg(desktop! { Config::new().with_window( WindowBuilder::new() diff --git a/examples/svg.rs b/examples/svg.rs index b462f30288..d4c23d1ddb 100644 --- a/examples/svg.rs +++ b/examples/svg.rs @@ -10,7 +10,7 @@ use dioxus::prelude::*; use rand::{thread_rng, Rng}; fn main() { - dioxus::launch(|| { + launch(|| { rsx! { div { user_select: "none", webkit_user_select: "none", margin_left: "10%", margin_right: "10%", h1 { "Click die to generate a new value" } diff --git a/examples/tailwind/.gitignore b/examples/tailwind/.gitignore new file mode 100644 index 0000000000..1521c8b765 --- /dev/null +++ b/examples/tailwind/.gitignore @@ -0,0 +1 @@ +dist diff --git a/examples/tailwind/Cargo.toml b/examples/tailwind/Cargo.toml new file mode 100644 index 0000000000..6660b87397 --- /dev/null +++ b/examples/tailwind/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "dioxus-tailwind" +version = "0.0.0" +authors = [] +edition = "2021" +description = "A tailwindcss example using Dioxus" +license = "MIT OR Apache-2.0" +repository = "https://github.com/DioxusLabs/dioxus/" +homepage = "https://dioxuslabs.com" +documentation = "https://dioxuslabs.com" +publish = false + +[dependencies] +manganis = { workspace = true } + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +dioxus = { path = "../../packages/dioxus", features = ["desktop"] } + +[target.'cfg(target_arch = "wasm32")'.dependencies] +dioxus = { path = "../../packages/dioxus", features = ["web"] } diff --git a/examples/tailwind/Dioxus.toml b/examples/tailwind/Dioxus.toml new file mode 100644 index 0000000000..af7fcf3b8f --- /dev/null +++ b/examples/tailwind/Dioxus.toml @@ -0,0 +1,27 @@ +[application] + +# App (Project) Name +name = "Tailwind CSS + Dioxus" + +# Dioxus App Default Platform +# desktop, web, mobile, ssr +default_platform = "web" + +# `build` & `serve` dist path +out_dir = "dist" + +# resource (public) file folder +asset_dir = "public" + +[web.app] + +# HTML title tag content +title = "dioxus | ⛺" + +[web.watcher] + +# when watcher trigger, regenerate the `index.html` +reload_html = true + +# which files or dirs will be watcher monitoring +watch_path = ["src", "public"] diff --git a/examples/tailwind/README.md b/examples/tailwind/README.md new file mode 100644 index 0000000000..6dca4b775e --- /dev/null +++ b/examples/tailwind/README.md @@ -0,0 +1,7 @@ +Example: Basic Tailwind usage + +This example shows how an app might be styled with TailwindCSS. + +## Running + +Our [Tailwind](https://dioxuslabs.com/learn/0.5/cookbook/tailwind) guide explains how to setup and run Dioxus-Tailwind projects. \ No newline at end of file diff --git a/examples/tailwind/input.css b/examples/tailwind/input.css new file mode 100644 index 0000000000..bd6213e1df --- /dev/null +++ b/examples/tailwind/input.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; \ No newline at end of file diff --git a/examples/tailwind/public/tailwind.css b/examples/tailwind/public/tailwind.css new file mode 100644 index 0000000000..65b95316e1 --- /dev/null +++ b/examples/tailwind/public/tailwind.css @@ -0,0 +1,833 @@ +/* +! tailwindcss v3.2.7 | MIT License | https://tailwindcss.com +*/ + +/* +1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) +2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) +*/ + +*, +::before, +::after { + box-sizing: border-box; + /* 1 */ + border-width: 0; + /* 2 */ + border-style: solid; + /* 2 */ + border-color: #e5e7eb; + /* 2 */ +} + +::before, +::after { + --tw-content: ''; +} + +/* +1. Use a consistent sensible line-height in all browsers. +2. Prevent adjustments of font size after orientation changes in iOS. +3. Use a more readable tab size. +4. Use the user's configured `sans` font-family by default. +5. Use the user's configured `sans` font-feature-settings by default. +*/ + +html { + line-height: 1.5; + /* 1 */ + -webkit-text-size-adjust: 100%; + /* 2 */ + -moz-tab-size: 4; + /* 3 */ + -o-tab-size: 4; + tab-size: 4; + /* 3 */ + font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + /* 4 */ + font-feature-settings: normal; + /* 5 */ +} + +/* +1. Remove the margin in all browsers. +2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. +*/ + +body { + margin: 0; + /* 1 */ + line-height: inherit; + /* 2 */ +} + +/* +1. Add the correct height in Firefox. +2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) +3. Ensure horizontal rules are visible by default. +*/ + +hr { + height: 0; + /* 1 */ + color: inherit; + /* 2 */ + border-top-width: 1px; + /* 3 */ +} + +/* +Add the correct text decoration in Chrome, Edge, and Safari. +*/ + +abbr:where([title]) { + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; +} + +/* +Remove the default font size and weight for headings. +*/ + +h1, +h2, +h3, +h4, +h5, +h6 { + font-size: inherit; + font-weight: inherit; +} + +/* +Reset links to optimize for opt-in styling instead of opt-out. +*/ + +a { + color: inherit; + text-decoration: inherit; +} + +/* +Add the correct font weight in Edge and Safari. +*/ + +b, +strong { + font-weight: bolder; +} + +/* +1. Use the user's configured `mono` font family by default. +2. Correct the odd `em` font sizing in all browsers. +*/ + +code, +kbd, +samp, +pre { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + /* 1 */ + font-size: 1em; + /* 2 */ +} + +/* +Add the correct font size in all browsers. +*/ + +small { + font-size: 80%; +} + +/* +Prevent `sub` and `sup` elements from affecting the line height in all browsers. +*/ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* +1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) +2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) +3. Remove gaps between table borders by default. +*/ + +table { + text-indent: 0; + /* 1 */ + border-color: inherit; + /* 2 */ + border-collapse: collapse; + /* 3 */ +} + +/* +1. Change the font styles in all browsers. +2. Remove the margin in Firefox and Safari. +3. Remove default padding in all browsers. +*/ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; + /* 1 */ + font-size: 100%; + /* 1 */ + font-weight: inherit; + /* 1 */ + line-height: inherit; + /* 1 */ + color: inherit; + /* 1 */ + margin: 0; + /* 2 */ + padding: 0; + /* 3 */ +} + +/* +Remove the inheritance of text transform in Edge and Firefox. +*/ + +button, +select { + text-transform: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Remove default button styles. +*/ + +button, +[type='button'], +[type='reset'], +[type='submit'] { + -webkit-appearance: button; + /* 1 */ + background-color: transparent; + /* 2 */ + background-image: none; + /* 2 */ +} + +/* +Use the modern Firefox focus style for all focusable elements. +*/ + +:-moz-focusring { + outline: auto; +} + +/* +Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) +*/ + +:-moz-ui-invalid { + box-shadow: none; +} + +/* +Add the correct vertical alignment in Chrome and Firefox. +*/ + +progress { + vertical-align: baseline; +} + +/* +Correct the cursor style of increment and decrement buttons in Safari. +*/ + +::-webkit-inner-spin-button, +::-webkit-outer-spin-button { + height: auto; +} + +/* +1. Correct the odd appearance in Chrome and Safari. +2. Correct the outline style in Safari. +*/ + +[type='search'] { + -webkit-appearance: textfield; + /* 1 */ + outline-offset: -2px; + /* 2 */ +} + +/* +Remove the inner padding in Chrome and Safari on macOS. +*/ + +::-webkit-search-decoration { + -webkit-appearance: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Change font properties to `inherit` in Safari. +*/ + +::-webkit-file-upload-button { + -webkit-appearance: button; + /* 1 */ + font: inherit; + /* 2 */ +} + +/* +Add the correct display in Chrome and Safari. +*/ + +summary { + display: list-item; +} + +/* +Removes the default spacing and border for appropriate elements. +*/ + +blockquote, +dl, +dd, +h1, +h2, +h3, +h4, +h5, +h6, +hr, +figure, +p, +pre { + margin: 0; +} + +fieldset { + margin: 0; + padding: 0; +} + +legend { + padding: 0; +} + +ol, +ul, +menu { + list-style: none; + margin: 0; + padding: 0; +} + +/* +Prevent resizing textareas horizontally by default. +*/ + +textarea { + resize: vertical; +} + +/* +1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) +2. Set the default placeholder color to the user's configured gray 400 color. +*/ + +input::-moz-placeholder, textarea::-moz-placeholder { + opacity: 1; + /* 1 */ + color: #9ca3af; + /* 2 */ +} + +input::placeholder, +textarea::placeholder { + opacity: 1; + /* 1 */ + color: #9ca3af; + /* 2 */ +} + +/* +Set the default cursor for buttons. +*/ + +button, +[role="button"] { + cursor: pointer; +} + +/* +Make sure disabled buttons don't get the pointer cursor. +*/ + +:disabled { + cursor: default; +} + +/* +1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) +2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) + This can trigger a poorly considered lint error in some tools but is included by design. +*/ + +img, +svg, +video, +canvas, +audio, +iframe, +embed, +object { + display: block; + /* 1 */ + vertical-align: middle; + /* 2 */ +} + +/* +Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) +*/ + +img, +video { + max-width: 100%; + height: auto; +} + +/* Make elements with the HTML hidden attribute stay hidden by default */ + +[hidden] { + display: none; +} + +*, ::before, ::after { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; +} + +::backdrop { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; +} + +.container { + width: 100%; +} + +@media (min-width: 640px) { + .container { + max-width: 640px; + } +} + +@media (min-width: 768px) { + .container { + max-width: 768px; + } +} + +@media (min-width: 1024px) { + .container { + max-width: 1024px; + } +} + +@media (min-width: 1280px) { + .container { + max-width: 1280px; + } +} + +@media (min-width: 1536px) { + .container { + max-width: 1536px; + } +} + +.mx-auto { + margin-left: auto; + margin-right: auto; +} + +.mb-16 { + margin-bottom: 4rem; +} + +.mb-4 { + margin-bottom: 1rem; +} + +.mb-8 { + margin-bottom: 2rem; +} + +.ml-1 { + margin-left: 0.25rem; +} + +.ml-3 { + margin-left: 0.75rem; +} + +.ml-4 { + margin-left: 1rem; +} + +.mr-5 { + margin-right: 1.25rem; +} + +.mt-4 { + margin-top: 1rem; +} + +.flex { + display: flex; +} + +.inline-flex { + display: inline-flex; +} + +.hidden { + display: none; +} + +.h-10 { + height: 2.5rem; +} + +.h-4 { + height: 1rem; +} + +.w-10 { + width: 2.5rem; +} + +.w-4 { + width: 1rem; +} + +.w-5\/6 { + width: 83.333333%; +} + +.flex-col { + flex-direction: column; +} + +.flex-wrap { + flex-wrap: wrap; +} + +.items-center { + align-items: center; +} + +.justify-center { + justify-content: center; +} + +.rounded { + border-radius: 0.25rem; +} + +.rounded-full { + border-radius: 9999px; +} + +.border-0 { + border-width: 0px; +} + +.bg-gray-800 { + --tw-bg-opacity: 1; + background-color: rgb(31 41 55 / var(--tw-bg-opacity)); +} + +.bg-gray-900 { + --tw-bg-opacity: 1; + background-color: rgb(17 24 39 / var(--tw-bg-opacity)); +} + +.bg-indigo-500 { + --tw-bg-opacity: 1; + background-color: rgb(99 102 241 / var(--tw-bg-opacity)); +} + +.object-cover { + -o-object-fit: cover; + object-fit: cover; +} + +.object-center { + -o-object-position: center; + object-position: center; +} + +.p-2 { + padding: 0.5rem; +} + +.p-5 { + padding: 1.25rem; +} + +.px-3 { + padding-left: 0.75rem; + padding-right: 0.75rem; +} + +.px-5 { + padding-left: 1.25rem; + padding-right: 1.25rem; +} + +.px-6 { + padding-left: 1.5rem; + padding-right: 1.5rem; +} + +.py-1 { + padding-top: 0.25rem; + padding-bottom: 0.25rem; +} + +.py-2 { + padding-top: 0.5rem; + padding-bottom: 0.5rem; +} + +.py-24 { + padding-top: 6rem; + padding-bottom: 6rem; +} + +.text-center { + text-align: center; +} + +.text-3xl { + font-size: 1.875rem; + line-height: 2.25rem; +} + +.text-base { + font-size: 1rem; + line-height: 1.5rem; +} + +.text-lg { + font-size: 1.125rem; + line-height: 1.75rem; +} + +.text-xl { + font-size: 1.25rem; + line-height: 1.75rem; +} + +.font-medium { + font-weight: 500; +} + +.leading-relaxed { + line-height: 1.625; +} + +.text-gray-400 { + --tw-text-opacity: 1; + color: rgb(156 163 175 / var(--tw-text-opacity)); +} + +.text-white { + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); +} + +.hover\:bg-gray-700:hover { + --tw-bg-opacity: 1; + background-color: rgb(55 65 81 / var(--tw-bg-opacity)); +} + +.hover\:bg-indigo-600:hover { + --tw-bg-opacity: 1; + background-color: rgb(79 70 229 / var(--tw-bg-opacity)); +} + +.hover\:text-white:hover { + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); +} + +.focus\:outline-none:focus { + outline: 2px solid transparent; + outline-offset: 2px; +} + +@media (min-width: 640px) { + .sm\:text-4xl { + font-size: 2.25rem; + line-height: 2.5rem; + } +} + +@media (min-width: 768px) { + .md\:mb-0 { + margin-bottom: 0px; + } + + .md\:ml-auto { + margin-left: auto; + } + + .md\:mt-0 { + margin-top: 0px; + } + + .md\:w-1\/2 { + width: 50%; + } + + .md\:flex-row { + flex-direction: row; + } + + .md\:items-start { + align-items: flex-start; + } + + .md\:pr-16 { + padding-right: 4rem; + } + + .md\:text-left { + text-align: left; + } +} + +@media (min-width: 1024px) { + .lg\:inline-block { + display: inline-block; + } + + .lg\:w-full { + width: 100%; + } + + .lg\:max-w-lg { + max-width: 32rem; + } + + .lg\:flex-grow { + flex-grow: 1; + } + + .lg\:pr-24 { + padding-right: 6rem; + } +} \ No newline at end of file diff --git a/examples/tailwind/src/main.rs b/examples/tailwind/src/main.rs new file mode 100644 index 0000000000..70ce484c38 --- /dev/null +++ b/examples/tailwind/src/main.rs @@ -0,0 +1,103 @@ +#![allow(non_snake_case)] + +use dioxus::prelude::*; + +const _STYLE: &str = asset!("public/tailwind.css"); + +fn main() { + launch(app); +} + +pub fn app() -> Element { + let grey_background = true; + rsx!( + div { + header { + class: "text-gray-400 body-font", + // you can use optional attributes to optionally apply a tailwind class + class: if grey_background { + "bg-gray-900" + }, + div { class: "container mx-auto flex flex-wrap p-5 flex-col md:flex-row items-center", + a { class: "flex title-font font-medium items-center text-white mb-4 md:mb-0", + StacksIcon {} + span { class: "ml-3 text-xl", "Hello Dioxus!" } + } + nav { class: "md:ml-auto flex flex-wrap items-center text-base justify-center", + a { class: "mr-5 hover:text-white", "First Link" } + a { class: "mr-5 hover:text-white", "Second Link" } + a { class: "mr-5 hover:text-white", "Third Link" } + a { class: "mr-5 hover:text-white", "Fourth Link" } + } + button { class: "inline-flex items-center bg-gray-800 border-0 py-1 px-3 focus:outline-none hover:bg-gray-700 rounded text-base mt-4 md:mt-0", + "Button" + RightArrowIcon {} + } + } + } + + section { class: "text-gray-400 bg-gray-900 body-font", + div { class: "container mx-auto flex px-5 py-24 md:flex-row flex-col items-center", + div { class: "lg:flex-grow md:w-1/2 lg:pr-24 md:pr-16 flex flex-col md:items-start md:text-left mb-16 md:mb-0 items-center text-center", + h1 { class: "title-font sm:text-4xl text-3xl mb-4 font-medium text-white", + br { class: "hidden lg:inline-block" } + "Dioxus Sneak Peek" + } + p { class: "mb-8 leading-relaxed", + + "Dioxus is a new UI framework that makes it easy and simple to write cross-platform apps using web + technologies! It is functional, fast, and portable. Dioxus can run on the web, on the desktop, and + on mobile and embedded platforms." + } + div { class: "flex justify-center", + button { class: "inline-flex text-white bg-indigo-500 border-0 py-2 px-6 focus:outline-none hover:bg-indigo-600 rounded text-lg", + "Learn more" + } + button { class: "ml-4 inline-flex text-gray-400 bg-gray-800 border-0 py-2 px-6 focus:outline-none hover:bg-gray-700 hover:text-white rounded text-lg", + "Build an app" + } + } + } + div { class: "lg:max-w-lg lg:w-full md:w-1/2 w-5/6", + img { + class: "object-cover object-center rounded", + src: "https://i.imgur.com/oK6BLtw.png", + referrerpolicy: "no-referrer", + alt: "hero" + } + } + } + } + } + ) +} + +pub fn StacksIcon() -> Element { + rsx!( + svg { + fill: "none", + stroke: "currentColor", + stroke_linecap: "round", + stroke_linejoin: "round", + stroke_width: "2", + class: "w-10 h-10 text-white p-2 bg-indigo-500 rounded-full", + view_box: "0 0 24 24", + path { d: "M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5" } + } + ) +} + +pub fn RightArrowIcon() -> Element { + rsx!( + svg { + fill: "none", + stroke: "currentColor", + stroke_linecap: "round", + stroke_linejoin: "round", + stroke_width: "2", + class: "w-4 h-4 ml-1", + view_box: "0 0 24 24", + path { d: "M5 12h14M12 5l7 7-7 7" } + } + ) +} diff --git a/examples/tailwind/tailwind.config.js b/examples/tailwind/tailwind.config.js new file mode 100644 index 0000000000..2a69d5803a --- /dev/null +++ b/examples/tailwind/tailwind.config.js @@ -0,0 +1,9 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + mode: "all", + content: ["./src/**/*.{rs,html,css}", "./dist/**/*.html"], + theme: { + extend: {}, + }, + plugins: [], +}; diff --git a/examples/title.rs b/examples/title.rs index f836a7f3d6..ebe258963c 100644 --- a/examples/title.rs +++ b/examples/title.rs @@ -3,7 +3,8 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + tracing_subscriber::fmt::init(); + launch(app); } fn app() -> Element { @@ -13,7 +14,7 @@ fn app() -> Element { div { // You can set the title of the page with the Title component // In web applications, this sets the title in the head. On desktop, it sets the window title - document::Title { "My Application (Count {count})" } + Title { "My Application (Count {count})" } button { onclick: move |_| count += 1, "Up high!" } button { onclick: move |_| count -= 1, "Down low!" } } diff --git a/examples/todomvc.rs b/examples/todomvc.rs index a95fda0e2d..ea2e528a12 100644 --- a/examples/todomvc.rs +++ b/examples/todomvc.rs @@ -3,10 +3,10 @@ use dioxus::prelude::*; use std::collections::HashMap; -const STYLE: Asset = asset!("/examples/assets/todomvc.css"); +const STYLE: &str = asset!("./examples/assets/todomvc.css"); fn main() { - dioxus::launch(app); + launch(app); } #[derive(PartialEq, Eq, Clone, Copy)] @@ -63,7 +63,7 @@ fn app() -> Element { }; rsx! { - document::Stylesheet { href: STYLE } + head::Link { rel: "stylesheet", href: STYLE } section { class: "todoapp", TodoHeader { todos } section { class: "main", diff --git a/examples/weather_app.rs b/examples/weather_app.rs index 7b56bbbe54..395fd81f19 100644 --- a/examples/weather_app.rs +++ b/examples/weather_app.rs @@ -4,7 +4,7 @@ use dioxus::prelude::*; use serde::{Deserialize, Serialize}; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { @@ -19,7 +19,7 @@ fn app() -> Element { let current_weather = use_resource(move || async move { get_weather(&country()).await }); rsx! { - document::Stylesheet { href: "https://unpkg.com/tailwindcss@^2.0/dist/tailwind.min.css" } + head::Link { rel: "stylesheet", href: "https://unpkg.com/tailwindcss@^2.0/dist/tailwind.min.css" } div { class: "mx-auto p-4 bg-gray-100 h-screen flex justify-center", div { class: "flex items-center justify-center flex-row", div { class: "flex items-start justify-center flex-row", diff --git a/examples/web_component.rs b/examples/web_component.rs index a5f831912b..df0515397b 100644 --- a/examples/web_component.rs +++ b/examples/web_component.rs @@ -7,7 +7,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/examples/window_event.rs b/examples/window_event.rs index ca126f6a1d..cb91d2a5b2 100644 --- a/examples/window_event.rs +++ b/examples/window_event.rs @@ -13,7 +13,7 @@ use dioxus::desktop::{window, Config, WindowBuilder}; use dioxus::prelude::*; fn main() { - dioxus::launch::builder() + LaunchBuilder::desktop() .with_cfg( Config::new().with_window( WindowBuilder::new() @@ -26,7 +26,7 @@ fn main() { fn app() -> Element { rsx!( - document::Link { href: "https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css", rel: "stylesheet" } + head::Link { href: "https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css", rel: "stylesheet" } Header {} div { class: "container mx-auto", div { class: "grid grid-cols-5", diff --git a/examples/window_focus.rs b/examples/window_focus.rs index 1213b3e994..447544077b 100644 --- a/examples/window_focus.rs +++ b/examples/window_focus.rs @@ -12,7 +12,7 @@ use dioxus::desktop::{Config, WindowCloseBehaviour}; use dioxus::prelude::*; fn main() { - dioxus::launch::builder() + LaunchBuilder::desktop() .with_cfg(Config::new().with_close_behaviour(WindowCloseBehaviour::CloseWindow)) .launch(app) } diff --git a/examples/window_zoom.rs b/examples/window_zoom.rs index 35d25f4fde..8b10d503b0 100644 --- a/examples/window_zoom.rs +++ b/examples/window_zoom.rs @@ -5,7 +5,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch_desktop(app); } fn app() -> Element { diff --git a/examples/xss_safety.rs b/examples/xss_safety.rs index 904a42b07b..cb8d414dd7 100644 --- a/examples/xss_safety.rs +++ b/examples/xss_safety.rs @@ -5,7 +5,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { From 61ca1b29f38f11f9bc7cc27349062f2b132ffa89 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Sun, 15 Sep 2024 18:25:27 -0700 Subject: [PATCH 112/139] no need to change .cargo --- .cargo/config.toml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.cargo/config.toml b/.cargo/config.toml index 96d030fa19..81a637782a 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,3 +1,9 @@ +# All of these variables are used in the `openid_connect_demo` example, they are set here for the CI to work, they are set here because as stated here for now: `https://doc.rust-lang.org/cargo/reference/config.html` the .cargo/config.toml of the inner workspaces are not read when being invoked from the root workspace. +[env] +DIOXUS_FRONT_ISSUER_URL = "" +DIOXUS_FRONT_CLIENT_ID = "" +DIOXUS_FRONT_URL = "" + [profile] [profile.dioxus-client] From 0b1e13bd5552d6dee7e02645cdd28a5cb3e18fa7 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Sun, 15 Sep 2024 18:28:21 -0700 Subject: [PATCH 113/139] small cleanup, revert some changes --- .cargo/config.toml | 6 ------ packages/lazy-js-bundle/src/lib.rs | 24 ------------------------ 2 files changed, 30 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 81a637782a..96d030fa19 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,9 +1,3 @@ -# All of these variables are used in the `openid_connect_demo` example, they are set here for the CI to work, they are set here because as stated here for now: `https://doc.rust-lang.org/cargo/reference/config.html` the .cargo/config.toml of the inner workspaces are not read when being invoked from the root workspace. -[env] -DIOXUS_FRONT_ISSUER_URL = "" -DIOXUS_FRONT_CLIENT_ID = "" -DIOXUS_FRONT_URL = "" - [profile] [profile.dioxus-client] diff --git a/packages/lazy-js-bundle/src/lib.rs b/packages/lazy-js-bundle/src/lib.rs index 05ff0f8ba4..79d1b2d75a 100644 --- a/packages/lazy-js-bundle/src/lib.rs +++ b/packages/lazy-js-bundle/src/lib.rs @@ -79,30 +79,6 @@ impl LazyTypeScriptBindings { // Compute the hash of the input files let hashes = hash_files(watching_paths); - // Try to find a common prefix for the output files and put the hash in there otherwise, write it to src/binding_hash.txt - // let mut hash_location: Option = None; - // for path in &self.binding { - // match hash_location { - // Some(current_hash_location) => { - // let mut common_path = PathBuf::new(); - // for component in path - // .output_path - // .components() - // .zip(current_hash_location.components()) - // { - // if component.0 != component.1 { - // break; - // } - // common_path.push(component.0); - // } - // hash_location = - // (common_path.components().next().is_some()).then_some(common_path); - // } - // None => { - // hash_location = Some(path.output_path.clone()); - // } - // }; - // } let hash_location = None; let hash_location = hash_location.unwrap_or_else(|| PathBuf::from("./src/js/")); std::fs::create_dir_all(&hash_location).unwrap_or_else(|err| { From 53116939a34a754c26e79d02cc98a1e546b8f21b Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Sun, 15 Sep 2024 18:32:16 -0700 Subject: [PATCH 114/139] undo launch changes --- Cargo.lock | 11678 ---------------- Cargo.toml | 23 +- packages/fullstack/src/axum_adapter.rs | 2 +- .../playwright-tests/fullstack/src/main.rs | 2 +- .../nested-suspense/src/main.rs | 2 +- .../static-generation/src/main.rs | 2 +- .../suspense-carousel/src/main.rs | 2 +- packages/playwright-tests/web/src/main.rs | 2 +- packages/router/examples/manual.rs | 2 +- packages/signals/examples/context.rs | 2 +- packages/signals/examples/dependencies.rs | 2 +- packages/signals/examples/map_signal.rs | 2 +- .../signals/examples/read_only_degrade.rs | 2 +- packages/signals/examples/selector.rs | 2 +- packages/signals/examples/send.rs | 2 +- .../signals/examples/split_subscriptions.rs | 2 +- .../examples/router/src/main.rs | 2 +- .../examples/simple/src/main.rs | 2 +- 18 files changed, 24 insertions(+), 11709 deletions(-) delete mode 100644 Cargo.lock diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index aeb4be98bd..0000000000 --- a/Cargo.lock +++ /dev/null @@ -1,11678 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "DioxusApp" -version = "0.1.0" -dependencies = [ - "dioxus", - "reqwest", - "serde", - "tracing", -] - -[[package]] -name = "addr2line" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" -dependencies = [ - "gimli 0.31.0", -] - -[[package]] -name = "adler" -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 = "aead" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" -dependencies = [ - "crypto-common", - "generic-array 0.14.7", -] - -[[package]] -name = "aes" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", -] - -[[package]] -name = "aes-gcm" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" -dependencies = [ - "aead", - "aes", - "cipher", - "ctr", - "ghash", - "subtle", -] - -[[package]] -name = "ahash" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" -dependencies = [ - "getrandom 0.2.15", - "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", - "const-random", - "getrandom 0.2.15", - "once_cell", - "version_check", - "zerocopy", -] - -[[package]] -name = "aho-corasick" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] - -[[package]] -name = "aligned" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "377e4c0ba83e4431b10df45c1d4666f178ea9c552cac93e60c3a88bf32785923" -dependencies = [ - "as-slice", -] - -[[package]] -name = "alloc-no-stdlib" -version = "2.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" - -[[package]] -name = "alloc-stdlib" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" -dependencies = [ - "alloc-no-stdlib", -] - -[[package]] -name = "allocator-api2" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" - -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "anes" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" - -[[package]] -name = "ansi-to-html" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d73c455ae09fa2223a75114789f30ad605e9e297f79537953523366c05995f5f" -dependencies = [ - "regex", - "thiserror", -] - -[[package]] -name = "ansi-to-tui" -version = "5.0.0-rc.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "428c2992b874104caf39204b05bf89eab4ceefdd4fcb26caa6759906f547f8e8" -dependencies = [ - "nom", - "ratatui", - "simdutf8", - "smallvec", - "thiserror", -] - -[[package]] -name = "anstream" -version = "0.6.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" - -[[package]] -name = "anstyle-parse" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" -dependencies = [ - "windows-sys 0.52.0", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" -dependencies = [ - "anstyle", - "windows-sys 0.52.0", -] - -[[package]] -name = "anyhow" -version = "1.0.88" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e1496f8fb1fbf272686b8d37f523dab3e4a7443300055e74cdaa449f3114356" - -[[package]] -name = "anymap2" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c" - -[[package]] -name = "ar" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d67af77d68a931ecd5cbd8a3b5987d63a1d1d1278f7f6a60ae33db485cdebb69" - -[[package]] -name = "arc-swap" -version = "1.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" - -[[package]] -name = "arrayvec" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" - -[[package]] -name = "as-slice" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516" -dependencies = [ - "stable_deref_trait", -] - -[[package]] -name = "ashpd" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd884d7c72877a94102c3715f3b1cd09ff4fac28221add3e57cfbe25c236d093" -dependencies = [ - "enumflags2", - "futures-channel", - "futures-util", - "rand 0.8.5", - "serde", - "serde_repr", - "tokio", - "url", - "zbus", -] - -[[package]] -name = "askama_escape" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341" - -[[package]] -name = "async-broadcast" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20cd0e2e25ea8e5f7e9df04578dc6cf5c83577fd09b1a46aaf5c85e1c33f2a7e" -dependencies = [ - "event-listener 5.3.1", - "event-listener-strategy", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-channel" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" -dependencies = [ - "concurrent-queue", - "event-listener 2.5.3", - "futures-core", -] - -[[package]] -name = "async-channel" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" -dependencies = [ - "concurrent-queue", - "event-listener-strategy", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-compression" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fec134f64e2bc57411226dfc4e52dec859ddfc7e711fc5e07b612584f000e4aa" -dependencies = [ - "brotli", - "flate2", - "futures-core", - "memchr", - "pin-project-lite", - "tokio", - "zstd 0.13.2", - "zstd-safe 7.2.1", -] - -[[package]] -name = "async-executor" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" -dependencies = [ - "async-task", - "concurrent-queue", - "fastrand", - "futures-lite", - "slab", -] - -[[package]] -name = "async-global-executor" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" -dependencies = [ - "async-channel 2.3.1", - "async-executor", - "async-io", - "async-lock", - "blocking", - "futures-lite", - "once_cell", -] - -[[package]] -name = "async-io" -version = "2.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "444b0228950ee6501b3568d3c93bf1176a1fdbc3b758dcd9475046d30f4dc7e8" -dependencies = [ - "async-lock", - "cfg-if", - "concurrent-queue", - "futures-io", - "futures-lite", - "parking", - "polling", - "rustix", - "slab", - "tracing", - "windows-sys 0.59.0", -] - -[[package]] -name = "async-lock" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" -dependencies = [ - "event-listener 5.3.1", - "event-listener-strategy", - "pin-project-lite", -] - -[[package]] -name = "async-process" -version = "2.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8a07789659a4d385b79b18b9127fc27e1a59e1e89117c78c5ea3b806f016374" -dependencies = [ - "async-channel 2.3.1", - "async-io", - "async-lock", - "async-signal", - "async-task", - "blocking", - "cfg-if", - "event-listener 5.3.1", - "futures-lite", - "rustix", - "tracing", - "windows-sys 0.59.0", -] - -[[package]] -name = "async-recursion" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "async-signal" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3" -dependencies = [ - "async-io", - "async-lock", - "atomic-waker", - "cfg-if", - "futures-core", - "futures-io", - "rustix", - "signal-hook-registry", - "slab", - "windows-sys 0.59.0", -] - -[[package]] -name = "async-std" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c634475f29802fde2b8f0b505b1bd00dfe4df7d4a000f0b36f7671197d5c3615" -dependencies = [ - "async-channel 1.9.0", - "async-global-executor", - "async-io", - "async-lock", - "crossbeam-utils", - "futures-channel", - "futures-core", - "futures-io", - "futures-lite", - "gloo-timers 0.3.0", - "kv-log-macro", - "log", - "memchr", - "once_cell", - "pin-project-lite", - "pin-utils", - "slab", - "wasm-bindgen-futures", -] - -[[package]] -name = "async-stream" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" -dependencies = [ - "async-stream-impl", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "async-task" -version = "4.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" - -[[package]] -name = "async-trait" -version = "0.1.82" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "atk" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4af014b17dd80e8af9fa689b2d4a211ddba6eb583c1622f35d0cb543f6b17e4" -dependencies = [ - "atk-sys", - "glib", - "libc", -] - -[[package]] -name = "atk-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "251e0b7d90e33e0ba930891a505a9a35ece37b2dd37a14f3ffc306c13b980009" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "atoi" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" -dependencies = [ - "num-traits", -] - -[[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - -[[package]] -name = "auth-git2" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51bd0e4592409df8631ca807716dc1e5caafae5d01ce0157c966c71c7e49c3c" -dependencies = [ - "dirs", - "git2", - "terminal-prompt", -] - -[[package]] -name = "autocfg" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" - -[[package]] -name = "aws-lc-rs" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f95446d919226d587817a7d21379e6eb099b97b45110a7f272a444ca5c54070" -dependencies = [ - "aws-lc-sys", - "mirai-annotations", - "paste", - "untrusted 0.7.1", - "zeroize", -] - -[[package]] -name = "aws-lc-sys" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234314bd569802ec87011d653d6815c6d7b9ffb969e9fee5b8b20ef860e8dce9" -dependencies = [ - "bindgen", - "cc", - "cmake", - "dunce", - "fs_extra", - "libc", - "paste", -] - -[[package]] -name = "axum" -version = "0.6.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" -dependencies = [ - "async-trait", - "axum-core 0.3.4", - "bitflags 1.3.2", - "bytes", - "futures-util", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.30", - "itoa 1.0.11", - "matchit", - "memchr", - "mime", - "percent-encoding", - "pin-project-lite", - "rustversion", - "serde", - "sync_wrapper 0.1.2", - "tower", - "tower-layer", - "tower-service", -] - -[[package]] -name = "axum" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" -dependencies = [ - "async-trait", - "axum-core 0.4.3", - "axum-macros", - "base64 0.21.7", - "bytes", - "futures-util", - "http 1.1.0", - "http-body 1.0.1", - "http-body-util", - "hyper 1.4.1", - "hyper-util", - "itoa 1.0.11", - "matchit", - "memchr", - "mime", - "multer", - "percent-encoding", - "pin-project-lite", - "rustversion", - "serde", - "serde_json", - "serde_path_to_error", - "serde_urlencoded", - "sha1", - "sync_wrapper 1.0.1", - "tokio", - "tokio-tungstenite", - "tower", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "axum-core" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" -dependencies = [ - "async-trait", - "bytes", - "futures-util", - "http 0.2.12", - "http-body 0.4.6", - "mime", - "rustversion", - "tower-layer", - "tower-service", -] - -[[package]] -name = "axum-core" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" -dependencies = [ - "async-trait", - "bytes", - "futures-util", - "http 1.1.0", - "http-body 1.0.1", - "http-body-util", - "mime", - "pin-project-lite", - "rustversion", - "sync_wrapper 0.1.2", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "axum-extra" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0be6ea09c9b96cb5076af0de2e383bd2bc0c18f827cf1967bdd353e0b910d733" -dependencies = [ - "axum 0.7.5", - "axum-core 0.4.3", - "bytes", - "futures-util", - "headers", - "http 1.1.0", - "http-body 1.0.1", - "http-body-util", - "mime", - "pin-project-lite", - "serde", - "tower", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "axum-macros" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00c055ee2d014ae5981ce1016374e8213682aa14d9bf40e48ab48b5f3ef20eaa" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "axum-server" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56bac90848f6a9393ac03c63c640925c4b7c8ca21654de40d53f55964667c7d8" -dependencies = [ - "arc-swap", - "bytes", - "futures-util", - "http 1.1.0", - "http-body 1.0.1", - "http-body-util", - "hyper 1.4.1", - "hyper-util", - "pin-project-lite", - "rustls 0.23.13", - "rustls-pemfile 2.1.3", - "rustls-pki-types", - "tokio", - "tokio-rustls", - "tower", - "tower-service", -] - -[[package]] -name = "axum_session" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0f0035d1d5a70ac279e80a75e60592023ba8b327c929c3827f6c5ed749b9f8b" -dependencies = [ - "aes-gcm", - "async-trait", - "axum 0.7.5", - "base64 0.21.7", - "bytes", - "chrono", - "cookie", - "dashmap", - "forwarded-header-value", - "futures", - "hmac", - "http 1.1.0", - "http-body 1.0.1", - "rand 0.8.5", - "serde", - "serde_json", - "sha2", - "sqlx", - "thiserror", - "tokio", - "tower-layer", - "tower-service", - "tracing", - "uuid", -] - -[[package]] -name = "axum_session_auth" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd4c620e865c92d9fca3ad57efeb4baef271f3ccdba163928002f13489952660" -dependencies = [ - "anyhow", - "async-recursion", - "async-trait", - "axum-core 0.4.3", - "axum_session", - "bytes", - "chrono", - "dashmap", - "futures", - "http 1.1.0", - "http-body 1.0.1", - "serde", - "tokio", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "backtrace" -version = "0.3.74" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide 0.8.0", - "object", - "rustc-demangle", - "windows-targets 0.52.6", -] - -[[package]] -name = "base16ct" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" - -[[package]] -name = "base64" -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "base64ct" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" - -[[package]] -name = "bcder" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c627747a6774aab38beb35990d88309481378558875a41da1a4b2e373c906ef0" -dependencies = [ - "bytes", - "smallvec", -] - -[[package]] -name = "bigdecimal" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6773ddc0eafc0e509fb60e48dff7f450f8e674a0686ae8605e8d9901bd5eefa" -dependencies = [ - "num-bigint", - "num-integer", - "num-traits", -] - -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - -[[package]] -name = "bindgen" -version = "0.69.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" -dependencies = [ - "bitflags 2.6.0", - "cexpr", - "clang-sys", - "itertools 0.12.1", - "lazy_static", - "lazycell", - "log", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash 1.1.0", - "shlex", - "syn 2.0.77", - "which", -] - -[[package]] -name = "bit-vec" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" - -[[package]] -name = "bit_field" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" - -[[package]] -name = "bitfield" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d7e60934ceec538daadb9d8432424ed043a904d8e0243f3c6446bce549a46ac" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" -dependencies = [ - "serde", -] - -[[package]] -name = "bitness" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57792b99d555ebf109c83169228076f7d997e2b37ba1a653850ccd703ac7bab0" -dependencies = [ - "sysctl", - "thiserror", - "uname", - "winapi", -] - -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - -[[package]] -name = "block" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array 0.14.7", -] - -[[package]] -name = "block-padding" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" -dependencies = [ - "generic-array 0.14.7", -] - -[[package]] -name = "block2" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" -dependencies = [ - "objc2", -] - -[[package]] -name = "blocking" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" -dependencies = [ - "async-channel 2.3.1", - "async-task", - "futures-io", - "futures-lite", - "piper", -] - -[[package]] -name = "blowfish" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7" -dependencies = [ - "byteorder", - "cipher", -] - -[[package]] -name = "borsh" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6362ed55def622cddc70a4746a68554d7b687713770de539e59a739b249f8ed" -dependencies = [ - "borsh-derive", - "cfg_aliases 0.2.1", -] - -[[package]] -name = "borsh-derive" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3ef8005764f53cd4dca619f5bf64cafd4664dada50ece25e4d81de54c80cc0b" -dependencies = [ - "once_cell", - "proc-macro-crate 3.2.0", - "proc-macro2", - "quote", - "syn 2.0.77", - "syn_derive", -] - -[[package]] -name = "brotli" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", - "brotli-decompressor", -] - -[[package]] -name = "brotli-decompressor" -version = "4.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", -] - -[[package]] -name = "bstr" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" -dependencies = [ - "memchr", - "regex-automata 0.4.7", - "serde", -] - -[[package]] -name = "buffer-redux" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e8acf87c5b9f5897cd3ebb9a327f420e0cae9dd4e5c1d2e36f2c84c571a58f1" -dependencies = [ - "memchr", -] - -[[package]] -name = "built" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "236e6289eda5a812bc6b53c3b024039382a2895fbbeef2d748b2931546d392c4" -dependencies = [ - "git2", -] - -[[package]] -name = "bumpalo" -version = "3.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" - -[[package]] -name = "bytecheck" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" -dependencies = [ - "bytecheck_derive", - "ptr_meta", - "simdutf8", -] - -[[package]] -name = "bytecheck_derive" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "bytemuck" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "bytes" -version = "1.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" - -[[package]] -name = "bzip2" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" -dependencies = [ - "bzip2-sys", - "libc", -] - -[[package]] -name = "bzip2-sys" -version = "0.1.11+1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - -[[package]] -name = "cairo-rs" -version = "0.18.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" -dependencies = [ - "bitflags 2.6.0", - "cairo-sys-rs", - "glib", - "libc", - "once_cell", - "thiserror", -] - -[[package]] -name = "cairo-sys-rs" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" -dependencies = [ - "glib-sys", - "libc", - "system-deps", -] - -[[package]] -name = "camellia" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3264e2574e9ef2b53ce6f536dea83a69ac0bc600b762d1523ff83fe07230ce30" -dependencies = [ - "byteorder", - "cipher", -] - -[[package]] -name = "camino" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo-config2" -version = "0.1.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1124054becb9262cc15c5e96e82f0d782f2aed3a3034d1f71a6385a6fa9e9595" -dependencies = [ - "home", - "serde", - "serde_derive", - "toml_edit 0.22.20", -] - -[[package]] -name = "cargo-generate" -version = "0.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b611d852b731eaaf84d3dfed2cefc1468f772b403ae0499fd3436a6a2b42b273" -dependencies = [ - "anstyle", - "anyhow", - "auth-git2", - "clap", - "console", - "dialoguer", - "env_logger", - "fs-err", - "git2", - "gix-config", - "heck 0.5.0", - "home", - "ignore", - "indexmap 2.5.0", - "indicatif", - "liquid", - "liquid-core", - "liquid-derive", - "liquid-lib", - "log", - "names", - "paste", - "path-absolutize", - "regex", - "remove_dir_all", - "rhai", - "sanitize-filename", - "semver", - "serde", - "tempfile", - "thiserror", - "time", - "toml", - "walkdir", -] - -[[package]] -name = "cargo-platform" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo_metadata" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" -dependencies = [ - "camino", - "cargo-platform", - "semver", - "serde", - "serde_json", - "thiserror", -] - -[[package]] -name = "cargo_toml" -version = "0.20.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad639525b1c67b6a298f378417b060fbc04618bea559482a8484381cce27d965" -dependencies = [ - "serde", - "toml", -] - -[[package]] -name = "cassowary" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" - -[[package]] -name = "cast" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" - -[[package]] -name = "cast5" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b07d673db1ccf000e90f54b819db9e75a8348d6eb056e9b8ab53231b7a9911" -dependencies = [ - "cipher", -] - -[[package]] -name = "castaway" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5" -dependencies = [ - "rustversion", -] - -[[package]] -name = "cc" -version = "1.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62ac837cdb5cb22e10a256099b4fc502b1dfe560cb282963a974d7abd80e476" -dependencies = [ - "jobserver", - "libc", - "shlex", -] - -[[package]] -name = "cesu8" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" - -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] - -[[package]] -name = "cfb" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" -dependencies = [ - "byteorder", - "fnv", - "uuid", -] - -[[package]] -name = "cfb-mode" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "738b8d467867f80a71351933f70461f5b56f24d5c93e0cf216e59229c968d330" -dependencies = [ - "cipher", -] - -[[package]] -name = "cfg-expr" -version = "0.15.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" -dependencies = [ - "smallvec", - "target-lexicon", -] - -[[package]] -name = "cfg-expr" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "345c78335be0624ed29012dc10c49102196c6882c12dde65d9f35b02da2aada8" -dependencies = [ - "smallvec", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "cfg_aliases" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" - -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - -[[package]] -name = "chrono" -version = "0.4.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "js-sys", - "num-traits", - "serde", - "wasm-bindgen", - "windows-targets 0.52.6", -] - -[[package]] -name = "ciborium" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" -dependencies = [ - "ciborium-io", - "ciborium-ll", - "serde", -] - -[[package]] -name = "ciborium-io" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" - -[[package]] -name = "ciborium-ll" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" -dependencies = [ - "ciborium-io", - "half", -] - -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", -] - -[[package]] -name = "clang-sys" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" -dependencies = [ - "glob", - "libc", - "libloading", -] - -[[package]] -name = "clap" -version = "4.5.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac" -dependencies = [ - "clap_builder", - "clap_derive", -] - -[[package]] -name = "clap_builder" -version = "4.5.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim 0.11.1", - "terminal_size", -] - -[[package]] -name = "clap_derive" -version = "4.5.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "clap_lex" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" - -[[package]] -name = "cmake" -version = "0.1.51" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb1e43aa7fd152b1f968787f7dbcdeb306d1867ff373c69955211876c053f91a" -dependencies = [ - "cc", -] - -[[package]] -name = "cocoa" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f79398230a6e2c08f5c9760610eb6924b52aa9e7950a619602baba59dcbbdbb2" -dependencies = [ - "bitflags 2.6.0", - "block", - "cocoa-foundation", - "core-foundation 0.10.0", - "core-graphics", - "foreign-types 0.5.0", - "libc", - "objc", -] - -[[package]] -name = "cocoa-foundation" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14045fb83be07b5acf1c0884b2180461635b433455fa35d1cd6f17f1450679d" -dependencies = [ - "bitflags 2.6.0", - "block", - "core-foundation 0.10.0", - "core-graphics-types", - "libc", - "objc", -] - -[[package]] -name = "codespan-reporting" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" -dependencies = [ - "termcolor", - "unicode-width", -] - -[[package]] -name = "color_quant" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" - -[[package]] -name = "colorchoice" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" - -[[package]] -name = "colored" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" -dependencies = [ - "lazy_static", - "windows-sys 0.48.0", -] - -[[package]] -name = "combine" -version = "4.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" -dependencies = [ - "bytes", - "memchr", -] - -[[package]] -name = "compact_str" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f" -dependencies = [ - "castaway", - "cfg-if", - "itoa 1.0.11", - "ryu", - "static_assertions", -] - -[[package]] -name = "concurrent-queue" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "console" -version = "0.15.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" -dependencies = [ - "encode_unicode", - "lazy_static", - "libc", - "unicode-width", - "windows-sys 0.52.0", -] - -[[package]] -name = "console-api" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a257c22cd7e487dd4a13d413beabc512c5052f0bc048db0da6a84c3d8a6142fd" -dependencies = [ - "futures-core", - "prost", - "prost-types", - "tonic", - "tracing-core", -] - -[[package]] -name = "console-subscriber" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c4cc54bae66f7d9188996404abdf7fdfa23034ef8e43478c8810828abad758" -dependencies = [ - "console-api", - "crossbeam-channel", - "crossbeam-utils", - "futures-task", - "hdrhistogram", - "humantime", - "prost", - "prost-types", - "serde", - "serde_json", - "thread_local", - "tokio", - "tokio-stream", - "tonic", - "tracing", - "tracing-core", - "tracing-subscriber", -] - -[[package]] -name = "console_error_panic_hook" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" -dependencies = [ - "cfg-if", - "wasm-bindgen", -] - -[[package]] -name = "const-oid" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" - -[[package]] -name = "const-random" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" -dependencies = [ - "const-random-macro", -] - -[[package]] -name = "const-random-macro" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" -dependencies = [ - "getrandom 0.2.15", - "once_cell", - "tiny-keccak", -] - -[[package]] -name = "const_format" -version = "0.2.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50c655d81ff1114fb0dcdea9225ea9f0cc712a6f8d189378e82bdf62a473a64b" -dependencies = [ - "const_format_proc_macros", -] - -[[package]] -name = "const_format_proc_macros" -version = "0.2.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eff1a44b93f47b1bac19a27932f5c591e43d1ba357ee4f61526c8a25603f0eb1" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - -[[package]] -name = "constant_time_eq" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" - -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - -[[package]] -name = "convert_case" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "cookie" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" -dependencies = [ - "aes-gcm", - "base64 0.22.1", - "percent-encoding", - "rand 0.8.5", - "subtle", - "time", - "version_check", -] - -[[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.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" -dependencies = [ - "bitflags 2.6.0", - "core-foundation 0.10.0", - "core-graphics-types", - "foreign-types 0.5.0", - "libc", -] - -[[package]] -name = "core-graphics-types" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" -dependencies = [ - "bitflags 2.6.0", - "core-foundation 0.10.0", - "libc", -] - -[[package]] -name = "cpio" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27e77cfc4543efb4837662cb7cd53464ae66f0fd5c708d71e0f338b1c11d62d3" - -[[package]] -name = "cpufeatures" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" -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 = "crc24" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd121741cf3eb82c08dd3023eb55bf2665e5f60ec20f89760cf836ae4562e6a0" - -[[package]] -name = "crc32fast" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "criterion" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" -dependencies = [ - "anes", - "cast", - "ciborium", - "clap", - "criterion-plot", - "futures", - "is-terminal", - "itertools 0.10.5", - "num-traits", - "once_cell", - "oorandom", - "plotters", - "rayon", - "regex", - "serde", - "serde_derive", - "serde_json", - "tinytemplate", - "tokio", - "walkdir", -] - -[[package]] -name = "criterion-plot" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" -dependencies = [ - "cast", - "itertools 0.10.5", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" -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-queue" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" - -[[package]] -name = "crossterm" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" -dependencies = [ - "bitflags 2.6.0", - "crossterm_winapi", - "futures-core", - "libc", - "mio 0.8.11", - "parking_lot", - "signal-hook", - "signal-hook-mio", - "winapi", -] - -[[package]] -name = "crossterm_winapi" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" -dependencies = [ - "winapi", -] - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[package]] -name = "crypto-bigint" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" -dependencies = [ - "generic-array 0.14.7", - "rand_core 0.6.4", - "subtle", - "zeroize", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array 0.14.7", - "rand_core 0.6.4", - "typenum", -] - -[[package]] -name = "cssparser" -version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a" -dependencies = [ - "cssparser-macros", - "dtoa-short", - "itoa 0.4.8", - "matches", - "phf 0.8.0", - "proc-macro2", - "quote", - "smallvec", - "syn 1.0.109", -] - -[[package]] -name = "cssparser-macros" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" -dependencies = [ - "quote", - "syn 2.0.77", -] - -[[package]] -name = "ctor" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f" -dependencies = [ - "quote", - "syn 2.0.77", -] - -[[package]] -name = "ctr" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" -dependencies = [ - "cipher", -] - -[[package]] -name = "ctrlc" -version = "3.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90eeab0aa92f3f9b4e87f258c72b139c207d251f9cbc1080a0086b86a8870dd3" -dependencies = [ - "nix 0.29.0", - "windows-sys 0.59.0", -] - -[[package]] -name = "curve25519-dalek" -version = "4.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" -dependencies = [ - "cfg-if", - "cpufeatures", - "curve25519-dalek-derive", - "digest", - "fiat-crypto", - "rustc_version", - "subtle", - "zeroize", -] - -[[package]] -name = "curve25519-dalek-derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "cvt" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ae9bf77fbf2d39ef573205d554d87e86c12f1994e9ea335b0651b9b278bcf1" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "cxx" -version = "1.0.128" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54ccead7d199d584d139148b04b4a368d1ec7556a1d9ea2548febb1b9d49f9a4" -dependencies = [ - "cc", - "cxxbridge-flags", - "cxxbridge-macro", - "link-cplusplus", -] - -[[package]] -name = "cxx-build" -version = "1.0.128" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77953e99f01508f89f55c494bfa867171ef3a6c8cea03d26975368f2121a5c1" -dependencies = [ - "cc", - "codespan-reporting", - "once_cell", - "proc-macro2", - "quote", - "scratch", - "syn 2.0.77", -] - -[[package]] -name = "cxxbridge-flags" -version = "1.0.128" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65777e06cc48f0cb0152024c77d6cf9e4bdb4408e7b48bea993d42fa0f5b02b6" - -[[package]] -name = "cxxbridge-macro" -version = "1.0.128" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98532a60dedaebc4848cb2cba5023337cc9ea3af16a5b062633fabfd9f18fb60" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "darling" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" -dependencies = [ - "darling_core 0.14.4", - "darling_macro 0.14.4", -] - -[[package]] -name = "darling" -version = "0.20.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" -dependencies = [ - "darling_core 0.20.10", - "darling_macro 0.20.10", -] - -[[package]] -name = "darling_core" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim 0.10.0", - "syn 1.0.109", -] - -[[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 0.11.1", - "syn 2.0.77", -] - -[[package]] -name = "darling_macro" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" -dependencies = [ - "darling_core 0.14.4", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "darling_macro" -version = "0.20.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" -dependencies = [ - "darling_core 0.20.10", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "dashmap" -version = "5.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" -dependencies = [ - "cfg-if", - "hashbrown 0.14.5", - "lock_api", - "once_cell", - "parking_lot_core", -] - -[[package]] -name = "data-encoding" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" - -[[package]] -name = "der" -version = "0.7.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" -dependencies = [ - "const-oid", - "pem-rfc7468", - "zeroize", -] - -[[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_builder" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d67778784b508018359cbc8696edb3db78160bab2c2a28ba7f56ef6932997f8" -dependencies = [ - "derive_builder_macro", -] - -[[package]] -name = "derive_builder_core" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c11bdc11a0c47bc7d37d582b5285da6849c96681023680b906673c5707af7b0f" -dependencies = [ - "darling 0.14.4", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "derive_builder_macro" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebcda35c7a396850a55ffeac740804b40ffec779b98fffbb1738f4033f0ee79e" -dependencies = [ - "derive_builder_core", - "syn 1.0.109", -] - -[[package]] -name = "derive_more" -version = "0.99.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" -dependencies = [ - "convert_case 0.4.0", - "proc-macro2", - "quote", - "rustc_version", - "syn 2.0.77", -] - -[[package]] -name = "des" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffdd80ce8ce993de27e9f063a444a4d53ce8e8db4c1f00cc03af5ad5a9867a1e" -dependencies = [ - "cipher", -] - -[[package]] -name = "dialoguer" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658bce805d770f407bc62102fca7c2c64ceef2fbcb2b8bd19d2765ce093980de" -dependencies = [ - "console", - "shell-words", - "tempfile", - "thiserror", - "zeroize", -] - -[[package]] -name = "diff" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "const-oid", - "crypto-common", - "subtle", -] - -[[package]] -name = "dioxus" -version = "0.6.0-alpha.2" -dependencies = [ - "axum 0.7.5", - "criterion", - "dioxus", - "dioxus-config-macro", - "dioxus-core", - "dioxus-core-macro", - "dioxus-core-types", - "dioxus-desktop", - "dioxus-devtools", - "dioxus-document", - "dioxus-fullstack", - "dioxus-hooks", - "dioxus-html", - "dioxus-isrg", - "dioxus-liveview", - "dioxus-mobile", - "dioxus-router", - "dioxus-signals", - "dioxus-ssr", - "dioxus-static-site-generation", - "dioxus-web", - "env_logger", - "futures-util", - "manganis", - "rand 0.8.5", - "serde", - "thiserror", - "tokio", - "tracing", -] - -[[package]] -name = "dioxus-autofmt" -version = "0.6.0-alpha.2" -dependencies = [ - "dioxus-rsx", - "pretty_assertions", - "prettyplease", - "proc-macro2", - "quote", - "serde", - "syn 2.0.77", -] - -[[package]] -name = "dioxus-check" -version = "0.6.0-alpha.2" -dependencies = [ - "indoc", - "owo-colors", - "pretty_assertions", - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "dioxus-cli" -version = "0.6.0-alpha.2" -dependencies = [ - "ansi-to-html", - "ansi-to-tui", - "anyhow", - "atty", - "axum 0.7.5", - "axum-extra", - "axum-server", - "brotli", - "built", - "cargo-config2", - "cargo-generate", - "cargo_metadata", - "cargo_toml", - "chrono", - "clap", - "console", - "console-subscriber", - "crossterm", - "ctrlc", - "dioxus-autofmt", - "dioxus-check", - "dioxus-core", - "dioxus-core-types", - "dioxus-devtools-types", - "dioxus-fullstack", - "dioxus-html", - "dioxus-rsx", - "dioxus-rsx-hotreload", - "dioxus-rsx-rosetta", - "dioxus-runtime-config", - "dirs", - "env_logger", - "flate2", - "fs_extra", - "futures-channel", - "futures-util", - "headers", - "html_parser", - "hyper 1.4.1", - "hyper-rustls", - "hyper-util", - "ignore", - "krates", - "manganis-core", - "notify", - "object", - "once_cell", - "open", - "prettyplease", - "proc-macro2", - "ratatui", - "rayon", - "regex", - "reqwest", - "rustls 0.23.13", - "serde", - "serde_json", - "subprocess", - "syn 2.0.77", - "tar", - "tauri-bundler", - "tempfile", - "thiserror", - "tokio", - "tokio-stream", - "tokio-util", - "toml", - "toml_edit 0.22.20", - "tower", - "tower-http", - "tracing", - "tracing-subscriber", - "uuid", - "walkdir", - "wasm-bindgen-cli-support", - "wasm-bindgen-shared", - "wasm-opt", - "zip", -] - -[[package]] -name = "dioxus-config-macro" -version = "0.6.0-alpha.2" -dependencies = [ - "proc-macro2", - "quote", -] - -[[package]] -name = "dioxus-core" -version = "0.6.0-alpha.2" -dependencies = [ - "const_format", - "dioxus", - "dioxus-core-types", - "dioxus-html", - "dioxus-ssr", - "futures-channel", - "futures-util", - "generational-box", - "longest-increasing-subsequence", - "pretty_assertions", - "rand 0.8.5", - "reqwest", - "rustc-hash 1.1.0", - "rustversion", - "serde", - "slab", - "slotmap", - "tokio", - "tracing", - "tracing-fluent-assertions", - "tracing-subscriber", - "warnings", - "web-sys", -] - -[[package]] -name = "dioxus-core-macro" -version = "0.6.0-alpha.2" -dependencies = [ - "convert_case 0.6.0", - "dioxus", - "dioxus-html", - "dioxus-rsx", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.77", - "tokio", - "trybuild", -] - -[[package]] -name = "dioxus-core-types" -version = "0.6.0-alpha.2" - -[[package]] -name = "dioxus-desktop" -version = "0.6.0-alpha.2" -dependencies = [ - "async-trait", - "cocoa", - "core-foundation 0.10.0", - "dioxus", - "dioxus-core", - "dioxus-devtools", - "dioxus-document", - "dioxus-hooks", - "dioxus-html", - "dioxus-interpreter-js", - "dioxus-runtime-config", - "dioxus-signals", - "dioxus-ssr", - "dunce", - "exitcode", - "futures-channel", - "futures-util", - "generational-box", - "global-hotkey", - "http-range", - "infer", - "muda", - "objc", - "objc_id", - "rand 0.8.5", - "reqwest", - "rfd", - "rustc-hash 1.1.0", - "separator", - "serde", - "serde_json", - "signal-hook", - "slab", - "tao", - "thiserror", - "tokio", - "tracing", - "urlencoding", - "webbrowser", - "wry", -] - -[[package]] -name = "dioxus-devtools" -version = "0.6.0-alpha.2" -dependencies = [ - "dioxus-core", - "dioxus-devtools-types", - "dioxus-signals", - "serde", - "serde_json", - "tokio", - "tracing", - "tungstenite 0.23.0", - "warnings", -] - -[[package]] -name = "dioxus-devtools-types" -version = "0.6.0-alpha.2" -dependencies = [ - "dioxus-core", - "serde", -] - -[[package]] -name = "dioxus-document" -version = "0.6.0-alpha.2" -dependencies = [ - "dioxus-core", - "dioxus-core-macro", - "dioxus-core-types", - "futures-channel", - "serde", - "serde_json", - "tracing", -] - -[[package]] -name = "dioxus-examples" -version = "0.6.0-alpha.2" -dependencies = [ - "async-std", - "base64 0.22.1", - "ciborium", - "dioxus", - "dioxus-ssr", - "form_urlencoded", - "futures-util", - "getrandom 0.2.15", - "http-range", - "rand 0.8.5", - "reqwest", - "separator", - "serde", - "serde_json", - "tokio", - "web-time", -] - -[[package]] -name = "dioxus-ext" -version = "0.6.0-alpha.2" -dependencies = [ - "dioxus-autofmt", - "dioxus-rsx-rosetta", - "html_parser", - "syn 2.0.77", - "wasm-bindgen", -] - -[[package]] -name = "dioxus-fullstack" -version = "0.6.0-alpha.2" -dependencies = [ - "async-trait", - "aws-lc-rs", - "axum 0.7.5", - "base64 0.22.1", - "bytes", - "ciborium", - "dioxus", - "dioxus-core-types", - "dioxus-desktop", - "dioxus-devtools", - "dioxus-document", - "dioxus-interpreter-js", - "dioxus-isrg", - "dioxus-lib", - "dioxus-mobile", - "dioxus-runtime-config", - "dioxus-ssr", - "dioxus-web", - "dioxus_server_macro", - "futures-channel", - "futures-util", - "generational-box", - "http 1.1.0", - "hyper 1.4.1", - "hyper-rustls", - "once_cell", - "parking_lot", - "pin-project", - "rustls 0.23.13", - "serde", - "server_fn", - "thiserror", - "tokio", - "tokio-stream", - "tokio-util", - "tower", - "tower-http", - "tower-layer", - "tracing", - "tracing-futures", - "web-sys", -] - -[[package]] -name = "dioxus-hooks" -version = "0.6.0-alpha.2" -dependencies = [ - "dioxus", - "dioxus-core", - "dioxus-signals", - "futures-channel", - "futures-util", - "generational-box", - "reqwest", - "rustversion", - "slab", - "tokio", - "tracing", - "warnings", - "web-sys", -] - -[[package]] -name = "dioxus-html" -version = "0.6.0-alpha.2" -dependencies = [ - "async-trait", - "dioxus", - "dioxus-core", - "dioxus-core-types", - "dioxus-html-internal-macro", - "dioxus-rsx", - "dioxus-rsx-hotreload", - "dioxus-web", - "enumset", - "euclid", - "futures-channel", - "keyboard-types", - "lazy-js-bundle", - "manganis", - "serde", - "serde_json", - "serde_repr", - "tokio", - "tracing", -] - -[[package]] -name = "dioxus-html-internal-macro" -version = "0.6.0-alpha.2" -dependencies = [ - "convert_case 0.6.0", - "proc-macro2", - "quote", - "syn 2.0.77", - "trybuild", -] - -[[package]] -name = "dioxus-interpreter-js" -version = "0.6.0-alpha.2" -dependencies = [ - "dioxus-core", - "dioxus-core-types", - "js-sys", - "lazy-js-bundle", - "rustc-hash 1.1.0", - "serde", - "sledgehammer_bindgen", - "sledgehammer_utils", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "dioxus-isrg" -version = "0.6.0-alpha.2" -dependencies = [ - "chrono", - "http 1.1.0", - "lru", - "rustc-hash 1.1.0", - "thiserror", - "tracing", -] - -[[package]] -name = "dioxus-lib" -version = "0.6.0-alpha.2" -dependencies = [ - "dioxus", - "dioxus-config-macro", - "dioxus-core", - "dioxus-core-macro", - "dioxus-hooks", - "dioxus-html", - "dioxus-rsx", - "dioxus-signals", - "manganis", -] - -[[package]] -name = "dioxus-liveview" -version = "0.6.0-alpha.2" -dependencies = [ - "axum 0.7.5", - "dioxus", - "dioxus-core", - "dioxus-core-types", - "dioxus-devtools", - "dioxus-document", - "dioxus-html", - "dioxus-interpreter-js", - "dioxus-runtime-config", - "env_logger", - "futures-channel", - "futures-util", - "generational-box", - "lazy-js-bundle", - "rustc-hash 1.1.0", - "serde", - "serde_json", - "slab", - "thiserror", - "tokio", - "tokio-stream", - "tokio-util", - "tower", - "tracing", -] - -[[package]] -name = "dioxus-mobile" -version = "0.6.0-alpha.2" -dependencies = [ - "dioxus-desktop", -] - -[[package]] -name = "dioxus-playwright-fullstack-test" -version = "0.1.0" -dependencies = [ - "dioxus", - "serde", - "tokio", -] - -[[package]] -name = "dioxus-playwright-liveview-test" -version = "0.0.1" -dependencies = [ - "axum 0.7.5", - "dioxus", - "dioxus-liveview", - "tokio", -] - -[[package]] -name = "dioxus-playwright-static-generation-test" -version = "0.1.0" -dependencies = [ - "dioxus", - "serde", - "tokio", -] - -[[package]] -name = "dioxus-playwright-web-test" -version = "0.0.1" -dependencies = [ - "dioxus", - "serde_json", - "tracing", - "tracing-wasm", -] - -[[package]] -name = "dioxus-pwa-example" -version = "0.1.0" -dependencies = [ - "console_error_panic_hook", - "dioxus", - "log", - "wasm-logger", -] - -[[package]] -name = "dioxus-router" -version = "0.6.0-alpha.2" -dependencies = [ - "axum 0.7.5", - "base64 0.21.7", - "ciborium", - "console_error_panic_hook", - "criterion", - "dioxus", - "dioxus-document", - "dioxus-fullstack", - "dioxus-lib", - "dioxus-liveview", - "dioxus-router-macro", - "dioxus-ssr", - "gloo", - "gloo-utils 0.1.7", - "http 1.1.0", - "js-sys", - "rustversion", - "serde", - "serde_json", - "tokio", - "tracing", - "url", - "urlencoding", - "wasm-bindgen", - "wasm-bindgen-test", - "web-sys", -] - -[[package]] -name = "dioxus-router-macro" -version = "0.6.0-alpha.2" -dependencies = [ - "dioxus", - "proc-macro2", - "quote", - "slab", - "syn 2.0.77", -] - -[[package]] -name = "dioxus-rsx" -version = "0.6.0-alpha.2" -dependencies = [ - "prettier-please", - "prettyplease", - "proc-macro2", - "proc-macro2-diagnostics", - "quote", - "syn 2.0.77", - "tracing", -] - -[[package]] -name = "dioxus-rsx-hotreload" -version = "0.6.0-alpha.2" -dependencies = [ - "dioxus-core", - "dioxus-core-types", - "dioxus-rsx", - "internment", - "proc-macro2", - "proc-macro2-diagnostics", - "quote", - "syn 2.0.77", - "tracing", -] - -[[package]] -name = "dioxus-rsx-rosetta" -version = "0.6.0-alpha.2" -dependencies = [ - "convert_case 0.6.0", - "dioxus-autofmt", - "dioxus-html", - "dioxus-rsx", - "html_parser", - "pretty_assertions", - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "dioxus-runtime-config" -version = "0.6.0-alpha.2" - -[[package]] -name = "dioxus-server" -version = "0.6.0-alpha.2" - -[[package]] -name = "dioxus-signals" -version = "0.6.0-alpha.2" -dependencies = [ - "dioxus", - "dioxus-core", - "futures-channel", - "futures-util", - "generational-box", - "once_cell", - "parking_lot", - "rand 0.8.5", - "reqwest", - "rustc-hash 1.1.0", - "serde", - "simple_logger", - "tokio", - "tracing", - "tracing-subscriber", - "warnings", -] - -[[package]] -name = "dioxus-ssr" -version = "0.6.0-alpha.2" -dependencies = [ - "askama_escape", - "dioxus", - "dioxus-core", - "dioxus-core-types", - "dioxus-html", - "rustc-hash 1.1.0", -] - -[[package]] -name = "dioxus-static-site-generation" -version = "0.6.0-alpha.2" -dependencies = [ - "axum 0.7.5", - "criterion", - "dioxus", - "dioxus-devtools", - "dioxus-fullstack", - "dioxus-isrg", - "dioxus-lib", - "dioxus-router", - "dioxus-ssr", - "dioxus-web", - "http 1.1.0", - "tokio", - "tower", - "tower-http", - "tracing", -] - -[[package]] -name = "dioxus-tailwind" -version = "0.0.0" -dependencies = [ - "dioxus", - "manganis", -] - -[[package]] -name = "dioxus-web" -version = "0.6.0-alpha.2" -dependencies = [ - "async-trait", - "ciborium", - "console_error_panic_hook", - "dioxus", - "dioxus-core", - "dioxus-core-types", - "dioxus-devtools", - "dioxus-document", - "dioxus-html", - "dioxus-interpreter-js", - "dioxus-signals", - "dioxus-ssr", - "dioxus-web", - "futures-channel", - "futures-util", - "generational-box", - "gloo-timers 0.3.0", - "js-sys", - "lazy-js-bundle", - "rustc-hash 1.1.0", - "serde", - "serde-wasm-bindgen 0.6.5", - "serde_json", - "tracing", - "tracing-wasm", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-bindgen-test", - "web-sys", -] - -[[package]] -name = "dioxus_server_macro" -version = "0.6.0-alpha.2" -dependencies = [ - "proc-macro2", - "quote", - "server_fn_macro", - "syn 2.0.77", -] - -[[package]] -name = "dirs" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-next" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" -dependencies = [ - "cfg-if", - "dirs-sys-next", -] - -[[package]] -name = "dirs-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" -dependencies = [ - "libc", - "option-ext", - "redox_users", - "windows-sys 0.48.0", -] - -[[package]] -name = "dirs-sys-next" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - -[[package]] -name = "dispatch" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" - -[[package]] -name = "dissimilar" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59f8e79d1fbf76bdfbde321e902714bf6c49df88a7dda6fc682fc2979226962d" - -[[package]] -name = "dlopen2" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1297103d2bbaea85724fcee6294c2d50b1081f9ad47d0f6f6f61eda65315a6" -dependencies = [ - "dlopen2_derive", - "libc", - "once_cell", - "winapi", -] - -[[package]] -name = "dlopen2_derive" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "doc-comment" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" - -[[package]] -name = "dotenvy" -version = "0.15.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" - -[[package]] -name = "dpi" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" - -[[package]] -name = "dsa" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48bc224a9084ad760195584ce5abb3c2c34a225fa312a128ad245a6b412b7689" -dependencies = [ - "digest", - "num-bigint-dig", - "num-traits", - "pkcs8", - "rfc6979", - "sha2", - "signature", - "zeroize", -] - -[[package]] -name = "dtoa" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" - -[[package]] -name = "dtoa-short" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87" -dependencies = [ - "dtoa", -] - -[[package]] -name = "dunce" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" - -[[package]] -name = "ecdsa" -version = "0.16.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" -dependencies = [ - "der", - "digest", - "elliptic-curve", - "rfc6979", - "signature", - "spki", -] - -[[package]] -name = "ed25519" -version = "2.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" -dependencies = [ - "pkcs8", - "signature", -] - -[[package]] -name = "ed25519-dalek" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" -dependencies = [ - "curve25519-dalek", - "ed25519", - "serde", - "sha2", - "subtle", - "zeroize", -] - -[[package]] -name = "either" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" -dependencies = [ - "serde", -] - -[[package]] -name = "elliptic-curve" -version = "0.13.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" -dependencies = [ - "base16ct", - "crypto-bigint", - "digest", - "ff", - "generic-array 0.14.7", - "group", - "hkdf", - "pem-rfc7468", - "pkcs8", - "rand_core 0.6.4", - "sec1", - "subtle", - "zeroize", -] - -[[package]] -name = "encode_unicode" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" - -[[package]] -name = "encoding_rs" -version = "0.8.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "endi" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" - -[[package]] -name = "enum-display-derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f16ef37b2a9b242295d61a154ee91ae884afff6b8b933b486b12481cc58310ca" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "enum-primitive-derive" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba7795da175654fe16979af73f81f26a8ea27638d8d9823d317016888a63dc4c" -dependencies = [ - "num-traits", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "enumflags2" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d232db7f5956f3f14313dc2f87985c58bd2c695ce124c8cdd984e08e15ac133d" -dependencies = [ - "enumflags2_derive", - "serde", -] - -[[package]] -name = "enumflags2_derive" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[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 0.20.10", - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "env_filter" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" -dependencies = [ - "log", - "regex", -] - -[[package]] -name = "env_logger" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" -dependencies = [ - "anstream", - "anstyle", - "env_filter", - "humantime", - "log", -] - -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - -[[package]] -name = "erased-serde" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24e2389d65ab4fab27dc2a5de7b191e1f6617d1f1c8855c0dc569c94a4cbb18d" -dependencies = [ - "serde", - "typeid", -] - -[[package]] -name = "errno" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "etcetera" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" -dependencies = [ - "cfg-if", - "home", - "windows-sys 0.48.0", -] - -[[package]] -name = "euclid" -version = "0.22.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad9cdb4b747e485a12abb0e6566612956c7a1bafa3bdb8d682c5b6d403589e48" -dependencies = [ - "num-traits", - "serde", -] - -[[package]] -name = "event-listener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - -[[package]] -name = "event-listener" -version = "5.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener-strategy" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" -dependencies = [ - "event-listener 5.3.1", - "pin-project-lite", -] - -[[package]] -name = "execute" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a82608ee96ce76aeab659e9b8d3c2b787bffd223199af88c674923d861ada10" -dependencies = [ - "execute-command-macro", - "execute-command-tokens", - "generic-array 1.1.0", -] - -[[package]] -name = "execute-command-macro" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90dec53d547564e911dc4ff3ecb726a64cf41a6fa01a2370ebc0d95175dd08bd" -dependencies = [ - "execute-command-macro-impl", -] - -[[package]] -name = "execute-command-macro-impl" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce8cd46a041ad005ab9c71263f9a0ff5b529eac0fe4cc9b4a20f4f0765d8cf4b" -dependencies = [ - "execute-command-tokens", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "execute-command-tokens" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69dc321eb6be977f44674620ca3aa21703cb20ffbe560e1ae97da08401ffbcad" - -[[package]] -name = "exitcode" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de853764b47027c2e862a995c34978ffa63c1501f2e15f987ba11bd4f9bba193" - -[[package]] -name = "exr" -version = "1.72.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4" -dependencies = [ - "bit_field", - "flume", - "half", - "lebe", - "miniz_oxide 0.7.4", - "rayon-core", - "smallvec", - "zune-inflate", -] - -[[package]] -name = "fallible-iterator" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" - -[[package]] -name = "faster-hex" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2a2b11eda1d40935b26cf18f6833c526845ae8c41e58d09af6adeb6f0269183" - -[[package]] -name = "fastrand" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" - -[[package]] -name = "fdeflate" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" -dependencies = [ - "simd-adler32", -] - -[[package]] -name = "ff" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" -dependencies = [ - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "fiat-crypto" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" - -[[package]] -name = "field-offset" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" -dependencies = [ - "memoffset", - "rustc_version", -] - -[[package]] -name = "filetime" -version = "0.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" -dependencies = [ - "cfg-if", - "libc", - "libredox", - "windows-sys 0.59.0", -] - -[[package]] -name = "fixedbitset" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" - -[[package]] -name = "flate2" -version = "1.0.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" -dependencies = [ - "crc32fast", - "miniz_oxide 0.8.0", -] - -[[package]] -name = "fluent-uri" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17c704e9dbe1ddd863da1e6ff3567795087b1eb201ce80d8fa81162e1516500d" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "flume" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" -dependencies = [ - "futures-core", - "futures-sink", - "spin", -] - -[[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.77", -] - -[[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 = "forwarded-header-value" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8835f84f38484cc86f110a805655697908257fb9a7af005234060891557198e9" -dependencies = [ - "nonempty", - "thiserror", -] - -[[package]] -name = "fs-err" -version = "2.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88a41f105fe1d5b6b34b2055e3dc59bb79b46b48b2040b9e6c7b4b5de097aa41" -dependencies = [ - "autocfg", -] - -[[package]] -name = "fs_at" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14af6c9694ea25db25baa2a1788703b9e7c6648dcaeeebeb98f7561b5384c036" -dependencies = [ - "aligned", - "cfg-if", - "cvt", - "libc", - "nix 0.29.0", - "windows-sys 0.52.0", -] - -[[package]] -name = "fs_extra" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" - -[[package]] -name = "fsevent-sys" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" -dependencies = [ - "libc", -] - -[[package]] -name = "fullstack-auth-example" -version = "0.1.0" -dependencies = [ - "anyhow", - "async-trait", - "axum 0.7.5", - "axum_session", - "axum_session_auth", - "dioxus", - "dioxus-fullstack", - "dioxus-web", - "execute", - "http 1.1.0", - "serde", - "simple_logger", - "sqlx", - "tokio", - "tower", - "tower-http", -] - -[[package]] -name = "fullstack-desktop-example" -version = "0.1.0" -dependencies = [ - "dioxus", - "serde", -] - -[[package]] -name = "fullstack-hackernews-example" -version = "0.1.0" -dependencies = [ - "chrono", - "dioxus", - "reqwest", - "serde", - "tracing", - "tracing-subscriber", - "tracing-wasm", -] - -[[package]] -name = "fullstack-hello-world-example" -version = "0.1.0" -dependencies = [ - "dioxus", - "reqwest", - "serde", - "simple_logger", - "tracing", - "tracing-subscriber", - "tracing-wasm", -] - -[[package]] -name = "fullstack-router-example" -version = "0.1.0" -dependencies = [ - "axum 0.7.5", - "dioxus", - "serde", - "tokio", -] - -[[package]] -name = "fullstack-streaming-example" -version = "0.1.0" -dependencies = [ - "dioxus", - "futures", - "futures-util", - "once_cell", - "serde", - "simple_logger", - "tokio", - "tracing", - "tracing-subscriber", - "tracing-wasm", -] - -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - -[[package]] -name = "futf" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" -dependencies = [ - "mac", - "new_debug_unreachable", -] - -[[package]] -name = "futures" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" - -[[package]] -name = "futures-executor" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-intrusive" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" -dependencies = [ - "futures-core", - "lock_api", - "parking_lot", -] - -[[package]] -name = "futures-io" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" - -[[package]] -name = "futures-lite" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" -dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "parking", - "pin-project-lite", -] - -[[package]] -name = "futures-macro" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "futures-sink" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" - -[[package]] -name = "futures-task" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" - -[[package]] -name = "futures-util" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "fxhash" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" -dependencies = [ - "byteorder", -] - -[[package]] -name = "gdk" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5ba081bdef3b75ebcdbfc953699ed2d7417d6bd853347a42a37d76406a33646" -dependencies = [ - "cairo-rs", - "gdk-pixbuf", - "gdk-sys", - "gio", - "glib", - "libc", - "pango", -] - -[[package]] -name = "gdk-pixbuf" -version = "0.18.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" -dependencies = [ - "gdk-pixbuf-sys", - "gio", - "glib", - "libc", - "once_cell", -] - -[[package]] -name = "gdk-pixbuf-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" -dependencies = [ - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "gdk-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31ff856cb3386dae1703a920f803abafcc580e9b5f711ca62ed1620c25b51ff2" -dependencies = [ - "cairo-sys-rs", - "gdk-pixbuf-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "pango-sys", - "pkg-config", - "system-deps", -] - -[[package]] -name = "gdkwayland-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a90fbf5c033c65d93792192a49a8efb5bb1e640c419682a58bb96f5ae77f3d4a" -dependencies = [ - "gdk-sys", - "glib-sys", - "gobject-sys", - "libc", - "pkg-config", - "system-deps", -] - -[[package]] -name = "gdkx11" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2ea8a4909d530f79921290389cbd7c34cb9d623bfe970eaae65ca5f9cd9cce" -dependencies = [ - "gdk", - "gdkx11-sys", - "gio", - "glib", - "libc", - "x11", -] - -[[package]] -name = "gdkx11-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fee8f00f4ee46cad2939b8990f5c70c94ff882c3028f3cc5abf950fa4ab53043" -dependencies = [ - "gdk-sys", - "glib-sys", - "libc", - "system-deps", - "x11", -] - -[[package]] -name = "generational-box" -version = "0.6.0-alpha.2" -dependencies = [ - "criterion", - "parking_lot", - "rand 0.8.5", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", - "zeroize", -] - -[[package]] -name = "generic-array" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96512db27971c2c3eece70a1e106fbe6c87760234e31e8f7e5634912fe52794a" -dependencies = [ - "typenum", -] - -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - -[[package]] -name = "getrandom" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi 0.11.0+wasi-snapshot-preview1", - "wasm-bindgen", -] - -[[package]] -name = "ghash" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" -dependencies = [ - "opaque-debug", - "polyval", -] - -[[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.26.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" -dependencies = [ - "fallible-iterator", - "indexmap 1.9.3", - "stable_deref_trait", -] - -[[package]] -name = "gimli" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" - -[[package]] -name = "gio" -version = "0.18.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-util", - "gio-sys", - "glib", - "libc", - "once_cell", - "pin-project-lite", - "smallvec", - "thiserror", -] - -[[package]] -name = "gio-sys" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", - "winapi", -] - -[[package]] -name = "git2" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b903b73e45dc0c6c596f2d37eccece7c1c8bb6e4407b001096387c63d0d93724" -dependencies = [ - "bitflags 2.6.0", - "libc", - "libgit2-sys", - "log", - "openssl-probe", - "openssl-sys", - "url", -] - -[[package]] -name = "github-pages-static-generation" -version = "0.1.0" -dependencies = [ - "dioxus", - "tower", - "tracing-subscriber", -] - -[[package]] -name = "gix-actor" -version = "0.31.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0e454357e34b833cc3a00b6efbbd3dd4d18b24b9fb0c023876ec2645e8aa3f2" -dependencies = [ - "bstr", - "gix-date", - "gix-utils", - "itoa 1.0.11", - "thiserror", - "winnow 0.6.18", -] - -[[package]] -name = "gix-config" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fafe42957e11d98e354a66b6bd70aeea00faf2f62dd11164188224a507c840" -dependencies = [ - "bstr", - "gix-config-value", - "gix-features", - "gix-glob", - "gix-path", - "gix-ref", - "gix-sec", - "memchr", - "once_cell", - "smallvec", - "thiserror", - "unicode-bom", - "winnow 0.6.18", -] - -[[package]] -name = "gix-config-value" -version = "0.14.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03f76169faa0dec598eac60f83d7fcdd739ec16596eca8fb144c88973dbe6f8c" -dependencies = [ - "bitflags 2.6.0", - "bstr", - "gix-path", - "libc", - "thiserror", -] - -[[package]] -name = "gix-date" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9eed6931f21491ee0aeb922751bd7ec97b4b2fe8fbfedcb678e2a2dce5f3b8c0" -dependencies = [ - "bstr", - "itoa 1.0.11", - "thiserror", - "time", -] - -[[package]] -name = "gix-features" -version = "0.38.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac7045ac9fe5f9c727f38799d002a7ed3583cd777e3322a7c4b43e3cf437dc69" -dependencies = [ - "gix-hash", - "gix-trace", - "gix-utils", - "libc", - "prodash", - "sha1_smol", - "walkdir", -] - -[[package]] -name = "gix-fs" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2bfe6249cfea6d0c0e0990d5226a4cb36f030444ba9e35e0639275db8f98575" -dependencies = [ - "fastrand", - "gix-features", - "gix-utils", -] - -[[package]] -name = "gix-glob" -version = "0.16.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74908b4bbc0a0a40852737e5d7889f676f081e340d5451a16e5b4c50d592f111" -dependencies = [ - "bitflags 2.6.0", - "bstr", - "gix-features", - "gix-path", -] - -[[package]] -name = "gix-hash" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93d7df7366121b5018f947a04d37f034717e113dcf9ccd85c34b58e57a74d5e" -dependencies = [ - "faster-hex", - "thiserror", -] - -[[package]] -name = "gix-lock" -version = "14.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bc7fe297f1f4614774989c00ec8b1add59571dc9b024b4c00acb7dedd4e19d" -dependencies = [ - "gix-tempfile", - "gix-utils", - "thiserror", -] - -[[package]] -name = "gix-object" -version = "0.42.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25da2f46b4e7c2fa7b413ce4dffb87f69eaf89c2057e386491f4c55cadbfe386" -dependencies = [ - "bstr", - "gix-actor", - "gix-date", - "gix-features", - "gix-hash", - "gix-utils", - "gix-validate", - "itoa 1.0.11", - "smallvec", - "thiserror", - "winnow 0.6.18", -] - -[[package]] -name = "gix-path" -version = "0.10.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebfc4febd088abdcbc9f1246896e57e37b7a34f6909840045a1767c6dafac7af" -dependencies = [ - "bstr", - "gix-trace", - "home", - "once_cell", - "thiserror", -] - -[[package]] -name = "gix-ref" -version = "0.44.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3394a2997e5bc6b22ebc1e1a87b41eeefbcfcff3dbfa7c4bd73cb0ac8f1f3e2e" -dependencies = [ - "gix-actor", - "gix-date", - "gix-features", - "gix-fs", - "gix-hash", - "gix-lock", - "gix-object", - "gix-path", - "gix-tempfile", - "gix-utils", - "gix-validate", - "memmap2", - "thiserror", - "winnow 0.6.18", -] - -[[package]] -name = "gix-sec" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fe4d52f30a737bbece5276fab5d3a8b276dc2650df963e293d0673be34e7a5f" -dependencies = [ - "bitflags 2.6.0", - "gix-path", - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "gix-tempfile" -version = "14.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046b4927969fa816a150a0cda2e62c80016fe11fb3c3184e4dddf4e542f108aa" -dependencies = [ - "gix-fs", - "libc", - "once_cell", - "parking_lot", - "tempfile", -] - -[[package]] -name = "gix-trace" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cae0e8661c3ff92688ce1c8b8058b3efb312aba9492bbe93661a21705ab431b" - -[[package]] -name = "gix-utils" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35192df7fd0fa112263bad8021e2df7167df4cc2a6e6d15892e1e55621d3d4dc" -dependencies = [ - "fastrand", - "unicode-normalization", -] - -[[package]] -name = "gix-validate" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82c27dd34a49b1addf193c92070bcbf3beaf6e10f16a78544de6372e146a0acf" -dependencies = [ - "bstr", - "thiserror", -] - -[[package]] -name = "glib" -version = "0.18.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" -dependencies = [ - "bitflags 2.6.0", - "futures-channel", - "futures-core", - "futures-executor", - "futures-task", - "futures-util", - "gio-sys", - "glib-macros", - "glib-sys", - "gobject-sys", - "libc", - "memchr", - "once_cell", - "smallvec", - "thiserror", -] - -[[package]] -name = "glib-macros" -version = "0.18.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" -dependencies = [ - "heck 0.4.1", - "proc-macro-crate 2.0.0", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "glib-sys" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" -dependencies = [ - "libc", - "system-deps", -] - -[[package]] -name = "glob" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" - -[[package]] -name = "global-hotkey" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1b75248f33c73df1ed69673f6cb36d2e048ae84d29aa1d3e53199d138ebb1df" -dependencies = [ - "crossbeam-channel", - "keyboard-types", - "objc2", - "objc2-app-kit", - "once_cell", - "thiserror", - "windows-sys 0.59.0", - "x11-dl", -] - -[[package]] -name = "globset" -version = "0.4.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15f1ce686646e7f1e19bf7d5533fe443a45dbfb990e00629110797578b42fb19" -dependencies = [ - "aho-corasick", - "bstr", - "log", - "regex-automata 0.4.7", - "regex-syntax 0.8.4", -] - -[[package]] -name = "gloo" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28999cda5ef6916ffd33fb4a7b87e1de633c47c0dc6d97905fee1cdaa142b94d" -dependencies = [ - "gloo-console", - "gloo-dialogs", - "gloo-events", - "gloo-file", - "gloo-history", - "gloo-net 0.3.1", - "gloo-render", - "gloo-storage", - "gloo-timers 0.2.6", - "gloo-utils 0.1.7", - "gloo-worker", -] - -[[package]] -name = "gloo-console" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b7ce3c05debe147233596904981848862b068862e9ec3e34be446077190d3f" -dependencies = [ - "gloo-utils 0.1.7", - "js-sys", - "serde", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gloo-dialogs" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67062364ac72d27f08445a46cab428188e2e224ec9e37efdba48ae8c289002e6" -dependencies = [ - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gloo-events" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b107f8abed8105e4182de63845afcc7b69c098b7852a813ea7462a320992fc" -dependencies = [ - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gloo-file" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d5564e570a38b43d78bdc063374a0c3098c4f0d64005b12f9bbe87e869b6d7" -dependencies = [ - "gloo-events", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gloo-history" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85725d90bf0ed47063b3930ef28e863658a7905989e9929a8708aab74a1d5e7f" -dependencies = [ - "gloo-events", - "gloo-utils 0.1.7", - "serde", - "serde-wasm-bindgen 0.5.0", - "serde_urlencoded", - "thiserror", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gloo-net" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a66b4e3c7d9ed8d315fd6b97c8b1f74a7c6ecbbc2320e65ae7ed38b7068cc620" -dependencies = [ - "futures-channel", - "futures-core", - "futures-sink", - "gloo-utils 0.1.7", - "http 0.2.12", - "js-sys", - "pin-project", - "serde", - "serde_json", - "thiserror", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "gloo-net" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06f627b1a58ca3d42b45d6104bf1e1a03799df472df00988b6ba21accc10580" -dependencies = [ - "futures-channel", - "futures-core", - "futures-sink", - "gloo-utils 0.2.0", - "http 1.1.0", - "js-sys", - "pin-project", - "serde", - "serde_json", - "thiserror", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "gloo-render" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd9306aef67cfd4449823aadcd14e3958e0800aa2183955a309112a84ec7764" -dependencies = [ - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gloo-storage" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d6ab60bf5dbfd6f0ed1f7843da31b41010515c745735c970e821945ca91e480" -dependencies = [ - "gloo-utils 0.1.7", - "js-sys", - "serde", - "serde_json", - "thiserror", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gloo-timers" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "gloo-timers" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" -dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "gloo-utils" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037fcb07216cb3a30f7292bd0176b050b7b9a052ba830ef7d5d65f6dc64ba58e" -dependencies = [ - "js-sys", - "serde", - "serde_json", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gloo-utils" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" -dependencies = [ - "js-sys", - "serde", - "serde_json", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gloo-worker" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13471584da78061a28306d1359dd0178d8d6fc1c7c80e5e35d27260346e0516a" -dependencies = [ - "anymap2", - "bincode", - "gloo-console", - "gloo-utils 0.1.7", - "js-sys", - "serde", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "gobject-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" -dependencies = [ - "glib-sys", - "libc", - "system-deps", -] - -[[package]] -name = "group" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" -dependencies = [ - "ff", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "gtk" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93c4f5e0e20b60e10631a5f06da7fe3dda744b05ad0ea71fee2f47adf865890c" -dependencies = [ - "atk", - "cairo-rs", - "field-offset", - "futures-channel", - "gdk", - "gdk-pixbuf", - "gio", - "glib", - "gtk-sys", - "gtk3-macros", - "libc", - "pango", - "pkg-config", -] - -[[package]] -name = "gtk-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771437bf1de2c1c0b496c11505bdf748e26066bbe942dfc8f614c9460f6d7722" -dependencies = [ - "atk-sys", - "cairo-sys-rs", - "gdk-pixbuf-sys", - "gdk-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "pango-sys", - "system-deps", -] - -[[package]] -name = "gtk3-macros" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6063efb63db582968fb7df72e1ae68aa6360dcfb0a75143f34fc7d616bad75e" -dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "h2" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http 0.2.12", - "indexmap 2.5.0", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "h2" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" -dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http 1.1.0", - "indexmap 2.5.0", - "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", - "crunchy", -] - -[[package]] -name = "handlebars" -version = "5.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d08485b96a0e6393e9e4d1b8d48cf74ad6c063cd905eb33f42c1ce3f0377539b" -dependencies = [ - "log", - "pest", - "pest_derive", - "serde", - "serde_json", - "thiserror", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash 0.7.8", -] - -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash 0.8.11", - "allocator-api2", - "serde", -] - -[[package]] -name = "hashlink" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" -dependencies = [ - "hashbrown 0.14.5", -] - -[[package]] -name = "hdrhistogram" -version = "7.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "765c9198f173dd59ce26ff9f95ef0aafd0a0fe01fb9d72841bc5066a4c06511d" -dependencies = [ - "base64 0.21.7", - "byteorder", - "flate2", - "nom", - "num-traits", -] - -[[package]] -name = "headers" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322106e6bd0cba2d5ead589ddb8150a13d7c4217cf80d7c4f682ca994ccc6aa9" -dependencies = [ - "base64 0.21.7", - "bytes", - "headers-core", - "http 1.1.0", - "httpdate", - "mime", - "sha1", -] - -[[package]] -name = "headers-core" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" -dependencies = [ - "http 1.1.0", -] - -[[package]] -name = "heck" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" -dependencies = [ - "unicode-segmentation", -] - -[[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.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - -[[package]] -name = "hermit-abi" -version = "0.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 = "hkdf" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" -dependencies = [ - "hmac", -] - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] - -[[package]] -name = "home" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" -dependencies = [ - "windows-sys 0.52.0", -] - -[[package]] -name = "html5ever" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" -dependencies = [ - "log", - "mac", - "markup5ever", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "html_parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f56db07b6612644f6f7719f8ef944f75fff9d6378fdf3d316fd32194184abd" -dependencies = [ - "doc-comment", - "pest", - "pest_derive", - "serde", - "serde_derive", - "serde_json", - "thiserror", -] - -[[package]] -name = "http" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" -dependencies = [ - "bytes", - "fnv", - "itoa 1.0.11", -] - -[[package]] -name = "http" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" -dependencies = [ - "bytes", - "fnv", - "itoa 1.0.11", -] - -[[package]] -name = "http-body" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" -dependencies = [ - "bytes", - "http 0.2.12", - "pin-project-lite", -] - -[[package]] -name = "http-body" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" -dependencies = [ - "bytes", - "http 1.1.0", -] - -[[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 1.1.0", - "http-body 1.0.1", - "pin-project-lite", -] - -[[package]] -name = "http-range" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573" - -[[package]] -name = "http-range-header" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08a397c49fec283e3d6211adbe480be95aae5f304cfb923e9970e08956d5168a" - -[[package]] -name = "httparse" -version = "1.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" - -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - -[[package]] -name = "hyper" -version = "0.14.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2 0.3.26", - "http 0.2.12", - "http-body 0.4.6", - "httparse", - "httpdate", - "itoa 1.0.11", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "h2 0.4.6", - "http 1.1.0", - "http-body 1.0.1", - "httparse", - "httpdate", - "itoa 1.0.11", - "pin-project-lite", - "smallvec", - "tokio", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.27.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" -dependencies = [ - "futures-util", - "http 1.1.0", - "hyper 1.4.1", - "hyper-util", - "log", - "rustls 0.23.13", - "rustls-native-certs", - "rustls-pki-types", - "tokio", - "tokio-rustls", - "tower-service", - "webpki-roots 0.26.5", -] - -[[package]] -name = "hyper-timeout" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" -dependencies = [ - "hyper 0.14.30", - "pin-project-lite", - "tokio", - "tokio-io-timeout", -] - -[[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 1.4.1", - "hyper-util", - "native-tls", - "tokio", - "tokio-native-tls", - "tower-service", -] - -[[package]] -name = "hyper-util" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da62f120a8a37763efb0cf8fdf264b884c7b8b9ac8660b900c8661030c00e6ba" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "http 1.1.0", - "http-body 1.0.1", - "hyper 1.4.1", - "pin-project-lite", - "socket2", - "tokio", - "tower", - "tower-service", - "tracing", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" -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 = "id-arena" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" - -[[package]] -name = "idea" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "075557004419d7f2031b8bb7f44bb43e55a83ca7b63076a8fb8fe75753836477" -dependencies = [ - "cipher", -] - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "idna" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "ignore" -version = "0.4.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b" -dependencies = [ - "crossbeam-deque", - "globset", - "log", - "memchr", - "regex-automata 0.4.7", - "same-file", - "walkdir", - "winapi-util", -] - -[[package]] -name = "image" -version = "0.24.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" -dependencies = [ - "bytemuck", - "byteorder", - "color_quant", - "exr", - "gif", - "jpeg-decoder", - "num-traits", - "png", - "qoi", - "tiff", -] - -[[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.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" -dependencies = [ - "equivalent", - "hashbrown 0.14.5", - "serde", -] - -[[package]] -name = "indicatif" -version = "0.17.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3" -dependencies = [ - "console", - "instant", - "number_prefix", - "portable-atomic", - "unicode-width", -] - -[[package]] -name = "indoc" -version = "2.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" - -[[package]] -name = "infer" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc150e5ce2330295b8616ce0e3f53250e53af31759a9dbedad1621ba29151847" -dependencies = [ - "cfb", -] - -[[package]] -name = "inotify" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" -dependencies = [ - "bitflags 1.3.2", - "inotify-sys", - "libc", -] - -[[package]] -name = "inotify-sys" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" -dependencies = [ - "libc", -] - -[[package]] -name = "inout" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" -dependencies = [ - "generic-array 0.14.7", -] - -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "internment" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04e8e537b529b8674e97e9fb82c10ff168a290ac3867a0295f112061ffbca1ef" -dependencies = [ - "hashbrown 0.14.5", - "parking_lot", -] - -[[package]] -name = "inventory" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f958d3d68f4167080a18141e10381e7634563984a537f2a49a30fd8e53ac5767" - -[[package]] -name = "ipnet" -version = "2.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4" - -[[package]] -name = "ipnetwork" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf466541e9d546596ee94f9f69590f89473455f88372423e0008fc1a7daf100e" -dependencies = [ - "serde", -] - -[[package]] -name = "iri-string" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e0f755bd3806e06ad4f366f92639415d99a339a2c7ecf8c26ccea2097c11cb6" -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-terminal" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" -dependencies = [ - "hermit-abi 0.4.0", - "libc", - "windows-sys 0.52.0", -] - -[[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_ci" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45" - -[[package]] -name = "is_terminal_polyfill" -version = "1.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" - -[[package]] -name = "iter-read" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071ed4cc1afd86650602c7b11aa2e1ce30762a1c27193201cb5cee9c6ebb1294" - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -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 = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" - -[[package]] -name = "itoa" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" - -[[package]] -name = "javascriptcore-rs" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca5671e9ffce8ffba57afc24070e906da7fc4b1ba66f2cabebf61bf2ea257fcc" -dependencies = [ - "bitflags 1.3.2", - "glib", - "javascriptcore-rs-sys", -] - -[[package]] -name = "javascriptcore-rs-sys" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1be78d14ffa4b75b66df31840478fef72b51f8c2465d4ca7c194da9f7a5124" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "jni" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" -dependencies = [ - "cesu8", - "cfg-if", - "combine", - "jni-sys", - "log", - "thiserror", - "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" -dependencies = [ - "rayon", -] - -[[package]] -name = "js-sys" -version = "0.3.70" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "json-patch" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b1fb8864823fad91877e6caea0baca82e49e8db50f8e5c9f9a453e27d3330fc" -dependencies = [ - "jsonptr", - "serde", - "serde_json", - "thiserror", -] - -[[package]] -name = "jsonptr" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c6e529149475ca0b2820835d3dce8fcc41c6b943ca608d32f35b449255e4627" -dependencies = [ - "fluent-uri", - "serde", - "serde_json", -] - -[[package]] -name = "k256" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" -dependencies = [ - "cfg-if", - "ecdsa", - "elliptic-curve", - "once_cell", - "sha2", - "signature", -] - -[[package]] -name = "keccak" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" -dependencies = [ - "cpufeatures", -] - -[[package]] -name = "keyboard-types" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" -dependencies = [ - "bitflags 2.6.0", - "serde", - "unicode-segmentation", -] - -[[package]] -name = "kqueue" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" -dependencies = [ - "kqueue-sys", - "libc", -] - -[[package]] -name = "kqueue-sys" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" -dependencies = [ - "bitflags 1.3.2", - "libc", -] - -[[package]] -name = "krates" -version = "0.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "866bd5c1d0fc9c130a2469a1b3086803ea67b93b35683ae8ab2ec8a970120a65" -dependencies = [ - "camino", - "cfg-expr 0.16.0", - "petgraph", - "semver", - "serde", - "serde_json", -] - -[[package]] -name = "kstring" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "558bf9508a558512042d3095138b1f7b8fe90c5467d94f9f1da28b3731c5dbd1" -dependencies = [ - "serde", - "static_assertions", -] - -[[package]] -name = "kuchikiki" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e4755b7b995046f510a7520c42b2fed58b77bd94d5a87a8eb43d2fd126da8" -dependencies = [ - "cssparser", - "html5ever", - "indexmap 1.9.3", - "matches", - "selectors", -] - -[[package]] -name = "kv-log-macro" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" -dependencies = [ - "log", -] - -[[package]] -name = "lazy-js-bundle" -version = "0.6.0-alpha.2" - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -dependencies = [ - "spin", -] - -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - -[[package]] -name = "leb128" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" - -[[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.158" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" - -[[package]] -name = "libgit2-sys" -version = "0.17.0+1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10472326a8a6477c3c20a64547b0059e4b0d086869eee31e6d7da728a8eb7224" -dependencies = [ - "cc", - "libc", - "libssh2-sys", - "libz-sys", - "openssl-sys", - "pkg-config", -] - -[[package]] -name = "libloading" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" -dependencies = [ - "cfg-if", - "windows-targets 0.52.6", -] - -[[package]] -name = "libm" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" - -[[package]] -name = "libredox" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" -dependencies = [ - "bitflags 2.6.0", - "libc", - "redox_syscall", -] - -[[package]] -name = "libsqlite3-sys" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4e226dcd58b4be396f7bd3c20da8fdee2911400705297ba7d2d7cc2c30f716" -dependencies = [ - "cc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "libssh2-sys" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dc8a030b787e2119a731f1951d6a773e2280c660f8ec4b0f5e1505a386e71ee" -dependencies = [ - "cc", - "libc", - "libz-sys", - "openssl-sys", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "libxdo" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00333b8756a3d28e78def82067a377de7fa61b24909000aeaa2b446a948d14db" -dependencies = [ - "libxdo-sys", -] - -[[package]] -name = "libxdo-sys" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db23b9e7e2b7831bbd8aac0bbeeeb7b68cbebc162b227e7052e8e55829a09212" -dependencies = [ - "libc", - "x11", -] - -[[package]] -name = "libz-sys" -version = "1.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "link-cplusplus" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d240c6f7e1ba3a28b0249f774e6a9dd0175054b52dfbb61b16eb8505c3785c9" -dependencies = [ - "cc", -] - -[[package]] -name = "linux-raw-sys" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" - -[[package]] -name = "liquid" -version = "0.26.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cdcc72b82748f47c2933c172313f5a9aea5b2c4eb3fa4c66b4ea55bb60bb4b1" -dependencies = [ - "doc-comment", - "liquid-core", - "liquid-derive", - "liquid-lib", - "serde", -] - -[[package]] -name = "liquid-core" -version = "0.26.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2752e978ffc53670f3f2e8b3ef09f348d6f7b5474a3be3f8a5befe5382e4effb" -dependencies = [ - "anymap2", - "itertools 0.13.0", - "kstring", - "liquid-derive", - "num-traits", - "pest", - "pest_derive", - "regex", - "serde", - "time", -] - -[[package]] -name = "liquid-derive" -version = "0.26.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b51f1d220e3fa869e24cfd75915efe3164bd09bb11b3165db3f37f57bf673e3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "liquid-lib" -version = "0.26.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59b1a298d3d2287ee5b1e43840d885b8fdfc37d3f4e90d82aacfd04d021618da" -dependencies = [ - "itertools 0.13.0", - "liquid-core", - "once_cell", - "percent-encoding", - "regex", - "time", - "unicode-segmentation", -] - -[[package]] -name = "liveview-router" -version = "0.1.0" -dependencies = [ - "axum 0.7.5", - "dioxus", - "tokio", -] - -[[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 = "log" -version = "0.4.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" -dependencies = [ - "value-bag", -] - -[[package]] -name = "longest-increasing-subsequence" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3bd0dd2cd90571056fdb71f6275fada10131182f84899f4b2a916e565d81d86" - -[[package]] -name = "lru" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" -dependencies = [ - "hashbrown 0.14.5", -] - -[[package]] -name = "lzma-sys" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - -[[package]] -name = "mac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" - -[[package]] -name = "mac_address" -version = "1.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8836fae9d0d4be2c8b4efcdd79e828a2faa058a90d005abf42f91cac5493a08e" -dependencies = [ - "nix 0.28.0", - "winapi", -] - -[[package]] -name = "malloc_buf" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" -dependencies = [ - "libc", -] - -[[package]] -name = "manganis" -version = "0.6.0-alpha.2" -dependencies = [ - "anyhow", - "base64 0.22.1", - "dioxus-core-types", - "dunce", - "manganis-macro", - "once_cell", - "serde", -] - -[[package]] -name = "manganis-core" -version = "0.6.0-alpha.2" -dependencies = [ - "serde", - "serde_json", -] - -[[package]] -name = "manganis-macro" -version = "0.6.0-alpha.2" -dependencies = [ - "manganis-core", - "proc-macro2", - "quote", - "serde", - "serde_json", - "syn 2.0.77", -] - -[[package]] -name = "markup5ever" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" -dependencies = [ - "log", - "phf 0.10.1", - "phf_codegen 0.10.0", - "string_cache", - "string_cache_codegen", - "tendril", -] - -[[package]] -name = "matchers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" -dependencies = [ - "regex-automata 0.1.10", -] - -[[package]] -name = "matches" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" - -[[package]] -name = "matchit" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" - -[[package]] -name = "md-5" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" -dependencies = [ - "cfg-if", - "digest", -] - -[[package]] -name = "md5" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" - -[[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.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" -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 = "mime_guess" -version = "2.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" -dependencies = [ - "mime", - "unicase", -] - -[[package]] -name = "minicov" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c71e683cd655513b99affab7d317deb690528255a0d5f717f1024093c12b169" -dependencies = [ - "cc", - "walkdir", -] - -[[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", - "simd-adler32", -] - -[[package]] -name = "miniz_oxide" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" -dependencies = [ - "adler2", -] - -[[package]] -name = "mio" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" -dependencies = [ - "libc", - "log", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.48.0", -] - -[[package]] -name = "mio" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" -dependencies = [ - "hermit-abi 0.3.9", - "libc", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.52.0", -] - -[[package]] -name = "mirai-annotations" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9be0862c1b3f26a88803c4a49de6889c10e608b3ee9344e6ef5b45fb37ad3d1" - -[[package]] -name = "muda" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba8ac4080fb1e097c2c22acae467e46e4da72d941f02e82b67a87a2a89fa38b1" -dependencies = [ - "cocoa", - "crossbeam-channel", - "dpi", - "gtk", - "keyboard-types", - "libxdo", - "objc", - "once_cell", - "png", - "thiserror", - "windows-sys 0.59.0", -] - -[[package]] -name = "multer" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b" -dependencies = [ - "bytes", - "encoding_rs", - "futures-util", - "http 1.1.0", - "httparse", - "memchr", - "mime", - "spin", - "version_check", -] - -[[package]] -name = "names" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bddcd3bf5144b6392de80e04c347cd7fab2508f6df16a85fc496ecd5cec39bc" -dependencies = [ - "rand 0.8.5", -] - -[[package]] -name = "native-tls" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" -dependencies = [ - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "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.6.0", - "jni-sys", - "log", - "ndk-sys", - "num_enum", - "raw-window-handle 0.6.2", - "thiserror", -] - -[[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 = "nested-suspense" -version = "0.1.0" -dependencies = [ - "dioxus", - "serde", - "tokio", -] - -[[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.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" -dependencies = [ - "bitflags 2.6.0", - "cfg-if", - "cfg_aliases 0.1.1", - "libc", - "memoffset", -] - -[[package]] -name = "nix" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" -dependencies = [ - "bitflags 2.6.0", - "cfg-if", - "cfg_aliases 0.2.1", - "libc", - "memoffset", -] - -[[package]] -name = "nodrop" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" - -[[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 = "nonempty" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9e591e719385e6ebaeb5ce5d3887f7d5676fceca6411d1925ccc95745f3d6f7" - -[[package]] -name = "normpath" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8911957c4b1549ac0dc74e30db9c8b0e66ddcd6d7acc33098f4c63a64a6d7ed" -dependencies = [ - "windows-sys 0.59.0", -] - -[[package]] -name = "notify" -version = "6.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" -dependencies = [ - "bitflags 2.6.0", - "crossbeam-channel", - "filetime", - "fsevent-sys", - "inotify", - "kqueue", - "libc", - "log", - "mio 0.8.11", - "serde", - "walkdir", - "windows-sys 0.48.0", -] - -[[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "winapi", -] - -[[package]] -name = "num" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" -dependencies = [ - "num-bigint", - "num-complex", - "num-integer", - "num-iter", - "num-rational", - "num-traits", -] - -[[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-bigint-dig" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" -dependencies = [ - "byteorder", - "lazy_static", - "libm", - "num-integer", - "num-iter", - "num-traits", - "rand 0.8.5", - "serde", - "smallvec", - "zeroize", -] - -[[package]] -name = "num-complex" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" -dependencies = [ - "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.77", -] - -[[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-iter" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" -dependencies = [ - "autocfg", - "num-integer", - "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", - "libm", -] - -[[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 3.2.0", - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "num_threads" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" -dependencies = [ - "libc", -] - -[[package]] -name = "number_prefix" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" - -[[package]] -name = "objc" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" -dependencies = [ - "malloc_buf", - "objc_exception", -] - -[[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.6.0", - "block2", - "libc", - "objc2", - "objc2-core-data", - "objc2-core-image", - "objc2-foundation", - "objc2-quartz-core", -] - -[[package]] -name = "objc2-core-data" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" -dependencies = [ - "bitflags 2.6.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-encode" -version = "4.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7891e71393cd1f227313c9379a26a584ff3d7e6e7159e988851f0934c993f0f8" - -[[package]] -name = "objc2-foundation" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" -dependencies = [ - "bitflags 2.6.0", - "block2", - "libc", - "objc2", -] - -[[package]] -name = "objc2-metal" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" -dependencies = [ - "bitflags 2.6.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.6.0", - "block2", - "objc2", - "objc2-foundation", - "objc2-metal", -] - -[[package]] -name = "objc_exception" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" -dependencies = [ - "cc", -] - -[[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.36.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" -dependencies = [ - "flate2", - "memchr", - "ruzstd", - "wasmparser 0.216.0", -] - -[[package]] -name = "once-cell-regex" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3de7e389a5043420c8f2b95ed03f3f104ad6f4c41f7d7e27298f033abc253e8" -dependencies = [ - "once_cell", - "regex", -] - -[[package]] -name = "once_cell" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" - -[[package]] -name = "oorandom" -version = "11.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" - -[[package]] -name = "opaque-debug" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" - -[[package]] -name = "open" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a877bf6abd716642a53ef1b89fb498923a4afca5c754f9050b4d081c05c4b3" -dependencies = [ - "is-wsl", - "libc", - "pathdiff", -] - -[[package]] -name = "openssl" -version = "0.10.66" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" -dependencies = [ - "bitflags 2.6.0", - "cfg-if", - "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.77", -] - -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-sys" -version = "0.9.103" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" -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 = "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 = "os_pipe" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ffd2b0a5634335b135d5728d84c5e0fd726954b87111f7506a61c502280d982" -dependencies = [ - "libc", - "windows-sys 0.59.0", -] - -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - -[[package]] -name = "owo-colors" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb37767f6569cd834a413442455e0f066d0d522de8630436e2a1761d9726ba56" -dependencies = [ - "supports-color 2.1.0", - "supports-color 3.0.1", -] - -[[package]] -name = "p256" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" -dependencies = [ - "ecdsa", - "elliptic-curve", - "primeorder", - "sha2", -] - -[[package]] -name = "p384" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" -dependencies = [ - "ecdsa", - "elliptic-curve", - "primeorder", - "sha2", -] - -[[package]] -name = "pango" -version = "0.18.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" -dependencies = [ - "gio", - "glib", - "libc", - "once_cell", - "pango-sys", -] - -[[package]] -name = "pango-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "parking" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" - -[[package]] -name = "parking_lot" -version = "0.12.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", - "libc", - "redox_syscall", - "smallvec", - "windows-targets 0.52.6", -] - -[[package]] -name = "password-hash" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" -dependencies = [ - "base64ct", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - -[[package]] -name = "path-absolutize" -version = "3.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4af381fe79fa195b4909485d99f73a80792331df0625188e707854f0b3383f5" -dependencies = [ - "path-dedot", -] - -[[package]] -name = "path-dedot" -version = "3.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07ba0ad7e047712414213ff67533e6dd477af0a4e1d14fb52343e53d30ea9397" -dependencies = [ - "once_cell", -] - -[[package]] -name = "pathdiff" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" - -[[package]] -name = "pbkdf2" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" -dependencies = [ - "digest", - "hmac", - "password-hash", - "sha2", -] - -[[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 = "pem-rfc7468" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" -dependencies = [ - "base64ct", -] - -[[package]] -name = "percent-encoding" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" - -[[package]] -name = "pest" -version = "2.7.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c73c26c01b8c87956cea613c907c9d6ecffd8d18a2a5908e5de0adfaa185cea" -dependencies = [ - "memchr", - "thiserror", - "ucd-trie", -] - -[[package]] -name = "pest_derive" -version = "2.7.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "664d22978e2815783adbdd2c588b455b1bd625299ce36b2a99881ac9627e6d8d" -dependencies = [ - "pest", - "pest_generator", -] - -[[package]] -name = "pest_generator" -version = "2.7.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2d5487022d5d33f4c30d91c22afa240ce2a644e87fe08caad974d4eab6badbe" -dependencies = [ - "pest", - "pest_meta", - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "pest_meta" -version = "2.7.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0091754bbd0ea592c4deb3a122ce8ecbb0753b738aa82bc055fcc2eccc8d8174" -dependencies = [ - "once_cell", - "pest", - "sha2", -] - -[[package]] -name = "petgraph" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" -dependencies = [ - "fixedbitset", - "indexmap 2.5.0", -] - -[[package]] -name = "pgp" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "031fa1e28c4cb54c90502ef0642a44ef10ec8349349ebe6372089f1b1ef4f297" -dependencies = [ - "aes", - "base64 0.21.7", - "bitfield", - "block-padding", - "blowfish", - "bstr", - "buffer-redux", - "byteorder", - "camellia", - "cast5", - "cfb-mode", - "chrono", - "cipher", - "const-oid", - "crc24", - "curve25519-dalek", - "derive_builder", - "des", - "digest", - "dsa", - "ed25519-dalek", - "elliptic-curve", - "flate2", - "generic-array 0.14.7", - "hex", - "idea", - "iter-read", - "k256", - "log", - "md-5", - "nom", - "num-bigint-dig", - "num-traits", - "num_enum", - "p256", - "p384", - "rand 0.8.5", - "ripemd", - "rsa", - "sha1", - "sha2", - "sha3", - "signature", - "smallvec", - "thiserror", - "twofish", - "x25519-dalek", - "zeroize", -] - -[[package]] -name = "phf" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" -dependencies = [ - "phf_macros 0.8.0", - "phf_shared 0.8.0", - "proc-macro-hack", -] - -[[package]] -name = "phf" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" -dependencies = [ - "phf_shared 0.10.0", -] - -[[package]] -name = "phf" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" -dependencies = [ - "phf_macros 0.11.2", - "phf_shared 0.11.2", -] - -[[package]] -name = "phf_codegen" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" -dependencies = [ - "phf_generator 0.8.0", - "phf_shared 0.8.0", -] - -[[package]] -name = "phf_codegen" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" -dependencies = [ - "phf_generator 0.10.0", - "phf_shared 0.10.0", -] - -[[package]] -name = "phf_generator" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" -dependencies = [ - "phf_shared 0.8.0", - "rand 0.7.3", -] - -[[package]] -name = "phf_generator" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" -dependencies = [ - "phf_shared 0.10.0", - "rand 0.8.5", -] - -[[package]] -name = "phf_generator" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" -dependencies = [ - "phf_shared 0.11.2", - "rand 0.8.5", -] - -[[package]] -name = "phf_macros" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" -dependencies = [ - "phf_generator 0.8.0", - "phf_shared 0.8.0", - "proc-macro-hack", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "phf_macros" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" -dependencies = [ - "phf_generator 0.11.2", - "phf_shared 0.11.2", - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "phf_shared" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" -dependencies = [ - "siphasher", -] - -[[package]] -name = "phf_shared" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" -dependencies = [ - "siphasher", -] - -[[package]] -name = "phf_shared" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" -dependencies = [ - "siphasher", -] - -[[package]] -name = "pin-project" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" - -[[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 = "pkcs1" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" -dependencies = [ - "der", - "pkcs8", - "spki", -] - -[[package]] -name = "pkcs8" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" -dependencies = [ - "der", - "spki", -] - -[[package]] -name = "pkg-config" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" - -[[package]] -name = "plist" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42cf17e9a1800f5f396bc67d193dc9411b59012a5876445ef450d449881e1016" -dependencies = [ - "base64 0.22.1", - "indexmap 2.5.0", - "quick-xml", - "serde", - "time", -] - -[[package]] -name = "plotters" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" -dependencies = [ - "num-traits", - "plotters-backend", - "plotters-svg", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "plotters-backend" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" - -[[package]] -name = "plotters-svg" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" -dependencies = [ - "plotters-backend", -] - -[[package]] -name = "png" -version = "0.17.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" -dependencies = [ - "bitflags 1.3.2", - "crc32fast", - "fdeflate", - "flate2", - "miniz_oxide 0.7.4", -] - -[[package]] -name = "polling" -version = "3.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2790cd301dec6cd3b7a025e4815cf825724a51c98dccfe6a3e55f05ffb6511" -dependencies = [ - "cfg-if", - "concurrent-queue", - "hermit-abi 0.4.0", - "pin-project-lite", - "rustix", - "tracing", - "windows-sys 0.59.0", -] - -[[package]] -name = "pollster" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2" - -[[package]] -name = "polyval" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" -dependencies = [ - "cfg-if", - "cpufeatures", - "opaque-debug", - "universal-hash", -] - -[[package]] -name = "portable-atomic" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" - -[[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 = "precomputed-hash" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" - -[[package]] -name = "prettier-please" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32db37eb2b0ec0af154e9c1b33425902d8cd9481e35167c4e9ffb28fec3916bb" -dependencies = [ - "proc-macro2", - "syn 2.0.77", -] - -[[package]] -name = "pretty_assertions" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" -dependencies = [ - "diff", - "yansi", -] - -[[package]] -name = "prettyplease" -version = "0.2.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" -dependencies = [ - "proc-macro2", - "syn 2.0.77", -] - -[[package]] -name = "primeorder" -version = "0.13.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" -dependencies = [ - "elliptic-curve", -] - -[[package]] -name = "proc-macro-crate" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" -dependencies = [ - "once_cell", - "toml_edit 0.19.15", -] - -[[package]] -name = "proc-macro-crate" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" -dependencies = [ - "toml_edit 0.20.7", -] - -[[package]] -name = "proc-macro-crate" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" -dependencies = [ - "toml_edit 0.22.20", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro-hack" -version = "0.5.20+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" - -[[package]] -name = "proc-macro2" -version = "1.0.86" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" -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.77", - "version_check", -] - -[[package]] -name = "prodash" -version = "28.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "744a264d26b88a6a7e37cbad97953fa233b94d585236310bcbc88474b4092d79" - -[[package]] -name = "prost" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" -dependencies = [ - "bytes", - "prost-derive", -] - -[[package]] -name = "prost-derive" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" -dependencies = [ - "anyhow", - "itertools 0.12.1", - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "prost-types" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0" -dependencies = [ - "prost", -] - -[[package]] -name = "ptr_meta" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" -dependencies = [ - "ptr_meta_derive", -] - -[[package]] -name = "ptr_meta_derive" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "qoi" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" -dependencies = [ - "bytemuck", -] - -[[package]] -name = "quick-xml" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2" -dependencies = [ - "memchr", -] - -[[package]] -name = "quinn" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c7c5fdde3cdae7203427dc4f0a68fe0ed09833edc525a03456b153b79828684" -dependencies = [ - "bytes", - "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash 2.0.0", - "rustls 0.23.13", - "socket2", - "thiserror", - "tokio", - "tracing", -] - -[[package]] -name = "quinn-proto" -version = "0.11.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6" -dependencies = [ - "bytes", - "rand 0.8.5", - "ring", - "rustc-hash 2.0.0", - "rustls 0.23.13", - "slab", - "thiserror", - "tinyvec", - "tracing", -] - -[[package]] -name = "quinn-udp" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fe68c2e9e1a1234e218683dbdf9f9dfcb094113c5ac2b938dfcb9bab4c4140b" -dependencies = [ - "libc", - "once_cell", - "socket2", - "tracing", - "windows-sys 0.59.0", -] - -[[package]] -name = "quote" -version = "1.0.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", - "rand_pcg", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", -] - -[[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 0.6.4", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.15", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "rand_pcg" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "ratatui" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16546c5b5962abf8ce6e2881e722b4e0ae3b6f1a08a26ae3573c55853ca68d3" -dependencies = [ - "bitflags 2.6.0", - "cassowary", - "compact_str", - "crossterm", - "itertools 0.13.0", - "lru", - "paste", - "stability", - "strum 0.26.3", - "strum_macros 0.26.4", - "unicode-segmentation", - "unicode-truncate", - "unicode-width", -] - -[[package]] -name = "raw-window-handle" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" - -[[package]] -name = "raw-window-handle" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" - -[[package]] -name = "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.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" -dependencies = [ - "bitflags 2.6.0", -] - -[[package]] -name = "redox_users" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" -dependencies = [ - "getrandom 0.2.15", - "libredox", - "thiserror", -] - -[[package]] -name = "regex" -version = "1.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata 0.4.7", - "regex-syntax 0.8.4", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", -] - -[[package]] -name = "regex-automata" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax 0.8.4", -] - -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - -[[package]] -name = "regex-syntax" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" - -[[package]] -name = "remove_dir_all" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c914caef075f03e9d5c568e2e71b3d3cf17dc61a5481ff379bb744721be0a75a" -dependencies = [ - "cfg-if", - "cvt", - "fs_at", - "libc", - "normpath", - "windows-sys 0.52.0", -] - -[[package]] -name = "rend" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" -dependencies = [ - "bytecheck", -] - -[[package]] -name = "reqwest" -version = "0.12.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63" -dependencies = [ - "base64 0.22.1", - "bytes", - "encoding_rs", - "futures-channel", - "futures-core", - "futures-util", - "h2 0.4.6", - "http 1.1.0", - "http-body 1.0.1", - "http-body-util", - "hyper 1.4.1", - "hyper-rustls", - "hyper-tls", - "hyper-util", - "ipnet", - "js-sys", - "log", - "mime", - "mime_guess", - "native-tls", - "once_cell", - "percent-encoding", - "pin-project-lite", - "quinn", - "rustls 0.23.13", - "rustls-pemfile 2.1.3", - "rustls-pki-types", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper 1.0.1", - "system-configuration", - "tokio", - "tokio-native-tls", - "tokio-rustls", - "tokio-util", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-streams", - "web-sys", - "webpki-roots 0.26.5", - "windows-registry 0.2.0", -] - -[[package]] -name = "rfc6979" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" -dependencies = [ - "hmac", - "subtle", -] - -[[package]] -name = "rfd" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25a73a7337fc24366edfca76ec521f51877b114e42dab584008209cca6719251" -dependencies = [ - "ashpd", - "block", - "dispatch", - "js-sys", - "log", - "objc", - "objc-foundation", - "objc_id", - "pollster", - "raw-window-handle 0.6.2", - "urlencoding", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "windows-sys 0.48.0", -] - -[[package]] -name = "rhai" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61797318be89b1a268a018a92a7657096d83f3ecb31418b9e9c16dcbb043b702" -dependencies = [ - "ahash 0.8.11", - "bitflags 2.6.0", - "instant", - "num-traits", - "once_cell", - "rhai_codegen", - "smallvec", - "smartstring", - "thin-vec", -] - -[[package]] -name = "rhai_codegen" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5a11a05ee1ce44058fa3d5961d05194fdbe3ad6b40f904af764d81b86450e6b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "ring" -version = "0.17.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" -dependencies = [ - "cc", - "cfg-if", - "getrandom 0.2.15", - "libc", - "spin", - "untrusted 0.9.0", - "windows-sys 0.52.0", -] - -[[package]] -name = "ripemd" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" -dependencies = [ - "digest", -] - -[[package]] -name = "rkyv" -version = "0.7.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9008cd6385b9e161d8229e1f6549dd23c3d022f132a2ea37ac3a10ac4935779b" -dependencies = [ - "bitvec", - "bytecheck", - "bytes", - "hashbrown 0.12.3", - "ptr_meta", - "rend", - "rkyv_derive", - "seahash", - "tinyvec", - "uuid", -] - -[[package]] -name = "rkyv_derive" -version = "0.7.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "503d1d27590a2b0a3a4ca4c94755aa2875657196ecbf401a42eff41d7de532c0" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "router-static-generation" -version = "0.1.0" -dependencies = [ - "dioxus", - "tracing-subscriber", -] - -[[package]] -name = "rpm" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e68a0d60350e5f4229cd69f08ec8f373e34424701702d1ee51a89aee1e9adcd1" -dependencies = [ - "bitflags 2.6.0", - "bzip2", - "chrono", - "cpio", - "digest", - "enum-display-derive", - "enum-primitive-derive", - "flate2", - "hex", - "itertools 0.12.1", - "log", - "md-5", - "nom", - "num", - "num-derive", - "num-traits", - "pgp", - "sha1", - "sha2", - "thiserror", - "xz2", - "zstd 0.13.2", -] - -[[package]] -name = "rsa" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" -dependencies = [ - "const-oid", - "digest", - "num-bigint-dig", - "num-integer", - "num-traits", - "pkcs1", - "pkcs8", - "rand_core 0.6.4", - "signature", - "spki", - "subtle", - "zeroize", -] - -[[package]] -name = "rust_decimal" -version = "1.36.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b082d80e3e3cc52b2ed634388d436fe1f4de6af5786cc2de9ba9737527bdf555" -dependencies = [ - "arrayvec", - "borsh", - "bytes", - "num-traits", - "rand 0.8.5", - "rkyv", - "serde", - "serde_json", -] - -[[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.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" - -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver", -] - -[[package]] -name = "rustix" -version = "0.38.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" -dependencies = [ - "bitflags 2.6.0", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.52.0", -] - -[[package]] -name = "rustls" -version = "0.21.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" -dependencies = [ - "ring", - "rustls-webpki 0.101.7", - "sct", -] - -[[package]] -name = "rustls" -version = "0.23.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8" -dependencies = [ - "aws-lc-rs", - "log", - "once_cell", - "ring", - "rustls-pki-types", - "rustls-webpki 0.102.8", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls-native-certs" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcaf18a4f2be7326cd874a5fa579fae794320a0f388d365dca7e480e55f83f8a" -dependencies = [ - "openssl-probe", - "rustls-pemfile 2.1.3", - "rustls-pki-types", - "schannel", - "security-framework", -] - -[[package]] -name = "rustls-pemfile" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" -dependencies = [ - "base64 0.21.7", -] - -[[package]] -name = "rustls-pemfile" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" -dependencies = [ - "base64 0.22.1", - "rustls-pki-types", -] - -[[package]] -name = "rustls-pki-types" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" - -[[package]] -name = "rustls-webpki" -version = "0.101.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" -dependencies = [ - "ring", - "untrusted 0.9.0", -] - -[[package]] -name = "rustls-webpki" -version = "0.102.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" -dependencies = [ - "aws-lc-rs", - "ring", - "rustls-pki-types", - "untrusted 0.9.0", -] - -[[package]] -name = "rustversion" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" - -[[package]] -name = "ruzstd" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99c3938e133aac070997ddc684d4b393777d293ba170f2988c8fd5ea2ad4ce21" -dependencies = [ - "twox-hash", -] - -[[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 = "sanitize-filename" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ed72fbaf78e6f2d41744923916966c4fbe3d7c74e3037a8ee482f1115572603" -dependencies = [ - "lazy_static", - "regex", -] - -[[package]] -name = "schannel" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" -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 = "scratch" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3cf7c11c38cb994f3d40e8a8cde3bbd1f72a435e4c49e85d6553d8312306152" - -[[package]] -name = "sct" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" -dependencies = [ - "ring", - "untrusted 0.9.0", -] - -[[package]] -name = "seahash" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" - -[[package]] -name = "sec1" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" -dependencies = [ - "base16ct", - "der", - "generic-array 0.14.7", - "pkcs8", - "subtle", - "zeroize", -] - -[[package]] -name = "security-framework" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" -dependencies = [ - "bitflags 2.6.0", - "core-foundation 0.9.4", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "selectors" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe" -dependencies = [ - "bitflags 1.3.2", - "cssparser", - "derive_more", - "fxhash", - "log", - "matches", - "phf 0.8.0", - "phf_codegen 0.8.0", - "precomputed-hash", - "servo_arc", - "smallvec", - "thin-slice", -] - -[[package]] -name = "semver" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" -dependencies = [ - "serde", -] - -[[package]] -name = "send_wrapper" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" -dependencies = [ - "futures-core", -] - -[[package]] -name = "separator" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f97841a747eef040fcd2e7b3b9a220a7205926e60488e673d9e4926d27772ce5" - -[[package]] -name = "serde" -version = "1.0.210" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde-untagged" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2676ba99bd82f75cae5cbd2c8eda6fa0b8760f18978ea840e980dd5567b5c5b6" -dependencies = [ - "erased-serde", - "serde", - "typeid", -] - -[[package]] -name = "serde-wasm-bindgen" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3b143e2833c57ab9ad3ea280d21fd34e285a42837aeb0ee301f4f41890fa00e" -dependencies = [ - "js-sys", - "serde", - "wasm-bindgen", -] - -[[package]] -name = "serde-wasm-bindgen" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b" -dependencies = [ - "js-sys", - "serde", - "wasm-bindgen", -] - -[[package]] -name = "serde_derive" -version = "1.0.210" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "serde_json" -version = "1.0.128" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" -dependencies = [ - "itoa 1.0.11", - "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 1.0.11", - "serde", -] - -[[package]] -name = "serde_qs" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0431a35568651e363364210c91983c1da5eb29404d9f0928b67d4ebcfa7d330c" -dependencies = [ - "percent-encoding", - "serde", - "thiserror", -] - -[[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.77", -] - -[[package]] -name = "serde_spanned" -version = "0.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" -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 1.0.11", - "ryu", - "serde", -] - -[[package]] -name = "serde_with" -version = "3.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cecfa94848272156ea67b2b1a53f20fc7bc638c4a46d2f8abde08f05f4b857" -dependencies = [ - "base64 0.22.1", - "chrono", - "hex", - "indexmap 1.9.3", - "indexmap 2.5.0", - "serde", - "serde_derive", - "serde_json", - "serde_with_macros", - "time", -] - -[[package]] -name = "serde_with_macros" -version = "3.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8fee4991ef4f274617a51ad4af30519438dacb2f56ac773b08a1922ff743350" -dependencies = [ - "darling 0.20.10", - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "server_fn" -version = "0.6.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fae7a3038a32e5a34ba32c6c45eb4852f8affaf8b794ebfcd4b1099e2d62ebe" -dependencies = [ - "axum 0.7.5", - "bytes", - "const_format", - "dashmap", - "futures", - "gloo-net 0.6.0", - "http 1.1.0", - "http-body-util", - "hyper 1.4.1", - "inventory", - "js-sys", - "once_cell", - "reqwest", - "send_wrapper", - "serde", - "serde_json", - "serde_qs", - "server_fn_macro_default", - "thiserror", - "tower", - "tower-layer", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-streams", - "web-sys", - "xxhash-rust", -] - -[[package]] -name = "server_fn_macro" -version = "0.6.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faaaf648c6967aef78177c0610478abb5a3455811f401f3c62d10ae9bd3901a1" -dependencies = [ - "const_format", - "convert_case 0.6.0", - "proc-macro2", - "quote", - "syn 2.0.77", - "xxhash-rust", -] - -[[package]] -name = "server_fn_macro_default" -version = "0.6.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2aa8119b558a17992e0ac1fd07f080099564f24532858811ce04f742542440" -dependencies = [ - "server_fn_macro", - "syn 2.0.77", -] - -[[package]] -name = "servo_arc" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432" -dependencies = [ - "nodrop", - "stable_deref_trait", -] - -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha1_smol" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" - -[[package]] -name = "sha2" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha3" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" -dependencies = [ - "digest", - "keccak", -] - -[[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 = "shell-words" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "signal-hook" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" -dependencies = [ - "libc", - "signal-hook-registry", -] - -[[package]] -name = "signal-hook-mio" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" -dependencies = [ - "libc", - "mio 0.8.11", - "signal-hook", -] - -[[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 = "signature" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" -dependencies = [ - "digest", - "rand_core 0.6.4", -] - -[[package]] -name = "simd-adler32" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" - -[[package]] -name = "simdutf8" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" - -[[package]] -name = "simple-static-generation" -version = "0.1.0" -dependencies = [ - "dioxus", - "tracing-subscriber", -] - -[[package]] -name = "simple_logger" -version = "4.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e7e46c8c90251d47d08b28b8a419ffb4aede0f87c2eea95e17d1d5bacbf3ef1" -dependencies = [ - "colored", - "log", - "time", - "windows-sys 0.48.0", -] - -[[package]] -name = "siphasher" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" - -[[package]] -name = "slab" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] - -[[package]] -name = "sledgehammer_bindgen" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49e83e178d176459c92bc129cfd0958afac3ced925471b889b3a75546cfc4133" -dependencies = [ - "sledgehammer_bindgen_macro", - "wasm-bindgen", -] - -[[package]] -name = "sledgehammer_bindgen_macro" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33a1b4f13e2bbf2f5b29d09dfebc9de69229ffee245aed80e3b70f9b5fd28c06" -dependencies = [ - "quote", - "syn 2.0.77", -] - -[[package]] -name = "sledgehammer_utils" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "debdd4b83524961983cea3c55383b3910fd2f24fd13a188f5b091d2d504a61ae" -dependencies = [ - "rustc-hash 1.1.0", -] - -[[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 = "smartstring" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29" -dependencies = [ - "autocfg", - "static_assertions", - "version_check", -] - -[[package]] -name = "socket2" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "socks" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c3dbbd9ae980613c6dd8e28a9407b50509d3803b57624d5dfe8315218cd58b" -dependencies = [ - "byteorder", - "libc", - "winapi", -] - -[[package]] -name = "soup3" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "471f924a40f31251afc77450e781cb26d55c0b650842efafc9c6cbd2f7cc4f9f" -dependencies = [ - "futures-channel", - "gio", - "glib", - "libc", - "soup3-sys", -] - -[[package]] -name = "soup3-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ebe8950a680a12f24f15ebe1bf70db7af98ad242d9db43596ad3108aab86c27" -dependencies = [ - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] - -[[package]] -name = "spki" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" -dependencies = [ - "base64ct", - "der", -] - -[[package]] -name = "sqlformat" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bba3a93db0cc4f7bdece8bb09e77e2e785c20bfebf79eb8340ed80708048790" -dependencies = [ - "nom", - "unicode_categories", -] - -[[package]] -name = "sqlx" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9a2ccff1a000a5a59cd33da541d9f2fdcd9e6e8229cc200565942bff36d0aaa" -dependencies = [ - "sqlx-core", - "sqlx-macros", - "sqlx-mysql", - "sqlx-postgres", - "sqlx-sqlite", -] - -[[package]] -name = "sqlx-core" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24ba59a9342a3d9bab6c56c118be528b27c9b60e490080e9711a04dccac83ef6" -dependencies = [ - "ahash 0.8.11", - "atoi", - "bigdecimal", - "bit-vec", - "byteorder", - "bytes", - "chrono", - "crc", - "crossbeam-queue", - "either", - "event-listener 2.5.3", - "futures-channel", - "futures-core", - "futures-intrusive", - "futures-io", - "futures-util", - "hashlink", - "hex", - "indexmap 2.5.0", - "ipnetwork", - "log", - "mac_address", - "memchr", - "native-tls", - "once_cell", - "paste", - "percent-encoding", - "rust_decimal", - "rustls 0.21.12", - "rustls-pemfile 1.0.4", - "serde", - "serde_json", - "sha2", - "smallvec", - "sqlformat", - "thiserror", - "time", - "tokio", - "tokio-stream", - "tracing", - "url", - "uuid", - "webpki-roots 0.25.4", -] - -[[package]] -name = "sqlx-macros" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ea40e2345eb2faa9e1e5e326db8c34711317d2b5e08d0d5741619048a803127" -dependencies = [ - "proc-macro2", - "quote", - "sqlx-core", - "sqlx-macros-core", - "syn 1.0.109", -] - -[[package]] -name = "sqlx-macros-core" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5833ef53aaa16d860e92123292f1f6a3d53c34ba8b1969f152ef1a7bb803f3c8" -dependencies = [ - "dotenvy", - "either", - "heck 0.4.1", - "hex", - "once_cell", - "proc-macro2", - "quote", - "serde", - "serde_json", - "sha2", - "sqlx-core", - "sqlx-mysql", - "sqlx-postgres", - "sqlx-sqlite", - "syn 1.0.109", - "tempfile", - "tokio", - "url", -] - -[[package]] -name = "sqlx-mysql" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ed31390216d20e538e447a7a9b959e06ed9fc51c37b514b46eb758016ecd418" -dependencies = [ - "atoi", - "base64 0.21.7", - "bigdecimal", - "bitflags 2.6.0", - "byteorder", - "bytes", - "chrono", - "crc", - "digest", - "dotenvy", - "either", - "futures-channel", - "futures-core", - "futures-io", - "futures-util", - "generic-array 0.14.7", - "hex", - "hkdf", - "hmac", - "itoa 1.0.11", - "log", - "md-5", - "memchr", - "once_cell", - "percent-encoding", - "rand 0.8.5", - "rsa", - "rust_decimal", - "serde", - "sha1", - "sha2", - "smallvec", - "sqlx-core", - "stringprep", - "thiserror", - "time", - "tracing", - "uuid", - "whoami", -] - -[[package]] -name = "sqlx-postgres" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c824eb80b894f926f89a0b9da0c7f435d27cdd35b8c655b114e58223918577e" -dependencies = [ - "atoi", - "base64 0.21.7", - "bigdecimal", - "bit-vec", - "bitflags 2.6.0", - "byteorder", - "chrono", - "crc", - "dotenvy", - "etcetera", - "futures-channel", - "futures-core", - "futures-io", - "futures-util", - "hex", - "hkdf", - "hmac", - "home", - "ipnetwork", - "itoa 1.0.11", - "log", - "mac_address", - "md-5", - "memchr", - "num-bigint", - "once_cell", - "rand 0.8.5", - "rust_decimal", - "serde", - "serde_json", - "sha2", - "smallvec", - "sqlx-core", - "stringprep", - "thiserror", - "time", - "tracing", - "uuid", - "whoami", -] - -[[package]] -name = "sqlx-sqlite" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b244ef0a8414da0bed4bb1910426e890b19e5e9bccc27ada6b797d05c55ae0aa" -dependencies = [ - "atoi", - "chrono", - "flume", - "futures-channel", - "futures-core", - "futures-executor", - "futures-intrusive", - "futures-util", - "libsqlite3-sys", - "log", - "percent-encoding", - "serde", - "sqlx-core", - "time", - "tracing", - "url", - "urlencoding", - "uuid", -] - -[[package]] -name = "stability" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d904e7009df136af5297832a3ace3370cd14ff1546a232f4f185036c2736fcac" -dependencies = [ - "quote", - "syn 2.0.77", -] - -[[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 = "string_cache" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" -dependencies = [ - "new_debug_unreachable", - "once_cell", - "parking_lot", - "phf_shared 0.10.0", - "precomputed-hash", - "serde", -] - -[[package]] -name = "string_cache_codegen" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" -dependencies = [ - "phf_generator 0.10.0", - "phf_shared 0.10.0", - "proc-macro2", - "quote", -] - -[[package]] -name = "stringprep" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" -dependencies = [ - "unicode-bidi", - "unicode-normalization", - "unicode-properties", -] - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - -[[package]] -name = "strum" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" - -[[package]] -name = "strum" -version = "0.26.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" -dependencies = [ - "strum_macros 0.26.4", -] - -[[package]] -name = "strum_macros" -version = "0.24.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "rustversion", - "syn 1.0.109", -] - -[[package]] -name = "strum_macros" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.77", -] - -[[package]] -name = "subprocess" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c2e86926081dda636c546d8c5e641661049d7562a68f5488be4a1f7f66f6086" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - -[[package]] -name = "supports-color" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6398cde53adc3c4557306a96ce67b302968513830a77a95b2b17305d9719a89" -dependencies = [ - "is-terminal", - "is_ci", -] - -[[package]] -name = "supports-color" -version = "3.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8775305acf21c96926c900ad056abeef436701108518cf890020387236ac5a77" -dependencies = [ - "is_ci", -] - -[[package]] -name = "suspense-carousel" -version = "0.6.0-alpha.2" -dependencies = [ - "dioxus", - "gloo-timers 0.3.0", - "serde", - "tokio", -] - -[[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.77" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn_derive" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1329189c02ff984e9736652b1631330da25eaa6bc639089ed4915d25446cbe7b" -dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "sync_wrapper" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - -[[package]] -name = "sync_wrapper" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" -dependencies = [ - "futures-core", -] - -[[package]] -name = "sysctl" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225e483f02d0ad107168dc57381a8a40c3aeea6abe47f37506931f861643cfa8" -dependencies = [ - "bitflags 1.3.2", - "byteorder", - "libc", - "thiserror", - "walkdir", -] - -[[package]] -name = "system-configuration" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" -dependencies = [ - "bitflags 2.6.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 0.15.8", - "heck 0.5.0", - "pkg-config", - "toml", - "version-compare", -] - -[[package]] -name = "tao" -version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a93f2c6b8fdaeb7f417bda89b5bc767999745c3052969664ae1fa65892deb7e" -dependencies = [ - "bitflags 2.6.0", - "cocoa", - "core-foundation 0.10.0", - "core-graphics", - "crossbeam-channel", - "dispatch", - "dlopen2", - "dpi", - "gdkwayland-sys", - "gdkx11-sys", - "gtk", - "instant", - "jni", - "lazy_static", - "libc", - "log", - "ndk", - "ndk-context", - "ndk-sys", - "objc", - "once_cell", - "parking_lot", - "raw-window-handle 0.5.2", - "raw-window-handle 0.6.2", - "scopeguard", - "tao-macros", - "unicode-segmentation", - "url", - "windows", - "windows-core 0.58.0", - "windows-version", - "x11-dl", -] - -[[package]] -name = "tao-macros" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] -name = "tar" -version = "0.4.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb797dad5fb5b76fcf519e702f4a589483b5ef06567f160c392832c1f5e44909" -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 = "tauri-bundler" -version = "2.0.0-rc.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afefaa18bdaa49afff89a299d6afb05de7ffc2017d84d717239455e6645d21ea" -dependencies = [ - "anyhow", - "ar", - "bitness", - "dirs", - "dunce", - "flate2", - "glob", - "handlebars", - "heck 0.5.0", - "hex", - "image", - "log", - "md5", - "os_pipe", - "plist", - "regex", - "rpm", - "semver", - "serde", - "serde_json", - "sha1", - "sha2", - "strsim 0.11.1", - "tar", - "tauri-icns", - "tauri-macos-sign", - "tauri-utils", - "tempfile", - "thiserror", - "time", - "ureq", - "uuid", - "walkdir", - "windows-registry 0.1.2", - "windows-sys 0.52.0", - "zip", -] - -[[package]] -name = "tauri-icns" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03b7eb4d0d43724ba9ba6a6717420ee68aee377816a3edbb45db8c18862b1431" -dependencies = [ - "byteorder", - "png", -] - -[[package]] -name = "tauri-macos-sign" -version = "0.1.0-beta.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a689cc26acd1e619b317f7cec95db2eb9fa1c69888030162a546a7fafa7eb3d" -dependencies = [ - "anyhow", - "dirs-next", - "once-cell-regex", - "os_pipe", - "plist", - "rand 0.8.5", - "serde", - "serde_json", - "tempfile", - "x509-certificate", -] - -[[package]] -name = "tauri-utils" -version = "2.0.0-rc.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba92ad9cdf7658fefa29a7218dda0acead9400c021bbf9c3f88e98f5e3b9bbab" -dependencies = [ - "ctor", - "dunce", - "glob", - "html5ever", - "infer", - "json-patch", - "kuchikiki", - "log", - "memchr", - "phf 0.11.2", - "regex", - "semver", - "serde", - "serde-untagged", - "serde_json", - "serde_with", - "thiserror", - "toml", - "url", - "urlpattern", - "walkdir", -] - -[[package]] -name = "tempfile" -version = "3.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" -dependencies = [ - "cfg-if", - "fastrand", - "rustix", - "windows-sys 0.52.0", -] - -[[package]] -name = "tendril" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" -dependencies = [ - "futf", - "mac", - "utf-8", -] - -[[package]] -name = "termcolor" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "terminal-prompt" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "572818b3472910acbd5dff46a3413715c18e934b071ab2ba464a7b2c2af16376" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "terminal_size" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" -dependencies = [ - "rustix", - "windows-sys 0.48.0", -] - -[[package]] -name = "test-package" -version = "0.2.1" -dependencies = [ - "manganis", - "test-package-dependency", -] - -[[package]] -name = "test-package-dependency" -version = "0.2.1" -dependencies = [ - "manganis", - "test-package-nested-dependency", -] - -[[package]] -name = "test-package-nested-dependency" -version = "0.2.1" -dependencies = [ - "manganis", -] - -[[package]] -name = "thin-slice" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" - -[[package]] -name = "thin-vec" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a38c90d48152c236a3ab59271da4f4ae63d678c5d7ad6b7714d7cb9760be5e4b" - -[[package]] -name = "thiserror" -version = "1.0.63" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.63" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "thread_local" -version = "1.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" -dependencies = [ - "cfg-if", - "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.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" -dependencies = [ - "deranged", - "itoa 1.0.11", - "libc", - "num-conv", - "num_threads", - "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.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" -dependencies = [ - "num-conv", - "time-core", -] - -[[package]] -name = "tiny-keccak" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" -dependencies = [ - "crunchy", -] - -[[package]] -name = "tinytemplate" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" -dependencies = [ - "serde", - "serde_json", -] - -[[package]] -name = "tinyvec" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tokio" -version = "1.40.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" -dependencies = [ - "backtrace", - "bytes", - "libc", - "mio 1.0.2", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "tracing", - "windows-sys 0.52.0", -] - -[[package]] -name = "tokio-io-timeout" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" -dependencies = [ - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tokio-macros" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[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.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" -dependencies = [ - "rustls 0.23.13", - "rustls-pki-types", - "tokio", -] - -[[package]] -name = "tokio-stream" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", - "tokio-util", -] - -[[package]] -name = "tokio-tungstenite" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" -dependencies = [ - "futures-util", - "log", - "tokio", - "tungstenite 0.21.0", -] - -[[package]] -name = "tokio-util" -version = "0.7.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" -dependencies = [ - "bytes", - "futures-core", - "futures-io", - "futures-sink", - "futures-util", - "hashbrown 0.14.5", - "pin-project-lite", - "slab", - "tokio", -] - -[[package]] -name = "toml" -version = "0.8.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" -dependencies = [ - "indexmap 2.5.0", - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit 0.22.20", -] - -[[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.19.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" -dependencies = [ - "indexmap 2.5.0", - "toml_datetime", - "winnow 0.5.40", -] - -[[package]] -name = "toml_edit" -version = "0.20.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" -dependencies = [ - "indexmap 2.5.0", - "toml_datetime", - "winnow 0.5.40", -] - -[[package]] -name = "toml_edit" -version = "0.22.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" -dependencies = [ - "indexmap 2.5.0", - "serde", - "serde_spanned", - "toml_datetime", - "winnow 0.6.18", -] - -[[package]] -name = "tonic" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76c4eb7a4e9ef9d4763600161f12f5070b92a578e1b634db88a6887844c91a13" -dependencies = [ - "async-stream", - "async-trait", - "axum 0.6.20", - "base64 0.21.7", - "bytes", - "h2 0.3.26", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.30", - "hyper-timeout", - "percent-encoding", - "pin-project", - "prost", - "tokio", - "tokio-stream", - "tower", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" -dependencies = [ - "futures-core", - "futures-util", - "indexmap 1.9.3", - "pin-project", - "pin-project-lite", - "rand 0.8.5", - "slab", - "tokio", - "tokio-util", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-http" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" -dependencies = [ - "async-compression", - "base64 0.21.7", - "bitflags 2.6.0", - "bytes", - "futures-core", - "futures-util", - "http 1.1.0", - "http-body 1.0.1", - "http-body-util", - "http-range-header", - "httpdate", - "iri-string", - "mime", - "mime_guess", - "percent-encoding", - "pin-project-lite", - "tokio", - "tokio-util", - "tower", - "tower-layer", - "tower-service", - "tracing", - "uuid", -] - -[[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.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" -dependencies = [ - "log", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "tracing-core" -version = "0.1.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-fluent-assertions" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12de1a8c6bcfee614305e836308b596bbac831137a04c61f7e5b0b0bf2cfeaf6" -dependencies = [ - "tracing", - "tracing-core", - "tracing-subscriber", -] - -[[package]] -name = "tracing-futures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" -dependencies = [ - "pin-project", - "tracing", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" -dependencies = [ - "matchers", - "nu-ansi-term", - "once_cell", - "regex", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log", -] - -[[package]] -name = "tracing-wasm" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4575c663a174420fa2d78f4108ff68f65bf2fbb7dd89f33749b6e826b3626e07" -dependencies = [ - "tracing", - "tracing-subscriber", - "wasm-bindgen", -] - -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - -[[package]] -name = "trybuild" -version = "1.0.99" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "207aa50d36c4be8d8c6ea829478be44a372c6a77669937bb39c698e52f1491e8" -dependencies = [ - "dissimilar", - "glob", - "serde", - "serde_derive", - "serde_json", - "termcolor", - "toml", -] - -[[package]] -name = "tungstenite" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" -dependencies = [ - "byteorder", - "bytes", - "data-encoding", - "http 1.1.0", - "httparse", - "log", - "rand 0.8.5", - "sha1", - "thiserror", - "url", - "utf-8", -] - -[[package]] -name = "tungstenite" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e2e2ce1e47ed2994fd43b04c8f618008d4cabdd5ee34027cf14f9d918edd9c8" -dependencies = [ - "byteorder", - "bytes", - "data-encoding", - "http 1.1.0", - "httparse", - "log", - "rand 0.8.5", - "sha1", - "thiserror", - "utf-8", -] - -[[package]] -name = "twofish" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a78e83a30223c757c3947cd144a31014ff04298d8719ae10d03c31c0448c8013" -dependencies = [ - "cipher", -] - -[[package]] -name = "twox-hash" -version = "1.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" -dependencies = [ - "cfg-if", - "static_assertions", -] - -[[package]] -name = "typeid" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e13db2e0ccd5e14a544e8a246ba2312cd25223f616442d7f2cb0e3db614236e" - -[[package]] -name = "typenum" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" - -[[package]] -name = "ucd-trie" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" - -[[package]] -name = "uds_windows" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" -dependencies = [ - "memoffset", - "tempfile", - "winapi", -] - -[[package]] -name = "uname" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b72f89f0ca32e4db1c04e2a72f5345d59796d4866a1ee0609084569f73683dc8" -dependencies = [ - "libc", -] - -[[package]] -name = "unic-char-property" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" -dependencies = [ - "unic-char-range", -] - -[[package]] -name = "unic-char-range" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" - -[[package]] -name = "unic-common" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" - -[[package]] -name = "unic-ucd-ident" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987" -dependencies = [ - "unic-char-property", - "unic-char-range", - "unic-ucd-version", -] - -[[package]] -name = "unic-ucd-version" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" -dependencies = [ - "unic-common", -] - -[[package]] -name = "unicase" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" -dependencies = [ - "version_check", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" - -[[package]] -name = "unicode-bom" -version = "2.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eec5d1121208364f6793f7d2e222bf75a915c19557537745b195b253dd64217" - -[[package]] -name = "unicode-ident" -version = "1.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" - -[[package]] -name = "unicode-normalization" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-properties" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ea75f83c0137a9b98608359a5f1af8144876eb67bcb1ce837368e906a9f524" - -[[package]] -name = "unicode-segmentation" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" - -[[package]] -name = "unicode-truncate" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" -dependencies = [ - "itertools 0.13.0", - "unicode-segmentation", - "unicode-width", -] - -[[package]] -name = "unicode-width" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" - -[[package]] -name = "unicode-xid" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a" - -[[package]] -name = "unicode_categories" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" - -[[package]] -name = "universal-hash" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" -dependencies = [ - "crypto-common", - "subtle", -] - -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "ureq" -version = "2.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b74fc6b57825be3373f7054754755f03ac3a8f5d70015ccad699ba2029956f4a" -dependencies = [ - "base64 0.22.1", - "log", - "once_cell", - "rustls 0.23.13", - "rustls-pki-types", - "socks", - "url", - "webpki-roots 0.26.5", -] - -[[package]] -name = "url" -version = "2.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" -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 = "urlpattern" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70acd30e3aa1450bc2eece896ce2ad0d178e9c079493819301573dae3c37ba6d" -dependencies = [ - "regex", - "serde", - "unic-ucd-ident", - "url", -] - -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - -[[package]] -name = "utf8parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" - -[[package]] -name = "uuid" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" -dependencies = [ - "getrandom 0.2.15", - "serde", - "sha1_smol", -] - -[[package]] -name = "valuable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - -[[package]] -name = "value-bag" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a84c137d37ab0142f0f2ddfe332651fdbf252e7b7dbb4e67b6c1f1b2e925101" - -[[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 = "walrus" -version = "0.21.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "160c3708e3ad718ab4d84bec8de8c3d3450cd2902bd6c3ee3bbf50ad7529c2ad" -dependencies = [ - "anyhow", - "gimli 0.26.2", - "id-arena", - "leb128", - "log", - "walrus-macro", - "wasm-encoder", - "wasmparser 0.212.0", -] - -[[package]] -name = "walrus-macro" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e5bd22c71e77d60140b0bd5be56155a37e5bd14e24f5f87298040d0cc40d7" -dependencies = [ - "heck 0.3.3", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -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.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0c672c7629eeed21c37d7a96ee9c0287b86a5e29b5730773117e4261d1a73ca" -dependencies = [ - "pin-project", - "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.77", -] - -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasite" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" - -[[package]] -name = "wasm-bindgen" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" -dependencies = [ - "cfg-if", - "once_cell", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn 2.0.77", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-cli-support" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a7f49ca6e7da74d53d6d716b868828a47d1c3808a8f676e2104fadd3b9874bb" -dependencies = [ - "anyhow", - "base64 0.22.1", - "log", - "rustc-demangle", - "serde_json", - "tempfile", - "unicode-ident", - "walrus", - "wasm-bindgen-externref-xform", - "wasm-bindgen-multi-value-xform", - "wasm-bindgen-shared", - "wasm-bindgen-threads-xform", - "wasm-bindgen-wasm-conventions", - "wasm-bindgen-wasm-interpreter", -] - -[[package]] -name = "wasm-bindgen-externref-xform" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c1f37f2705f4177cc87e5b2763115d078d39e4843e351438b34b085d53a8930" -dependencies = [ - "anyhow", - "walrus", - "wasm-bindgen-wasm-conventions", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-multi-value-xform" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2864e3f221fef3869992b541b238e55f53f99b43c4ce220422a6e1ec9458dc21" -dependencies = [ - "anyhow", - "walrus", - "wasm-bindgen-wasm-conventions", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" - -[[package]] -name = "wasm-bindgen-test" -version = "0.3.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68497a05fb21143a08a7d24fc81763384a3072ee43c44e86aad1744d6adef9d9" -dependencies = [ - "console_error_panic_hook", - "js-sys", - "minicov", - "scoped-tls", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-bindgen-test-macro", -] - -[[package]] -name = "wasm-bindgen-test-macro" -version = "0.3.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b8220be1fa9e4c889b30fd207d4906657e7e90b12e0e6b0c8b8d8709f5de021" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "wasm-bindgen-threads-xform" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e9ca60ee029d64cf6f63a630050935360c3877844f6de38e8287afb8f1d2715" -dependencies = [ - "anyhow", - "walrus", - "wasm-bindgen-wasm-conventions", -] - -[[package]] -name = "wasm-bindgen-wasm-conventions" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e429e00149de60ffc768e6e1f0563778a237e7c11cc01801edf9734bff8161f" -dependencies = [ - "anyhow", - "leb128", - "log", - "walrus", - "wasmparser 0.212.0", -] - -[[package]] -name = "wasm-bindgen-wasm-interpreter" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771c49db324f195f221796bf3868247dd91cca4a85604dcb3729213e439abe59" -dependencies = [ - "anyhow", - "log", - "walrus", - "wasm-bindgen-wasm-conventions", -] - -[[package]] -name = "wasm-encoder" -version = "0.212.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501940df4418b8929eb6d52f1aade1fdd15a5b86c92453cb696e3c906bd3fc33" -dependencies = [ - "leb128", -] - -[[package]] -name = "wasm-logger" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "074649a66bb306c8f2068c9016395fa65d8e08d2affcbf95acf3c24c3ab19718" -dependencies = [ - "log", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-opt" -version = "0.116.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd87a4c135535ffed86123b6fb0f0a5a0bc89e50416c942c5f0662c645f679c" -dependencies = [ - "anyhow", - "libc", - "strum 0.24.1", - "strum_macros 0.24.3", - "tempfile", - "thiserror", - "wasm-opt-cxx-sys", - "wasm-opt-sys", -] - -[[package]] -name = "wasm-opt-cxx-sys" -version = "0.116.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c57b28207aa724318fcec6575fe74803c23f6f266fce10cbc9f3f116762f12e" -dependencies = [ - "anyhow", - "cxx", - "cxx-build", - "wasm-opt-sys", -] - -[[package]] -name = "wasm-opt-sys" -version = "0.116.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a1cce564dc768dacbdb718fc29df2dba80bd21cb47d8f77ae7e3d95ceb98cbe" -dependencies = [ - "anyhow", - "cc", - "cxx", - "cxx-build", -] - -[[package]] -name = "wasm-streams" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" -dependencies = [ - "futures-util", - "js-sys", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "wasmparser" -version = "0.212.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d28bc49ba1e5c5b61ffa7a2eace10820443c4b7d1c0b144109261d14570fdf8" -dependencies = [ - "ahash 0.8.11", - "bitflags 2.6.0", - "hashbrown 0.14.5", - "indexmap 2.5.0", - "semver", - "serde", -] - -[[package]] -name = "wasmparser" -version = "0.216.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcdee6bea3619d311fb4b299721e89a986c3470f804b6d534340e412589028e3" -dependencies = [ - "bitflags 2.6.0", -] - -[[package]] -name = "web-sys" -version = "0.3.70" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" -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", - "wasm-bindgen", -] - -[[package]] -name = "webbrowser" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "425ba64c1e13b1c6e8c5d2541c8fac10022ca584f33da781db01b5756aef1f4e" -dependencies = [ - "block2", - "core-foundation 0.9.4", - "home", - "jni", - "log", - "ndk-context", - "objc2", - "objc2-foundation", - "url", - "web-sys", -] - -[[package]] -name = "webkit2gtk" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76b1bc1e54c581da1e9f179d0b38512ba358fb1af2d634a1affe42e37172361a" -dependencies = [ - "bitflags 1.3.2", - "cairo-rs", - "gdk", - "gdk-sys", - "gio", - "gio-sys", - "glib", - "glib-sys", - "gobject-sys", - "gtk", - "gtk-sys", - "javascriptcore-rs", - "libc", - "once_cell", - "soup3", - "webkit2gtk-sys", -] - -[[package]] -name = "webkit2gtk-sys" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62daa38afc514d1f8f12b8693d30d5993ff77ced33ce30cd04deebc267a6d57c" -dependencies = [ - "bitflags 1.3.2", - "cairo-sys-rs", - "gdk-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "gtk-sys", - "javascriptcore-rs-sys", - "libc", - "pkg-config", - "soup3-sys", - "system-deps", -] - -[[package]] -name = "webpki-roots" -version = "0.25.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" - -[[package]] -name = "webpki-roots" -version = "0.26.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bd24728e5af82c6c4ec1b66ac4844bdf8156257fccda846ec58b42cd0cdbe6a" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "webview2-com" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f61ff3d9d0ee4efcb461b14eb3acfda2702d10dc329f339303fc3e57215ae2c" -dependencies = [ - "webview2-com-macros", - "webview2-com-sys", - "windows", - "windows-core 0.58.0", - "windows-implement", - "windows-interface", -] - -[[package]] -name = "webview2-com-macros" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d228f15bba3b9d56dde8bddbee66fa24545bd17b48d5128ccf4a8742b18e431" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "webview2-com-sys" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3a3e2eeb58f82361c93f9777014668eb3d07e7d174ee4c819575a9208011886" -dependencies = [ - "thiserror", - "windows", - "windows-core 0.58.0", -] - -[[package]] -name = "weezl" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" - -[[package]] -name = "which" -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" -dependencies = [ - "either", - "home", - "once_cell", - "rustix", -] - -[[package]] -name = "whoami" -version = "1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d" -dependencies = [ - "redox_syscall", - "wasite", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.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.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.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-result 0.2.0", - "windows-strings", - "windows-targets 0.52.6", -] - -[[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.77", -] - -[[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.77", -] - -[[package]] -name = "windows-registry" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acc134c90a0318d873ec962b13149e9c862ff0d2669082a709a4810167a3c6ee" -dependencies = [ - "windows-result 0.1.2", - "windows-targets 0.52.6", -] - -[[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-version" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6998aa457c9ba8ff2fb9f13e9d2a930dabcea28f1d0ab94d687d8b3654844515" -dependencies = [ - "windows-targets 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 = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "winnow" -version = "0.5.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" -dependencies = [ - "memchr", -] - -[[package]] -name = "winnow" -version = "0.6.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" -dependencies = [ - "memchr", -] - -[[package]] -name = "wry" -version = "0.43.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4d715cf5fe88e9647f3d17b207b6d060d4a88e7171d4ccb2d2c657dd1d44728" -dependencies = [ - "base64 0.22.1", - "block", - "cocoa", - "core-graphics", - "crossbeam-channel", - "dpi", - "dunce", - "gdkx11", - "gtk", - "html5ever", - "http 1.1.0", - "javascriptcore-rs", - "jni", - "kuchikiki", - "libc", - "ndk", - "objc", - "objc_id", - "once_cell", - "percent-encoding", - "raw-window-handle 0.6.2", - "sha2", - "soup3", - "tao-macros", - "thiserror", - "webkit2gtk", - "webkit2gtk-sys", - "webview2-com", - "windows", - "windows-core 0.58.0", - "windows-version", - "x11-dl", -] - -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] - -[[package]] -name = "x11" -version = "2.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" -dependencies = [ - "libc", - "pkg-config", -] - -[[package]] -name = "x11-dl" -version = "2.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" -dependencies = [ - "libc", - "once_cell", - "pkg-config", -] - -[[package]] -name = "x25519-dalek" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" -dependencies = [ - "curve25519-dalek", - "rand_core 0.6.4", - "serde", - "zeroize", -] - -[[package]] -name = "x509-certificate" -version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66534846dec7a11d7c50a74b7cdb208b9a581cad890b7866430d438455847c85" -dependencies = [ - "bcder", - "bytes", - "chrono", - "der", - "hex", - "pem", - "ring", - "signature", - "spki", - "thiserror", - "zeroize", -] - -[[package]] -name = "xattr" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" -dependencies = [ - "libc", - "linux-raw-sys", - "rustix", -] - -[[package]] -name = "xdg-home" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6" -dependencies = [ - "libc", - "windows-sys 0.59.0", -] - -[[package]] -name = "xxhash-rust" -version = "0.8.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a5cbf750400958819fb6178eaa83bee5cd9c29a26a40cc241df8c70fdd46984" - -[[package]] -name = "xz2" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2" -dependencies = [ - "lzma-sys", -] - -[[package]] -name = "yansi" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" - -[[package]] -name = "zbus" -version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb97012beadd29e654708a0fdb4c84bc046f537aecfde2c3ee0a9e4b4d48c725" -dependencies = [ - "async-broadcast", - "async-process", - "async-recursion", - "async-trait", - "enumflags2", - "event-listener 5.3.1", - "futures-core", - "futures-sink", - "futures-util", - "hex", - "nix 0.29.0", - "ordered-stream", - "rand 0.8.5", - "serde", - "serde_repr", - "sha1", - "static_assertions", - "tokio", - "tracing", - "uds_windows", - "windows-sys 0.52.0", - "xdg-home", - "zbus_macros", - "zbus_names", - "zvariant", -] - -[[package]] -name = "zbus_macros" -version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e" -dependencies = [ - "proc-macro-crate 3.2.0", - "proc-macro2", - "quote", - "syn 2.0.77", - "zvariant_utils", -] - -[[package]] -name = "zbus_names" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" -dependencies = [ - "serde", - "static_assertions", - "zvariant", -] - -[[package]] -name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "byteorder", - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "zeroize" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "zip" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" -dependencies = [ - "aes", - "byteorder", - "bzip2", - "constant_time_eq", - "crc32fast", - "crossbeam-utils", - "flate2", - "hmac", - "pbkdf2", - "sha1", - "time", - "zstd 0.11.2+zstd.1.5.2", -] - -[[package]] -name = "zstd" -version = "0.11.2+zstd.1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" -dependencies = [ - "zstd-safe 5.0.2+zstd.1.5.2", -] - -[[package]] -name = "zstd" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" -dependencies = [ - "zstd-safe 7.2.1", -] - -[[package]] -name = "zstd-safe" -version = "5.0.2+zstd.1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" -dependencies = [ - "libc", - "zstd-sys", -] - -[[package]] -name = "zstd-safe" -version = "7.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" -dependencies = [ - "zstd-sys", -] - -[[package]] -name = "zstd-sys" -version = "2.0.13+zstd.1.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" -dependencies = [ - "cc", - "pkg-config", -] - -[[package]] -name = "zune-inflate" -version = "0.2.54" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" -dependencies = [ - "simd-adler32", -] - -[[package]] -name = "zvariant" -version = "4.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2084290ab9a1c471c38fc524945837734fbf124487e105daec2bb57fd48c81fe" -dependencies = [ - "endi", - "enumflags2", - "serde", - "static_assertions", - "url", - "zvariant_derive", -] - -[[package]] -name = "zvariant_derive" -version = "4.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449" -dependencies = [ - "proc-macro-crate 3.2.0", - "proc-macro2", - "quote", - "syn 2.0.77", - "zvariant_utils", -] - -[[package]] -name = "zvariant_utils" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] diff --git a/Cargo.toml b/Cargo.toml index 144a203ea4..476f11762d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,21 +57,14 @@ members = [ # manganis "packages/manganis", "packages/manganis-macro", - "example-projects/manganis-test-package", - "example-projects/manganis-test-package/test-package-dependency", - "example-projects/manganis-test-package/test-package-nested-dependency", - - # Helpful full projects - "example-projects/hello-world", - "example-projects/router", - "example-projects/streaming", - "example-projects/desktop", - "example-projects/auth", - "example-projects/hackernews", - "example-projects/fullstack-mobile", - "example-projects/tailwind", - "example-projects/pwa", - "example-projects/liveview-router", + + # Fullstack examples + "packages/fullstack/examples/hello-world", + "packages/fullstack/examples/router", + "packages/fullstack/examples/streaming", + "packages/fullstack/examples/desktop", + "packages/fullstack/examples/auth", + "packages/fullstack/examples/hackernews", ] [workspace.package] diff --git a/packages/fullstack/src/axum_adapter.rs b/packages/fullstack/src/axum_adapter.rs index 15de7b2102..4d3a1c6131 100644 --- a/packages/fullstack/src/axum_adapter.rs +++ b/packages/fullstack/src/axum_adapter.rs @@ -8,7 +8,7 @@ //! fn main() { //! #[cfg(feature = "web")] //! // Hydrate the application on the client -//! dioxus::launch(app); +//! launch(app); //! #[cfg(feature = "server")] //! { //! tokio::runtime::Runtime::new() diff --git a/packages/playwright-tests/fullstack/src/main.rs b/packages/playwright-tests/fullstack/src/main.rs index cbd3d0ef88..e52b07c5cd 100644 --- a/packages/playwright-tests/fullstack/src/main.rs +++ b/packages/playwright-tests/fullstack/src/main.rs @@ -8,7 +8,7 @@ use dioxus::{prelude::*, CapturedError}; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/packages/playwright-tests/nested-suspense/src/main.rs b/packages/playwright-tests/nested-suspense/src/main.rs index d859948485..0570313090 100644 --- a/packages/playwright-tests/nested-suspense/src/main.rs +++ b/packages/playwright-tests/nested-suspense/src/main.rs @@ -12,7 +12,7 @@ use dioxus::prelude::*; use serde::{Deserialize, Serialize}; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/packages/playwright-tests/static-generation/src/main.rs b/packages/playwright-tests/static-generation/src/main.rs index 7a9d2ed0ed..f4e870fb1c 100644 --- a/packages/playwright-tests/static-generation/src/main.rs +++ b/packages/playwright-tests/static-generation/src/main.rs @@ -8,7 +8,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/packages/playwright-tests/suspense-carousel/src/main.rs b/packages/playwright-tests/suspense-carousel/src/main.rs index 3339334b40..b3661abe61 100644 --- a/packages/playwright-tests/suspense-carousel/src/main.rs +++ b/packages/playwright-tests/suspense-carousel/src/main.rs @@ -115,5 +115,5 @@ async fn sleep(millis: u64) { } fn main() { - dioxus::launch(app); + launch(app); } diff --git a/packages/playwright-tests/web/src/main.rs b/packages/playwright-tests/web/src/main.rs index 798bec26e5..bbcb9bb57d 100644 --- a/packages/playwright-tests/web/src/main.rs +++ b/packages/playwright-tests/web/src/main.rs @@ -78,5 +78,5 @@ fn main() { .set_max_level(tracing::Level::TRACE) .build(), ); - dioxus::launch(app); + launch(app); } diff --git a/packages/router/examples/manual.rs b/packages/router/examples/manual.rs index 925d1d7351..de3c581cd6 100644 --- a/packages/router/examples/manual.rs +++ b/packages/router/examples/manual.rs @@ -3,7 +3,7 @@ use std::num::ParseIntError; use dioxus::prelude::*; fn main() { - dioxus::launch(|| { + launch(|| { rsx! { Router:: {} } diff --git a/packages/signals/examples/context.rs b/packages/signals/examples/context.rs index e769a3ea8e..1e22423d0e 100644 --- a/packages/signals/examples/context.rs +++ b/packages/signals/examples/context.rs @@ -3,7 +3,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app) + launch(app) } // Because signal is never read in this component, this component will not rerun when the signal changes diff --git a/packages/signals/examples/dependencies.rs b/packages/signals/examples/dependencies.rs index 81fce00a10..70319da403 100644 --- a/packages/signals/examples/dependencies.rs +++ b/packages/signals/examples/dependencies.rs @@ -1,7 +1,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/packages/signals/examples/map_signal.rs b/packages/signals/examples/map_signal.rs index f8a5cd681c..71d882638f 100644 --- a/packages/signals/examples/map_signal.rs +++ b/packages/signals/examples/map_signal.rs @@ -3,7 +3,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/packages/signals/examples/read_only_degrade.rs b/packages/signals/examples/read_only_degrade.rs index 52e6efe889..a69101beca 100644 --- a/packages/signals/examples/read_only_degrade.rs +++ b/packages/signals/examples/read_only_degrade.rs @@ -4,7 +4,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/packages/signals/examples/selector.rs b/packages/signals/examples/selector.rs index 6e282cbc55..c9f578ddb1 100644 --- a/packages/signals/examples/selector.rs +++ b/packages/signals/examples/selector.rs @@ -1,7 +1,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app) + launch(app) } fn app() -> Element { diff --git a/packages/signals/examples/send.rs b/packages/signals/examples/send.rs index 3591d9f058..5d9c5e51f0 100644 --- a/packages/signals/examples/send.rs +++ b/packages/signals/examples/send.rs @@ -1,7 +1,7 @@ use dioxus::prelude::*; fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { diff --git a/packages/signals/examples/split_subscriptions.rs b/packages/signals/examples/split_subscriptions.rs index b041c533d7..da02ba34f7 100644 --- a/packages/signals/examples/split_subscriptions.rs +++ b/packages/signals/examples/split_subscriptions.rs @@ -4,7 +4,7 @@ use dioxus::prelude::*; use dioxus_signals::Signal; fn main() { - dioxus::launch(app); + launch(app); } #[derive(Clone, Copy, Default)] diff --git a/packages/static-generation/examples/router/src/main.rs b/packages/static-generation/examples/router/src/main.rs index b96b6c98de..2d43a264cb 100644 --- a/packages/static-generation/examples/router/src/main.rs +++ b/packages/static-generation/examples/router/src/main.rs @@ -5,7 +5,7 @@ use dioxus::prelude::*; // Generate all routes and output them to the static path fn main() { - dioxus::launch(|| { + launch(|| { rsx! { Router:: {} } diff --git a/packages/static-generation/examples/simple/src/main.rs b/packages/static-generation/examples/simple/src/main.rs index b69315c8e2..b7816277c3 100644 --- a/packages/static-generation/examples/simple/src/main.rs +++ b/packages/static-generation/examples/simple/src/main.rs @@ -3,7 +3,7 @@ use dioxus::prelude::*; // Generate all routes and output them to the static path fn main() { - dioxus::launch(app); + launch(app); } fn app() -> Element { From b9349336d6c443a6098f957aabdafb8dc52036a5 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Sun, 15 Sep 2024 18:33:47 -0700 Subject: [PATCH 115/139] dont nuke fullstack examples --- packages/fullstack/examples/auth/.gitignore | 4 + packages/fullstack/examples/auth/Cargo.toml | 47 +++ packages/fullstack/examples/auth/src/auth.rs | 262 +++++++++++++++ packages/fullstack/examples/auth/src/main.rs | 155 +++++++++ .../fullstack/examples/desktop/.gitignore | 4 + .../fullstack/examples/desktop/Cargo.toml | 14 + .../fullstack/examples/desktop/src/main.rs | 43 +++ .../fullstack/examples/hackernews/.gitignore | 2 + .../fullstack/examples/hackernews/Cargo.toml | 22 ++ .../examples/hackernews/assets/hackernews.css | 17 + .../fullstack/examples/hackernews/src/main.rs | 306 ++++++++++++++++++ .../fullstack/examples/hello-world/.gitignore | 4 + .../fullstack/examples/hello-world/Cargo.toml | 21 ++ .../examples/hello-world/src/main.rs | 54 ++++ packages/fullstack/examples/router/.gitignore | 4 + packages/fullstack/examples/router/Cargo.toml | 19 ++ .../fullstack/examples/router/src/main.rs | 85 +++++ .../fullstack/examples/streaming/.gitignore | 4 + .../fullstack/examples/streaming/Cargo.toml | 24 ++ .../fullstack/examples/streaming/src/main.rs | 41 +++ 20 files changed, 1132 insertions(+) create mode 100644 packages/fullstack/examples/auth/.gitignore create mode 100644 packages/fullstack/examples/auth/Cargo.toml create mode 100644 packages/fullstack/examples/auth/src/auth.rs create mode 100644 packages/fullstack/examples/auth/src/main.rs create mode 100644 packages/fullstack/examples/desktop/.gitignore create mode 100644 packages/fullstack/examples/desktop/Cargo.toml create mode 100644 packages/fullstack/examples/desktop/src/main.rs create mode 100644 packages/fullstack/examples/hackernews/.gitignore create mode 100644 packages/fullstack/examples/hackernews/Cargo.toml create mode 100644 packages/fullstack/examples/hackernews/assets/hackernews.css create mode 100644 packages/fullstack/examples/hackernews/src/main.rs create mode 100644 packages/fullstack/examples/hello-world/.gitignore create mode 100644 packages/fullstack/examples/hello-world/Cargo.toml create mode 100644 packages/fullstack/examples/hello-world/src/main.rs create mode 100644 packages/fullstack/examples/router/.gitignore create mode 100644 packages/fullstack/examples/router/Cargo.toml create mode 100644 packages/fullstack/examples/router/src/main.rs create mode 100644 packages/fullstack/examples/streaming/.gitignore create mode 100644 packages/fullstack/examples/streaming/Cargo.toml create mode 100644 packages/fullstack/examples/streaming/src/main.rs diff --git a/packages/fullstack/examples/auth/.gitignore b/packages/fullstack/examples/auth/.gitignore new file mode 100644 index 0000000000..21fff11dd3 --- /dev/null +++ b/packages/fullstack/examples/auth/.gitignore @@ -0,0 +1,4 @@ +dist +target +static +.dioxus \ No newline at end of file diff --git a/packages/fullstack/examples/auth/Cargo.toml b/packages/fullstack/examples/auth/Cargo.toml new file mode 100644 index 0000000000..a42450dc5c --- /dev/null +++ b/packages/fullstack/examples/auth/Cargo.toml @@ -0,0 +1,47 @@ +[package] +name = "fullstack-auth-example" +version = "0.1.0" +edition = "2021" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +dioxus-web = { workspace = true, features = ["hydrate"], optional = true } +dioxus = { features = ["fullstack"], workspace = true } +dioxus-fullstack = { workspace = true } +axum = { workspace = true, optional = true } +tokio = { workspace = true, features = ["full"], optional = true } +tower-http = { workspace = true, features = ["auth"], optional = true } +simple_logger = { version = "4.2.0", optional = true } +async-trait = { version = "0.1.71", optional = true } +sqlx = { version = "0.7.0", features = [ + "macros", + "migrate", + "postgres", + "sqlite", + "_unstable-all-types", + "tls-native-tls", + "runtime-tokio", +], optional = true } +http = { workspace = true, optional = true } +tower = { workspace = true, optional = true } + +serde = "1.0.159" +execute = "0.2.12" +anyhow = "1.0.71" + +[dependencies.axum_session] +workspace = true +features = ["sqlite-rustls"] +optional = true + +[dependencies.axum_session_auth] +workspace = true +features = ["sqlite-rustls"] +optional = true + +[features] +default = [] +server = ["axum", "tokio", "dioxus-fullstack/axum", "tower-http", "simple_logger", "async-trait", "sqlx", "axum_session", "axum_session_auth", "http", "tower"] +web = ["dioxus-web"] diff --git a/packages/fullstack/examples/auth/src/auth.rs b/packages/fullstack/examples/auth/src/auth.rs new file mode 100644 index 0000000000..9111ce121b --- /dev/null +++ b/packages/fullstack/examples/auth/src/auth.rs @@ -0,0 +1,262 @@ +use async_trait::async_trait; +use axum::{ + http::Method, + response::{IntoResponse, Response}, + routing::get, + Router, +}; +use axum_session::{SessionConfig, SessionLayer, SessionSqlitePool, SessionStore}; +use axum_session_auth::*; +use core::pin::Pin; +use dioxus_fullstack::prelude::*; +use serde::{Deserialize, Serialize}; +use sqlx::sqlite::{SqliteConnectOptions, SqlitePool, SqlitePoolOptions}; +use std::error::Error; +use std::future::Future; +use std::{collections::HashSet, net::SocketAddr, str::FromStr}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct User { + pub id: i32, + pub anonymous: bool, + pub username: String, + pub permissions: HashSet, +} + +#[derive(sqlx::FromRow, Clone)] +pub struct SqlPermissionTokens { + pub token: String, +} + +impl Default for User { + fn default() -> Self { + let mut permissions = HashSet::new(); + + permissions.insert("Category::View".to_owned()); + + Self { + id: 1, + anonymous: true, + username: "Guest".into(), + permissions, + } + } +} + +#[async_trait] +impl Authentication for User { + async fn load_user(userid: i64, pool: Option<&SqlitePool>) -> Result { + let pool = pool.unwrap(); + + User::get_user(userid, pool) + .await + .ok_or_else(|| anyhow::anyhow!("Could not load user")) + } + + fn is_authenticated(&self) -> bool { + !self.anonymous + } + + fn is_active(&self) -> bool { + !self.anonymous + } + + fn is_anonymous(&self) -> bool { + self.anonymous + } +} + +#[async_trait] +impl HasPermission for User { + async fn has(&self, perm: &str, _pool: &Option<&SqlitePool>) -> bool { + self.permissions.contains(perm) + } +} + +impl User { + pub async fn get_user(id: i64, pool: &SqlitePool) -> Option { + let sqluser = sqlx::query_as::<_, SqlUser>("SELECT * FROM users WHERE id = $1") + .bind(id) + .fetch_one(pool) + .await + .ok()?; + + //lets just get all the tokens the user can use, we will only use the full permissions if modifying them. + let sql_user_perms = sqlx::query_as::<_, SqlPermissionTokens>( + "SELECT token FROM user_permissions WHERE user_id = $1;", + ) + .bind(id) + .fetch_all(pool) + .await + .ok()?; + + Some(sqluser.into_user(Some(sql_user_perms))) + } + + pub async fn create_user_tables(pool: &SqlitePool) { + sqlx::query( + r#" + CREATE TABLE IF NOT EXISTS users ( + "id" INTEGER PRIMARY KEY, + "anonymous" BOOLEAN NOT NULL, + "username" VARCHAR(256) NOT NULL + ) + "#, + ) + .execute(pool) + .await + .unwrap(); + + sqlx::query( + r#" + CREATE TABLE IF NOT EXISTS user_permissions ( + "user_id" INTEGER NOT NULL, + "token" VARCHAR(256) NOT NULL + ) + "#, + ) + .execute(pool) + .await + .unwrap(); + + sqlx::query( + r#" + INSERT INTO users + (id, anonymous, username) SELECT 1, true, 'Guest' + ON CONFLICT(id) DO UPDATE SET + anonymous = EXCLUDED.anonymous, + username = EXCLUDED.username + "#, + ) + .execute(pool) + .await + .unwrap(); + + sqlx::query( + r#" + INSERT INTO users + (id, anonymous, username) SELECT 2, false, 'Test' + ON CONFLICT(id) DO UPDATE SET + anonymous = EXCLUDED.anonymous, + username = EXCLUDED.username + "#, + ) + .execute(pool) + .await + .unwrap(); + + sqlx::query( + r#" + INSERT INTO user_permissions + (user_id, token) SELECT 2, 'Category::View' + "#, + ) + .execute(pool) + .await + .unwrap(); + } +} + +#[derive(sqlx::FromRow, Clone)] +pub struct SqlUser { + pub id: i32, + pub anonymous: bool, + pub username: String, +} + +impl SqlUser { + pub fn into_user(self, sql_user_perms: Option>) -> User { + User { + id: self.id, + anonymous: self.anonymous, + username: self.username, + permissions: if let Some(user_perms) = sql_user_perms { + user_perms + .into_iter() + .map(|x| x.token) + .collect::>() + } else { + HashSet::::new() + }, + } + } +} + +pub async fn connect_to_database() -> SqlitePool { + let connect_opts = SqliteConnectOptions::from_str("sqlite::memory:").unwrap(); + + SqlitePoolOptions::new() + .max_connections(5) + .connect_with(connect_opts) + .await + .unwrap() +} + +pub struct Session( + pub axum_session_auth::AuthSession< + crate::auth::User, + i64, + axum_session_auth::SessionSqlitePool, + sqlx::SqlitePool, + >, +); + +impl std::ops::Deref for Session { + type Target = axum_session_auth::AuthSession< + crate::auth::User, + i64, + axum_session_auth::SessionSqlitePool, + sqlx::SqlitePool, + >; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl std::ops::DerefMut for Session { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +#[derive(Debug)] +pub struct AuthSessionLayerNotFound; + +impl std::fmt::Display for AuthSessionLayerNotFound { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "AuthSessionLayer was not found") + } +} + +impl std::error::Error for AuthSessionLayerNotFound {} + +impl IntoResponse for AuthSessionLayerNotFound { + fn into_response(self) -> Response { + ( + http::status::StatusCode::INTERNAL_SERVER_ERROR, + "AuthSessionLayer was not found", + ) + .into_response() + } +} + +#[async_trait] +impl axum::extract::FromRequestParts for Session { + type Rejection = AuthSessionLayerNotFound; + + async fn from_request_parts( + parts: &mut http::request::Parts, + state: &S, + ) -> Result { + axum_session_auth::AuthSession::< + crate::auth::User, + i64, + axum_session_auth::SessionSqlitePool, + sqlx::SqlitePool, + >::from_request_parts(parts, state) + .await + .map(Session) + .map_err(|_| AuthSessionLayerNotFound) + } +} diff --git a/packages/fullstack/examples/auth/src/main.rs b/packages/fullstack/examples/auth/src/main.rs new file mode 100644 index 0000000000..ec8097997b --- /dev/null +++ b/packages/fullstack/examples/auth/src/main.rs @@ -0,0 +1,155 @@ +//! Run with: +//! +//! ```sh +//! dx build --features web +//! cargo run --features server +//! ``` + +#![allow(non_snake_case, unused)] + +#[cfg(feature = "server")] +mod auth; + +use dioxus::prelude::*; +use dioxus_fullstack::prelude::*; +use serde::{Deserialize, Serialize}; + +fn main() { + #[cfg(feature = "web")] + // Hydrate the application on the client + dioxus_web::launch::launch_cfg(app, dioxus_web::Config::new().hydrate(true)); + + #[cfg(feature = "server")] + { + use crate::auth::*; + use axum::routing::*; + use axum_session::SessionConfig; + use axum_session::SessionStore; + use axum_session_auth::AuthConfig; + use axum_session_auth::SessionSqlitePool; + simple_logger::SimpleLogger::new().init().unwrap(); + tokio::runtime::Runtime::new() + .unwrap() + .block_on(async move { + let pool = connect_to_database().await; + + //This Defaults as normal Cookies. + //To enable Private cookies for integrity, and authenticity please check the next Example. + let session_config = SessionConfig::default().with_table_name("test_table"); + let auth_config = AuthConfig::::default().with_anonymous_user_id(Some(1)); + let session_store = SessionStore::::new( + Some(pool.clone().into()), + session_config, + ) + .await + .unwrap(); + + User::create_user_tables(&pool).await; + + // build our application with some routes + let app = Router::new() + // Server side render the application, serve static assets, and register server functions + .serve_dioxus_application(ServeConfig::new().unwrap(), app) + .layer( + axum_session_auth::AuthSessionLayer::< + crate::auth::User, + i64, + axum_session_auth::SessionSqlitePool, + sqlx::SqlitePool, + >::new(Some(pool)) + .with_config(auth_config), + ) + .layer(axum_session::SessionLayer::new(session_store)); + + // run it + let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 3000)); + let listener = tokio::net::TcpListener::bind(&addr).await.unwrap(); + + axum::serve(listener, app.into_make_service()) + .await + .unwrap(); + }); + } +} +// +fn app() -> Element { + let mut user_name = use_signal(|| "?".to_string()); + let mut permissions = use_signal(|| "?".to_string()); + + rsx! { + div { + button { onclick: move |_| { + async move { + login().await.unwrap(); + } + }, + "Login Test User" + } + } + div { + button { + onclick: move |_| async move { + if let Ok(data) = get_user_name().await { + user_name.set(data); + } + }, + "Get User Name" + } + "User name: {user_name}" + } + div { + button { + onclick: move |_| async move { + if let Ok(data) = get_permissions().await { + permissions.set(data); + } + }, + "Get Permissions" + } + "Permissions: {permissions}" + } + } +} + +#[server(GetUserName)] +pub async fn get_user_name() -> Result { + let session: crate::auth::Session = extract().await?; + Ok(session.0.current_user.unwrap().username.to_string()) +} + +#[server(Login)] +pub async fn login() -> Result<(), ServerFnError> { + let auth: crate::auth::Session = extract().await?; + auth.login_user(2); + Ok(()) +} + +#[server(Permissions)] +pub async fn get_permissions() -> Result { + let method: axum::http::Method = extract().await?; + let auth: crate::auth::Session = extract().await?; + let current_user = auth.current_user.clone().unwrap_or_default(); + + // lets check permissions only and not worry about if they are anon or not + if !axum_session_auth::Auth::::build( + [axum::http::Method::POST], + false, + ) + .requires(axum_session_auth::Rights::any([ + axum_session_auth::Rights::permission("Category::View"), + axum_session_auth::Rights::permission("Admin::View"), + ])) + .validate(¤t_user, &method, None) + .await + { + return Ok(format!( + "User {}, Does not have permissions needed to view this page please login", + current_user.username + )); + } + + Ok(format!( + "User has Permissions needed. Here are the Users permissions: {:?}", + current_user.permissions + )) +} diff --git a/packages/fullstack/examples/desktop/.gitignore b/packages/fullstack/examples/desktop/.gitignore new file mode 100644 index 0000000000..21fff11dd3 --- /dev/null +++ b/packages/fullstack/examples/desktop/.gitignore @@ -0,0 +1,4 @@ +dist +target +static +.dioxus \ No newline at end of file diff --git a/packages/fullstack/examples/desktop/Cargo.toml b/packages/fullstack/examples/desktop/Cargo.toml new file mode 100644 index 0000000000..008a6a9f13 --- /dev/null +++ b/packages/fullstack/examples/desktop/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "fullstack-desktop-example" +version = "0.1.0" +edition = "2021" +publish = false + +[dependencies] +dioxus = { workspace = true, features = ["launch", "fullstack"] } +serde = "1.0.159" + +[features] +default = [] +server = ["dioxus/server"] +desktop = ["dioxus/desktop"] diff --git a/packages/fullstack/examples/desktop/src/main.rs b/packages/fullstack/examples/desktop/src/main.rs new file mode 100644 index 0000000000..0e39cc69d7 --- /dev/null +++ b/packages/fullstack/examples/desktop/src/main.rs @@ -0,0 +1,43 @@ +#![allow(non_snake_case)] +use dioxus::prelude::*; + +fn main() { + // Set the url of the server where server functions are hosted. + #[cfg(not(feature = "server"))] + dioxus::fullstack::prelude::server_fn::client::set_server_url("http://127.0.0.1:8080"); + launch(app); +} + +pub fn app() -> Element { + let mut count = use_signal(|| 0); + let mut text = use_signal(|| "...".to_string()); + + rsx! { + h1 { "High-Five counter: {count}" } + button { onclick: move |_| count += 1, "Up high!" } + button { onclick: move |_| count -= 1, "Down low!" } + button { + onclick: move |_| async move { + if let Ok(data) = get_server_data().await { + println!("Client received: {}", data); + text.set(data.clone()); + post_server_data(data).await.unwrap(); + } + }, + "Run a server function" + } + "Server said: {text}" + } +} + +#[server(PostServerData)] +async fn post_server_data(data: String) -> Result<(), ServerFnError> { + println!("Server received: {}", data); + + Ok(()) +} + +#[server(GetServerData)] +async fn get_server_data() -> Result { + Ok("Hello from the server!".to_string()) +} diff --git a/packages/fullstack/examples/hackernews/.gitignore b/packages/fullstack/examples/hackernews/.gitignore new file mode 100644 index 0000000000..12cf96b284 --- /dev/null +++ b/packages/fullstack/examples/hackernews/.gitignore @@ -0,0 +1,2 @@ +/static +/dist \ No newline at end of file diff --git a/packages/fullstack/examples/hackernews/Cargo.toml b/packages/fullstack/examples/hackernews/Cargo.toml new file mode 100644 index 0000000000..68c247573e --- /dev/null +++ b/packages/fullstack/examples/hackernews/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "fullstack-hackernews-example" +version = "0.1.0" +authors = ["Evan Almloff "] +edition = "2021" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +dioxus = { workspace = true, features = ["fullstack", "router"] } +chrono = { version = "0.4.38", features = ["serde"] } +reqwest = { workspace= true, features = ["json"] } +serde = { workspace = true, features = ["derive"] } +tracing-wasm = "0.2.1" +tracing = { workspace = true } +tracing-subscriber = "0.3.17" + +[features] +default = [] +server = ["dioxus/axum"] +web = ["dioxus/web"] diff --git a/packages/fullstack/examples/hackernews/assets/hackernews.css b/packages/fullstack/examples/hackernews/assets/hackernews.css new file mode 100644 index 0000000000..cd01ad879e --- /dev/null +++ b/packages/fullstack/examples/hackernews/assets/hackernews.css @@ -0,0 +1,17 @@ +@keyframes spin { + 0% { + transform: rotate(0deg); + } + + 100% { + transform: rotate(360deg); + } +} +.spinner { + width: 10px; + height: 10px; + border: 4px solid #f3f3f3; + border-top: 4px solid #3498db; + border-radius: 50%; + animation: spin 2s linear infinite; +} \ No newline at end of file diff --git a/packages/fullstack/examples/hackernews/src/main.rs b/packages/fullstack/examples/hackernews/src/main.rs new file mode 100644 index 0000000000..62bf9d8f7b --- /dev/null +++ b/packages/fullstack/examples/hackernews/src/main.rs @@ -0,0 +1,306 @@ +#![allow(non_snake_case, unused)] +use dioxus::prelude::*; +// Define the Hackernews API and types +use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; +use std::{ + fmt::{Display, Formatter}, + num::ParseIntError, + str::FromStr, +}; +use svg_attributes::to; + +fn main() { + #[cfg(feature = "web")] + tracing_wasm::set_as_global_default(); + + #[cfg(feature = "server")] + tracing_subscriber::fmt::init(); + + launch(|| rsx! { Router:: {} }); +} + +#[derive(Clone, Routable)] +enum Route { + #[redirect("/", || Route::Homepage { story: PreviewState { active_story: None } })] + #[route("/:story")] + Homepage { story: PreviewState }, +} + +pub fn App() -> Element { + rsx! { + Router:: {} + } +} + +#[component] +fn Homepage(story: ReadOnlySignal) -> Element { + rsx! { + head::Link { rel: "stylesheet", href: asset!("./assets/hackernews.css") } + div { display: "flex", flex_direction: "row", width: "100%", + div { + width: "50%", + SuspenseBoundary { + fallback: |context: SuspenseContext| rsx! { + "Loading..." + }, + Stories {} + } + } + div { width: "50%", + SuspenseBoundary { + fallback: |context: SuspenseContext| rsx! { + "Loading preview..." + }, + Preview { + story + } + } + } + } + } +} + +#[component] +fn Stories() -> Element { + let stories: Resource>> = use_server_future(move || async move { + let url = format!("{}topstories.json", BASE_API_URL); + let mut stories_ids = reqwest::get(&url).await?.json::>().await?; + stories_ids.truncate(30); + Ok(stories_ids) + })?; + + match stories().unwrap() { + Ok(list) => rsx! { + div { + for story in list { + ChildrenOrLoading { + key: "{story}", + StoryListing { story } + } + } + } + }, + Err(err) => rsx! {"An error occurred while fetching stories {err}"}, + } +} + +#[component] +fn StoryListing(story: ReadOnlySignal) -> Element { + let story = use_server_future(move || get_story(story()))?; + + let StoryItem { + title, + url, + by, + score, + time, + kids, + id, + .. + } = story().unwrap()?.item; + + let url = url.as_deref().unwrap_or_default(); + let hostname = url + .trim_start_matches("https://") + .trim_start_matches("http://") + .trim_start_matches("www."); + let score = format!("{score} {}", if score == 1 { " point" } else { " points" }); + let comments = format!( + "{} {}", + kids.len(), + if kids.len() == 1 { + " comment" + } else { + " comments" + } + ); + let time = time.format("%D %l:%M %p"); + + rsx! { + div { + padding: "0.5rem", + position: "relative", + div { font_size: "1.5rem", + Link { + to: Route::Homepage { story: PreviewState { active_story: Some(id) } }, + "{title}" + } + a { + color: "gray", + href: "https://news.ycombinator.com/from?site={hostname}", + text_decoration: "none", + " ({hostname})" + } + } + div { display: "flex", flex_direction: "row", color: "gray", + div { "{score}" } + div { padding_left: "0.5rem", "by {by}" } + div { padding_left: "0.5rem", "{time}" } + div { padding_left: "0.5rem", "{comments}" } + } + } + } +} + +#[derive(Clone, Debug, Default)] +struct PreviewState { + active_story: Option, +} + +impl FromStr for PreviewState { + type Err = ParseIntError; + + fn from_str(s: &str) -> Result { + let state = i64::from_str(s)?; + Ok(PreviewState { + active_story: Some(state), + }) + } +} + +impl Display for PreviewState { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + if let Some(id) = &self.active_story { + write!(f, "{id}")?; + } + Ok(()) + } +} + +#[component] +fn Preview(story: ReadOnlySignal) -> Element { + let PreviewState { + active_story: Some(id), + } = story() + else { + return rsx! {"Hover over a story to preview it here"}; + }; + + let story = use_server_future(use_reactive!(|id| get_story(id)))?; + + let story = story().unwrap()?; + + rsx! { + div { padding: "0.5rem", + div { font_size: "1.5rem", a { href: story.item.url, "{story.item.title}" } } + if let Some(text) = &story.item.text { div { dangerous_inner_html: "{text}" } } + for comment in story.item.kids.iter().copied() { + ChildrenOrLoading { + key: "{comment}", + Comment { comment } + } + } + } + } +} + +#[component] +fn Comment(comment: i64) -> Element { + let comment: Resource> = + use_server_future(use_reactive!(|comment| async move { + let url = format!("{}{}{}.json", BASE_API_URL, ITEM_API, comment); + let mut comment = reqwest::get(&url).await?.json::().await?; + Ok(comment) + }))?; + + let CommentData { + by, + time, + text, + id, + kids, + .. + } = comment().unwrap()?; + + rsx! { + div { padding: "0.5rem", + div { color: "gray", "by {by}" } + div { dangerous_inner_html: "{text}" } + for comment in kids.iter().copied() { + ChildrenOrLoading { + key: "{comment}", + Comment { comment } + } + } + } + } +} + +pub static BASE_API_URL: &str = "https://hacker-news.firebaseio.com/v0/"; +pub static ITEM_API: &str = "item/"; +pub static USER_API: &str = "user/"; +const COMMENT_DEPTH: i64 = 1; + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct StoryPageData { + #[serde(flatten)] + pub item: StoryItem, + #[serde(default)] + pub comments: Vec, +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct CommentData { + pub id: i64, + /// there will be no by field if the comment was deleted + #[serde(default)] + pub by: String, + #[serde(default)] + pub text: String, + #[serde(with = "chrono::serde::ts_seconds")] + pub time: DateTime, + #[serde(default)] + pub kids: Vec, + pub r#type: String, +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct StoryItem { + pub id: i64, + pub title: String, + pub url: Option, + pub text: Option, + #[serde(default)] + pub by: String, + #[serde(default)] + pub score: i64, + #[serde(default)] + pub descendants: i64, + #[serde(with = "chrono::serde::ts_seconds")] + pub time: DateTime, + #[serde(default)] + pub kids: Vec, + pub r#type: String, +} + +pub async fn get_story(id: i64) -> dioxus::Result { + let url = format!("{}{}{}.json", BASE_API_URL, ITEM_API, id); + Ok(reqwest::get(&url).await?.json::().await?) +} + +#[component] +fn ChildrenOrLoading(children: Element) -> Element { + rsx! { + SuspenseBoundary { + fallback: |context: SuspenseContext| { + rsx! { + if let Some(placeholder) = context.suspense_placeholder() { + {placeholder} + } else { + LoadingIndicator {} + } + } + }, + children + } + } +} + +fn LoadingIndicator() -> Element { + rsx! { + div { + class: "spinner", + } + } +} diff --git a/packages/fullstack/examples/hello-world/.gitignore b/packages/fullstack/examples/hello-world/.gitignore new file mode 100644 index 0000000000..21fff11dd3 --- /dev/null +++ b/packages/fullstack/examples/hello-world/.gitignore @@ -0,0 +1,4 @@ +dist +target +static +.dioxus \ No newline at end of file diff --git a/packages/fullstack/examples/hello-world/Cargo.toml b/packages/fullstack/examples/hello-world/Cargo.toml new file mode 100644 index 0000000000..16bb88cfff --- /dev/null +++ b/packages/fullstack/examples/hello-world/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "fullstack-hello-world-example" +version = "0.1.0" +edition = "2021" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +dioxus = { workspace = true, features = ["fullstack"]} +serde = "1.0.159" +simple_logger = "4.2.0" +tracing-wasm = "0.2.1" +tracing = { workspace = true } +tracing-subscriber = "0.3.17" +reqwest = { workspace = true } + +[features] +default = [] +server = ["dioxus/axum"] +web = ["dioxus/web"] diff --git a/packages/fullstack/examples/hello-world/src/main.rs b/packages/fullstack/examples/hello-world/src/main.rs new file mode 100644 index 0000000000..513791fa4c --- /dev/null +++ b/packages/fullstack/examples/hello-world/src/main.rs @@ -0,0 +1,54 @@ +//! Run with: +//! +//! ```sh +//! dx serve --platform fullstack +//! ``` + +#![allow(non_snake_case, unused)] +use dioxus::prelude::*; +use serde::{Deserialize, Serialize}; + +fn app() -> Element { + let mut count = use_signal(|| 0); + let mut text = use_signal(|| "...".to_string()); + let server_future = use_server_future(get_server_data)?; + + rsx! { + h1 { "High-Five counter: {count}" } + button { onclick: move |_| count += 1, "Up high!" } + button { onclick: move |_| count -= 1, "Down low!" } + button { + onclick: move |_| async move { + if let Ok(data) = get_server_data().await { + println!("Client received: {}", data); + text.set(data.clone()); + post_server_data(data).await.unwrap(); + } + }, + "Run a server function!" + } + "Server said: {text}" + } +} + +#[server] +async fn post_server_data(data: String) -> Result<(), ServerFnError> { + println!("Server received: {}", data); + + Ok(()) +} + +#[server] +async fn get_server_data() -> Result { + Ok(reqwest::get("https://httpbin.org/ip").await?.text().await?) +} + +fn main() { + #[cfg(feature = "web")] + tracing_wasm::set_as_global_default(); + + #[cfg(feature = "server")] + tracing_subscriber::fmt::init(); + + launch(app); +} diff --git a/packages/fullstack/examples/router/.gitignore b/packages/fullstack/examples/router/.gitignore new file mode 100644 index 0000000000..21fff11dd3 --- /dev/null +++ b/packages/fullstack/examples/router/.gitignore @@ -0,0 +1,4 @@ +dist +target +static +.dioxus \ No newline at end of file diff --git a/packages/fullstack/examples/router/Cargo.toml b/packages/fullstack/examples/router/Cargo.toml new file mode 100644 index 0000000000..9f92d87aa1 --- /dev/null +++ b/packages/fullstack/examples/router/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "fullstack-router-example" +version = "0.1.0" +edition = "2021" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +dioxus = { workspace = true, features = ["fullstack", "router"] } +axum = { workspace = true, optional = true } +tokio = {workspace = true, features = ["full"], optional = true } +serde = { version = "1.0.159", features = ["derive"] } + +[features] +default = [] +server = ["axum", "dioxus/axum"] +web = ["dioxus/web"] + diff --git a/packages/fullstack/examples/router/src/main.rs b/packages/fullstack/examples/router/src/main.rs new file mode 100644 index 0000000000..2240291b26 --- /dev/null +++ b/packages/fullstack/examples/router/src/main.rs @@ -0,0 +1,85 @@ +//! Run with: +//! +//! ```sh +//! dx serve --platform fullstack +//! ``` + +use dioxus::prelude::*; + +fn main() { + LaunchBuilder::fullstack() + .with_cfg(server_only!(ServeConfig::builder().incremental( + IncrementalRendererConfig::default() + .invalidate_after(std::time::Duration::from_secs(120)), + ))) + .launch(app); +} + +fn app() -> Element { + rsx! { Router:: {} } +} + +#[derive(Clone, Routable, Debug, PartialEq, serde::Serialize, serde::Deserialize)] +enum Route { + #[route("/")] + Home {}, + + #[route("/blog/:id/")] + Blog { id: i32 }, +} + +#[component] +fn Blog(id: i32) -> Element { + rsx! { + Link { to: Route::Home {}, "Go to counter" } + table { + tbody { + for _ in 0..id { + tr { + for _ in 0..id { + td { "hello world!" } + } + } + } + } + } + } +} + +#[component] +fn Home() -> Element { + let mut count = use_signal(|| 0); + let mut text = use_signal(|| "...".to_string()); + + rsx! { + Link { to: Route::Blog { id: count() }, "Go to blog" } + div { + h1 { "High-Five counter: {count}" } + button { onclick: move |_| count += 1, "Up high!" } + button { onclick: move |_| count -= 1, "Down low!" } + button { + onclick: move |_| async move { + if let Ok(data) = get_server_data().await { + println!("Client received: {}", data); + text.set(data.clone()); + post_server_data(data).await.unwrap(); + } + }, + "Run server function!" + } + "Server said: {text}" + } + } +} + +#[server(PostServerData)] +async fn post_server_data(data: String) -> Result<(), ServerFnError> { + println!("Server received: {}", data); + + Ok(()) +} + +#[server(GetServerData)] +async fn get_server_data() -> Result { + Ok("Hello from the server!".to_string()) +} diff --git a/packages/fullstack/examples/streaming/.gitignore b/packages/fullstack/examples/streaming/.gitignore new file mode 100644 index 0000000000..21fff11dd3 --- /dev/null +++ b/packages/fullstack/examples/streaming/.gitignore @@ -0,0 +1,4 @@ +dist +target +static +.dioxus \ No newline at end of file diff --git a/packages/fullstack/examples/streaming/Cargo.toml b/packages/fullstack/examples/streaming/Cargo.toml new file mode 100644 index 0000000000..29eba049ad --- /dev/null +++ b/packages/fullstack/examples/streaming/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "fullstack-streaming-example" +version = "0.1.0" +edition = "2021" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +dioxus = { workspace = true, features = ["fullstack"] } +serde = "1.0.159" +simple_logger = "4.2.0" +tracing-wasm = "0.2.1" +tracing.workspace = true +tracing-subscriber = "0.3.17" +futures = "0.3.30" +tokio = { workspace = true, optional = true } +futures-util.workspace = true +once_cell = "1.19.0" + +[features] +default = [] +server = ["dioxus/axum", "tokio"] +web = ["dioxus/web"] diff --git a/packages/fullstack/examples/streaming/src/main.rs b/packages/fullstack/examples/streaming/src/main.rs new file mode 100644 index 0000000000..fc6e82c020 --- /dev/null +++ b/packages/fullstack/examples/streaming/src/main.rs @@ -0,0 +1,41 @@ +use dioxus::prelude::*; +use futures::StreamExt; +use server_fn::codec::{StreamingText, TextStream}; + +fn app() -> Element { + let mut response = use_signal(String::new); + + rsx! { + button { + onclick: move |_| async move { + response.write().clear(); + if let Ok(stream) = test_stream().await { + response.write().push_str("Stream started\n"); + let mut stream = stream.into_inner(); + while let Some(Ok(text)) = stream.next().await { + response.write().push_str(&text); + } + } + }, + "Start stream" + } + "{response}" + } +} + +#[server(output = StreamingText)] +pub async fn test_stream() -> Result { + let (tx, rx) = futures::channel::mpsc::unbounded(); + tokio::spawn(async move { + loop { + tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; + let _ = tx.unbounded_send(Ok("Hello, world!".to_string())); + } + }); + + Ok(TextStream::new(rx)) +} + +fn main() { + launch(app) +} From dc3a5ebfada3b474022a6f8038722cee4a40e516 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Sun, 15 Sep 2024 18:35:04 -0700 Subject: [PATCH 116/139] dont move random line --- Cargo.lock | 11606 +++++++++++++++++++++++++++++++ packages/core-macro/src/lib.rs | 20 +- 2 files changed, 11616 insertions(+), 10 deletions(-) create mode 100644 Cargo.lock diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000000..06ae21f766 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,11606 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" +dependencies = [ + "gimli 0.31.0", +] + +[[package]] +name = "adler" +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 = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array 0.14.7", +] + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom 0.2.15", + "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", + "const-random", + "getrandom 0.2.15", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "aligned" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "377e4c0ba83e4431b10df45c1d4666f178ea9c552cac93e60c3a88bf32785923" +dependencies = [ + "as-slice", +] + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "ansi-to-html" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d73c455ae09fa2223a75114789f30ad605e9e297f79537953523366c05995f5f" +dependencies = [ + "regex", + "thiserror", +] + +[[package]] +name = "ansi-to-tui" +version = "5.0.0-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "428c2992b874104caf39204b05bf89eab4ceefdd4fcb26caa6759906f547f8e8" +dependencies = [ + "nom", + "ratatui", + "simdutf8", + "smallvec", + "thiserror", +] + +[[package]] +name = "anstream" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" + +[[package]] +name = "anstyle-parse" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + +[[package]] +name = "anyhow" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" + +[[package]] +name = "anymap2" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c" + +[[package]] +name = "ar" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d67af77d68a931ecd5cbd8a3b5987d63a1d1d1278f7f6a60ae33db485cdebb69" + +[[package]] +name = "arc-swap" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "as-slice" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516" +dependencies = [ + "stable_deref_trait", +] + +[[package]] +name = "ashpd" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd884d7c72877a94102c3715f3b1cd09ff4fac28221add3e57cfbe25c236d093" +dependencies = [ + "enumflags2", + "futures-channel", + "futures-util", + "rand 0.8.5", + "serde", + "serde_repr", + "tokio", + "url", + "zbus", +] + +[[package]] +name = "askama_escape" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341" + +[[package]] +name = "async-broadcast" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cd0e2e25ea8e5f7e9df04578dc6cf5c83577fd09b1a46aaf5c85e1c33f2a7e" +dependencies = [ + "event-listener 5.3.1", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener 2.5.3", + "futures-core", +] + +[[package]] +name = "async-channel" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-compression" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fec134f64e2bc57411226dfc4e52dec859ddfc7e711fc5e07b612584f000e4aa" +dependencies = [ + "brotli", + "flate2", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", + "zstd 0.13.2", + "zstd-safe 7.2.1", +] + +[[package]] +name = "async-executor" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "slab", +] + +[[package]] +name = "async-global-executor" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" +dependencies = [ + "async-channel 2.3.1", + "async-executor", + "async-io", + "async-lock", + "blocking", + "futures-lite", + "once_cell", +] + +[[package]] +name = "async-io" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "444b0228950ee6501b3568d3c93bf1176a1fdbc3b758dcd9475046d30f4dc7e8" +dependencies = [ + "async-lock", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "async-lock" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener 5.3.1", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-process" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb" +dependencies = [ + "async-channel 2.3.1", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener 5.3.1", + "futures-lite", + "rustix", + "tracing", +] + +[[package]] +name = "async-recursion" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "async-signal" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix", + "signal-hook-registry", + "slab", + "windows-sys 0.59.0", +] + +[[package]] +name = "async-std" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c634475f29802fde2b8f0b505b1bd00dfe4df7d4a000f0b36f7671197d5c3615" +dependencies = [ + "async-channel 1.9.0", + "async-global-executor", + "async-io", + "async-lock", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite", + "gloo-timers 0.3.0", + "kv-log-macro", + "log", + "memchr", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] + +[[package]] +name = "async-stream" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "async-trait" +version = "0.1.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "atk" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4af014b17dd80e8af9fa689b2d4a211ddba6eb583c1622f35d0cb543f6b17e4" +dependencies = [ + "atk-sys", + "glib", + "libc", +] + +[[package]] +name = "atk-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "251e0b7d90e33e0ba930891a505a9a35ece37b2dd37a14f3ffc306c13b980009" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "atoi" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" +dependencies = [ + "num-traits", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[package]] +name = "auth-git2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3810b5af212b013fe7302b12d86616c6c39a48e18f2e4b812a5a9e5710213791" +dependencies = [ + "dirs", + "git2", + "terminal-prompt", +] + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "aws-lc-rs" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f95446d919226d587817a7d21379e6eb099b97b45110a7f272a444ca5c54070" +dependencies = [ + "aws-lc-sys", + "mirai-annotations", + "paste", + "untrusted 0.7.1", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234314bd569802ec87011d653d6815c6d7b9ffb969e9fee5b8b20ef860e8dce9" +dependencies = [ + "bindgen", + "cc", + "cmake", + "dunce", + "fs_extra", + "libc", + "paste", +] + +[[package]] +name = "axum" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" +dependencies = [ + "async-trait", + "axum-core 0.3.4", + "bitflags 1.3.2", + "bytes", + "futures-util", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.30", + "itoa 1.0.11", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "sync_wrapper 0.1.2", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" +dependencies = [ + "async-trait", + "axum-core 0.4.3", + "axum-macros", + "base64 0.21.7", + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.4.1", + "hyper-util", + "itoa 1.0.11", + "matchit", + "memchr", + "mime", + "multer", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sha1", + "sync_wrapper 1.0.1", + "tokio", + "tokio-tungstenite", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 0.2.12", + "http-body 0.4.6", + "mime", + "rustversion", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper 0.1.2", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-extra" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0be6ea09c9b96cb5076af0de2e383bd2bc0c18f827cf1967bdd353e0b910d733" +dependencies = [ + "axum 0.7.5", + "axum-core 0.4.3", + "bytes", + "futures-util", + "headers", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "mime", + "pin-project-lite", + "serde", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00c055ee2d014ae5981ce1016374e8213682aa14d9bf40e48ab48b5f3ef20eaa" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "axum-server" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56bac90848f6a9393ac03c63c640925c4b7c8ca21654de40d53f55964667c7d8" +dependencies = [ + "arc-swap", + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.4.1", + "hyper-util", + "pin-project-lite", + "rustls 0.23.13", + "rustls-pemfile 2.1.3", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower", + "tower-service", +] + +[[package]] +name = "axum_session" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0f0035d1d5a70ac279e80a75e60592023ba8b327c929c3827f6c5ed749b9f8b" +dependencies = [ + "aes-gcm", + "async-trait", + "axum 0.7.5", + "base64 0.21.7", + "bytes", + "chrono", + "cookie", + "dashmap", + "forwarded-header-value", + "futures", + "hmac", + "http 1.1.0", + "http-body 1.0.1", + "rand 0.8.5", + "serde", + "serde_json", + "sha2", + "sqlx", + "thiserror", + "tokio", + "tower-layer", + "tower-service", + "tracing", + "uuid", +] + +[[package]] +name = "axum_session_auth" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd4c620e865c92d9fca3ad57efeb4baef271f3ccdba163928002f13489952660" +dependencies = [ + "anyhow", + "async-recursion", + "async-trait", + "axum-core 0.4.3", + "axum_session", + "bytes", + "chrono", + "dashmap", + "futures", + "http 1.1.0", + "http-body 1.0.1", + "serde", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide 0.8.0", + "object", + "rustc-demangle", + "windows-targets 0.52.6", +] + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bcder" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c627747a6774aab38beb35990d88309481378558875a41da1a4b2e373c906ef0" +dependencies = [ + "bytes", + "smallvec", +] + +[[package]] +name = "bigdecimal" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6773ddc0eafc0e509fb60e48dff7f450f8e674a0686ae8605e8d9901bd5eefa" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bindgen" +version = "0.69.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" +dependencies = [ + "bitflags 2.6.0", + "cexpr", + "clang-sys", + "itertools 0.12.1", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash 1.1.0", + "shlex", + "syn 2.0.77", + "which", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + +[[package]] +name = "bitfield" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d7e60934ceec538daadb9d8432424ed043a904d8e0243f3c6446bce549a46ac" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +dependencies = [ + "serde", +] + +[[package]] +name = "bitness" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57792b99d555ebf109c83169228076f7d997e2b37ba1a653850ccd703ac7bab0" +dependencies = [ + "sysctl", + "thiserror", + "uname", + "winapi", +] + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "block-padding" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "block2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" +dependencies = [ + "objc2", +] + +[[package]] +name = "blocking" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +dependencies = [ + "async-channel 2.3.1", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + +[[package]] +name = "blowfish" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7" +dependencies = [ + "byteorder", + "cipher", +] + +[[package]] +name = "borsh" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6362ed55def622cddc70a4746a68554d7b687713770de539e59a739b249f8ed" +dependencies = [ + "borsh-derive", + "cfg_aliases 0.2.1", +] + +[[package]] +name = "borsh-derive" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ef8005764f53cd4dca619f5bf64cafd4664dada50ece25e4d81de54c80cc0b" +dependencies = [ + "once_cell", + "proc-macro-crate 3.2.0", + "proc-macro2", + "quote", + "syn 2.0.77", + "syn_derive", +] + +[[package]] +name = "brotli" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bstr" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" +dependencies = [ + "memchr", + "regex-automata 0.4.7", + "serde", +] + +[[package]] +name = "buffer-redux" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e8acf87c5b9f5897cd3ebb9a327f420e0cae9dd4e5c1d2e36f2c84c571a58f1" +dependencies = [ + "memchr", +] + +[[package]] +name = "built" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "236e6289eda5a812bc6b53c3b024039382a2895fbbeef2d748b2931546d392c4" +dependencies = [ + "git2", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "bytecheck" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "bytemuck" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" + +[[package]] +name = "bzip2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" +dependencies = [ + "bzip2-sys", + "libc", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.11+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] +name = "cairo-rs" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" +dependencies = [ + "bitflags 2.6.0", + "cairo-sys-rs", + "glib", + "libc", + "once_cell", + "thiserror", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "camellia" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3264e2574e9ef2b53ce6f536dea83a69ac0bc600b762d1523ff83fe07230ce30" +dependencies = [ + "byteorder", + "cipher", +] + +[[package]] +name = "camino" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-config2" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1124054becb9262cc15c5e96e82f0d782f2aed3a3034d1f71a6385a6fa9e9595" +dependencies = [ + "home", + "serde", + "serde_derive", + "toml_edit 0.22.20", +] + +[[package]] +name = "cargo-generate" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b611d852b731eaaf84d3dfed2cefc1468f772b403ae0499fd3436a6a2b42b273" +dependencies = [ + "anstyle", + "anyhow", + "auth-git2", + "clap", + "console", + "dialoguer", + "env_logger", + "fs-err", + "git2", + "gix-config", + "heck 0.5.0", + "home", + "ignore", + "indexmap 2.5.0", + "indicatif", + "liquid", + "liquid-core", + "liquid-derive", + "liquid-lib", + "log", + "names", + "paste", + "path-absolutize", + "regex", + "remove_dir_all", + "rhai", + "sanitize-filename", + "semver", + "serde", + "tempfile", + "thiserror", + "time", + "toml", + "walkdir", +] + +[[package]] +name = "cargo-platform" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "cargo_toml" +version = "0.20.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad639525b1c67b6a298f378417b060fbc04618bea559482a8484381cce27d965" +dependencies = [ + "serde", + "toml", +] + +[[package]] +name = "cassowary" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cast5" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b07d673db1ccf000e90f54b819db9e75a8348d6eb056e9b8ab53231b7a9911" +dependencies = [ + "cipher", +] + +[[package]] +name = "castaway" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5" +dependencies = [ + "rustversion", +] + +[[package]] +name = "cc" +version = "1.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d74707dde2ba56f86ae90effb3b43ddd369504387e718014de010cec7959800" +dependencies = [ + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfb" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" +dependencies = [ + "byteorder", + "fnv", + "uuid", +] + +[[package]] +name = "cfb-mode" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "738b8d467867f80a71351933f70461f5b56f24d5c93e0cf216e59229c968d330" +dependencies = [ + "cipher", +] + +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cfg-expr" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "345c78335be0624ed29012dc10c49102196c6882c12dde65d9f35b02da2aada8" +dependencies = [ + "smallvec", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-targets 0.52.6", +] + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "4.5.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim 0.11.1", + "terminal_size", +] + +[[package]] +name = "clap_derive" +version = "4.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "clap_lex" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" + +[[package]] +name = "cmake" +version = "0.1.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb1e43aa7fd152b1f968787f7dbcdeb306d1867ff373c69955211876c053f91a" +dependencies = [ + "cc", +] + +[[package]] +name = "cocoa" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f79398230a6e2c08f5c9760610eb6924b52aa9e7950a619602baba59dcbbdbb2" +dependencies = [ + "bitflags 2.6.0", + "block", + "cocoa-foundation", + "core-foundation 0.10.0", + "core-graphics", + "foreign-types 0.5.0", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14045fb83be07b5acf1c0884b2180461635b433455fa35d1cd6f17f1450679d" +dependencies = [ + "bitflags 2.6.0", + "block", + "core-foundation 0.10.0", + "core-graphics-types", + "libc", + "objc", +] + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "colorchoice" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" + +[[package]] +name = "colored" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" +dependencies = [ + "lazy_static", + "windows-sys 0.48.0", +] + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "compact_str" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f" +dependencies = [ + "castaway", + "cfg-if", + "itoa 1.0.11", + "ryu", + "static_assertions", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "console" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "unicode-width", + "windows-sys 0.52.0", +] + +[[package]] +name = "console-api" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a257c22cd7e487dd4a13d413beabc512c5052f0bc048db0da6a84c3d8a6142fd" +dependencies = [ + "futures-core", + "prost", + "prost-types", + "tonic", + "tracing-core", +] + +[[package]] +name = "console-subscriber" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31c4cc54bae66f7d9188996404abdf7fdfa23034ef8e43478c8810828abad758" +dependencies = [ + "console-api", + "crossbeam-channel", + "crossbeam-utils", + "futures-task", + "hdrhistogram", + "humantime", + "prost", + "prost-types", + "serde", + "serde_json", + "thread_local", + "tokio", + "tokio-stream", + "tonic", + "tracing", + "tracing-core", + "tracing-subscriber", +] + +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom 0.2.15", + "once_cell", + "tiny-keccak", +] + +[[package]] +name = "const_format" +version = "0.2.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50c655d81ff1114fb0dcdea9225ea9f0cc712a6f8d189378e82bdf62a473a64b" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eff1a44b93f47b1bac19a27932f5c591e43d1ba357ee4f61526c8a25603f0eb1" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cookie" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +dependencies = [ + "aes-gcm", + "base64 0.22.1", + "percent-encoding", + "rand 0.8.5", + "subtle", + "time", + "version_check", +] + +[[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.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" +dependencies = [ + "bitflags 2.6.0", + "core-foundation 0.10.0", + "core-graphics-types", + "foreign-types 0.5.0", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" +dependencies = [ + "bitflags 2.6.0", + "core-foundation 0.10.0", + "libc", +] + +[[package]] +name = "cpio" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27e77cfc4543efb4837662cb7cd53464ae66f0fd5c708d71e0f338b1c11d62d3" + +[[package]] +name = "cpufeatures" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +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 = "crc24" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd121741cf3eb82c08dd3023eb55bf2665e5f60ec20f89760cf836ae4562e6a0" + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "futures", + "is-terminal", + "itertools 0.10.5", + "num-traits", + "once_cell", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "tokio", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools 0.10.5", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +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-queue" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crossterm" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" +dependencies = [ + "bitflags 2.6.0", + "crossterm_winapi", + "futures-core", + "libc", + "mio 0.8.11", + "parking_lot", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array 0.14.7", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array 0.14.7", + "rand_core 0.6.4", + "typenum", +] + +[[package]] +name = "cssparser" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa 0.4.8", + "matches", + "phf 0.8.0", + "proc-macro2", + "quote", + "smallvec", + "syn 1.0.109", +] + +[[package]] +name = "cssparser-macros" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" +dependencies = [ + "quote", + "syn 2.0.77", +] + +[[package]] +name = "ctor" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f" +dependencies = [ + "quote", + "syn 2.0.77", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "ctrlc" +version = "3.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90eeab0aa92f3f9b4e87f258c72b139c207d251f9cbc1080a0086b86a8870dd3" +dependencies = [ + "nix 0.29.0", + "windows-sys 0.59.0", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "cvt" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ae9bf77fbf2d39ef573205d554d87e86c12f1994e9ea335b0651b9b278bcf1" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "cxx" +version = "1.0.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54ccead7d199d584d139148b04b4a368d1ec7556a1d9ea2548febb1b9d49f9a4" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c77953e99f01508f89f55c494bfa867171ef3a6c8cea03d26975368f2121a5c1" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn 2.0.77", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65777e06cc48f0cb0152024c77d6cf9e4bdb4408e7b48bea993d42fa0f5b02b6" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98532a60dedaebc4848cb2cba5023337cc9ea3af16a5b062633fabfd9f18fb60" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "darling" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +dependencies = [ + "darling_core 0.14.4", + "darling_macro 0.14.4", +] + +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core 0.20.10", + "darling_macro 0.20.10", +] + +[[package]] +name = "darling_core" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.10.0", + "syn 1.0.109", +] + +[[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 0.11.1", + "syn 2.0.77", +] + +[[package]] +name = "darling_macro" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +dependencies = [ + "darling_core 0.14.4", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core 0.20.10", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "data-encoding" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" + +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[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_builder" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d67778784b508018359cbc8696edb3db78160bab2c2a28ba7f56ef6932997f8" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c11bdc11a0c47bc7d37d582b5285da6849c96681023680b906673c5707af7b0f" +dependencies = [ + "darling 0.14.4", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_builder_macro" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebcda35c7a396850a55ffeac740804b40ffec779b98fffbb1738f4033f0ee79e" +dependencies = [ + "derive_builder_core", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "0.99.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" +dependencies = [ + "convert_case 0.4.0", + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.77", +] + +[[package]] +name = "des" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdd80ce8ce993de27e9f063a444a4d53ce8e8db4c1f00cc03af5ad5a9867a1e" +dependencies = [ + "cipher", +] + +[[package]] +name = "dialoguer" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658bce805d770f407bc62102fca7c2c64ceef2fbcb2b8bd19d2765ce093980de" +dependencies = [ + "console", + "shell-words", + "tempfile", + "thiserror", + "zeroize", +] + +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "dioxus" +version = "0.6.0-alpha.2" +dependencies = [ + "axum 0.7.5", + "criterion", + "dioxus", + "dioxus-config-macro", + "dioxus-core", + "dioxus-core-macro", + "dioxus-core-types", + "dioxus-desktop", + "dioxus-devtools", + "dioxus-document", + "dioxus-fullstack", + "dioxus-hooks", + "dioxus-html", + "dioxus-isrg", + "dioxus-liveview", + "dioxus-mobile", + "dioxus-router", + "dioxus-signals", + "dioxus-ssr", + "dioxus-static-site-generation", + "dioxus-web", + "env_logger", + "futures-util", + "manganis", + "rand 0.8.5", + "serde", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "dioxus-autofmt" +version = "0.6.0-alpha.2" +dependencies = [ + "dioxus-rsx", + "pretty_assertions", + "prettyplease", + "proc-macro2", + "quote", + "serde", + "syn 2.0.77", +] + +[[package]] +name = "dioxus-check" +version = "0.6.0-alpha.2" +dependencies = [ + "indoc", + "owo-colors", + "pretty_assertions", + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "dioxus-cli" +version = "0.6.0-alpha.2" +dependencies = [ + "ansi-to-html", + "ansi-to-tui", + "anyhow", + "atty", + "axum 0.7.5", + "axum-extra", + "axum-server", + "brotli", + "built", + "cargo-config2", + "cargo-generate", + "cargo_metadata", + "cargo_toml", + "chrono", + "clap", + "console", + "console-subscriber", + "crossterm", + "ctrlc", + "dioxus-autofmt", + "dioxus-check", + "dioxus-core", + "dioxus-core-types", + "dioxus-devtools-types", + "dioxus-fullstack", + "dioxus-html", + "dioxus-rsx", + "dioxus-rsx-hotreload", + "dioxus-rsx-rosetta", + "dioxus-runtime-config", + "dirs", + "env_logger", + "flate2", + "fs_extra", + "futures-channel", + "futures-util", + "headers", + "html_parser", + "hyper 1.4.1", + "hyper-rustls", + "hyper-util", + "ignore", + "krates", + "manganis-core", + "notify", + "object", + "once_cell", + "open", + "prettyplease", + "proc-macro2", + "ratatui", + "rayon", + "regex", + "reqwest", + "rustls 0.23.13", + "serde", + "serde_json", + "subprocess", + "syn 2.0.77", + "tar", + "tauri-bundler", + "tempfile", + "thiserror", + "tokio", + "tokio-stream", + "tokio-util", + "toml", + "toml_edit 0.22.20", + "tower", + "tower-http", + "tracing", + "tracing-subscriber", + "uuid", + "walkdir", + "wasm-bindgen-cli-support", + "wasm-bindgen-shared", + "wasm-opt", + "zip", +] + +[[package]] +name = "dioxus-config-macro" +version = "0.6.0-alpha.2" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "dioxus-core" +version = "0.6.0-alpha.2" +dependencies = [ + "const_format", + "dioxus", + "dioxus-core-types", + "dioxus-html", + "dioxus-ssr", + "futures-channel", + "futures-util", + "generational-box", + "longest-increasing-subsequence", + "pretty_assertions", + "rand 0.8.5", + "reqwest", + "rustc-hash 1.1.0", + "rustversion", + "serde", + "slab", + "slotmap", + "tokio", + "tracing", + "tracing-fluent-assertions", + "tracing-subscriber", + "warnings", + "web-sys", +] + +[[package]] +name = "dioxus-core-macro" +version = "0.6.0-alpha.2" +dependencies = [ + "convert_case 0.6.0", + "dioxus", + "dioxus-html", + "dioxus-rsx", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.77", + "tokio", + "trybuild", +] + +[[package]] +name = "dioxus-core-types" +version = "0.6.0-alpha.2" + +[[package]] +name = "dioxus-desktop" +version = "0.6.0-alpha.2" +dependencies = [ + "async-trait", + "cocoa", + "core-foundation 0.10.0", + "dioxus", + "dioxus-core", + "dioxus-devtools", + "dioxus-document", + "dioxus-hooks", + "dioxus-html", + "dioxus-interpreter-js", + "dioxus-runtime-config", + "dioxus-signals", + "dioxus-ssr", + "dunce", + "exitcode", + "futures-channel", + "futures-util", + "generational-box", + "global-hotkey", + "http-range", + "infer", + "muda", + "objc", + "objc_id", + "rand 0.8.5", + "reqwest", + "rfd", + "rustc-hash 1.1.0", + "separator", + "serde", + "serde_json", + "signal-hook", + "slab", + "tao", + "thiserror", + "tokio", + "tracing", + "urlencoding", + "webbrowser", + "wry", +] + +[[package]] +name = "dioxus-devtools" +version = "0.6.0-alpha.2" +dependencies = [ + "dioxus-core", + "dioxus-devtools-types", + "dioxus-signals", + "serde", + "serde_json", + "tokio", + "tracing", + "tungstenite 0.23.0", + "warnings", +] + +[[package]] +name = "dioxus-devtools-types" +version = "0.6.0-alpha.2" +dependencies = [ + "dioxus-core", + "serde", +] + +[[package]] +name = "dioxus-document" +version = "0.6.0-alpha.2" +dependencies = [ + "dioxus-core", + "dioxus-core-macro", + "dioxus-core-types", + "futures-channel", + "serde", + "serde_json", + "tracing", +] + +[[package]] +name = "dioxus-examples" +version = "0.6.0-alpha.2" +dependencies = [ + "async-std", + "base64 0.22.1", + "ciborium", + "dioxus", + "dioxus-ssr", + "form_urlencoded", + "futures-util", + "getrandom 0.2.15", + "http-range", + "rand 0.8.5", + "reqwest", + "separator", + "serde", + "serde_json", + "tokio", + "web-time", +] + +[[package]] +name = "dioxus-ext" +version = "0.6.0-alpha.2" +dependencies = [ + "dioxus-autofmt", + "dioxus-rsx-rosetta", + "html_parser", + "syn 2.0.77", + "wasm-bindgen", +] + +[[package]] +name = "dioxus-fullstack" +version = "0.6.0-alpha.2" +dependencies = [ + "async-trait", + "aws-lc-rs", + "axum 0.7.5", + "base64 0.22.1", + "bytes", + "ciborium", + "dioxus", + "dioxus-core-types", + "dioxus-desktop", + "dioxus-devtools", + "dioxus-document", + "dioxus-interpreter-js", + "dioxus-isrg", + "dioxus-lib", + "dioxus-mobile", + "dioxus-runtime-config", + "dioxus-ssr", + "dioxus-web", + "dioxus_server_macro", + "futures-channel", + "futures-util", + "generational-box", + "http 1.1.0", + "hyper 1.4.1", + "hyper-rustls", + "once_cell", + "parking_lot", + "pin-project", + "rustls 0.23.13", + "serde", + "server_fn", + "thiserror", + "tokio", + "tokio-stream", + "tokio-util", + "tower", + "tower-http", + "tower-layer", + "tracing", + "tracing-futures", + "web-sys", +] + +[[package]] +name = "dioxus-hooks" +version = "0.6.0-alpha.2" +dependencies = [ + "dioxus", + "dioxus-core", + "dioxus-signals", + "futures-channel", + "futures-util", + "generational-box", + "reqwest", + "rustversion", + "slab", + "tokio", + "tracing", + "warnings", + "web-sys", +] + +[[package]] +name = "dioxus-html" +version = "0.6.0-alpha.2" +dependencies = [ + "async-trait", + "dioxus", + "dioxus-core", + "dioxus-core-types", + "dioxus-html-internal-macro", + "dioxus-rsx", + "dioxus-rsx-hotreload", + "dioxus-web", + "enumset", + "euclid", + "futures-channel", + "keyboard-types", + "lazy-js-bundle", + "manganis", + "serde", + "serde_json", + "serde_repr", + "tokio", + "tracing", +] + +[[package]] +name = "dioxus-html-internal-macro" +version = "0.6.0-alpha.2" +dependencies = [ + "convert_case 0.6.0", + "proc-macro2", + "quote", + "syn 2.0.77", + "trybuild", +] + +[[package]] +name = "dioxus-interpreter-js" +version = "0.6.0-alpha.2" +dependencies = [ + "dioxus-core", + "dioxus-core-types", + "js-sys", + "lazy-js-bundle", + "rustc-hash 1.1.0", + "serde", + "sledgehammer_bindgen", + "sledgehammer_utils", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "dioxus-isrg" +version = "0.6.0-alpha.2" +dependencies = [ + "chrono", + "http 1.1.0", + "lru", + "rustc-hash 1.1.0", + "thiserror", + "tracing", +] + +[[package]] +name = "dioxus-lib" +version = "0.6.0-alpha.2" +dependencies = [ + "dioxus", + "dioxus-config-macro", + "dioxus-core", + "dioxus-core-macro", + "dioxus-hooks", + "dioxus-html", + "dioxus-rsx", + "dioxus-signals", + "manganis", +] + +[[package]] +name = "dioxus-liveview" +version = "0.6.0-alpha.2" +dependencies = [ + "axum 0.7.5", + "dioxus", + "dioxus-core", + "dioxus-core-types", + "dioxus-devtools", + "dioxus-document", + "dioxus-html", + "dioxus-interpreter-js", + "dioxus-runtime-config", + "env_logger", + "futures-channel", + "futures-util", + "generational-box", + "lazy-js-bundle", + "rustc-hash 1.1.0", + "serde", + "serde_json", + "slab", + "thiserror", + "tokio", + "tokio-stream", + "tokio-util", + "tower", + "tracing", +] + +[[package]] +name = "dioxus-mobile" +version = "0.6.0-alpha.2" +dependencies = [ + "dioxus-desktop", +] + +[[package]] +name = "dioxus-playwright-fullstack-test" +version = "0.1.0" +dependencies = [ + "dioxus", + "serde", + "tokio", +] + +[[package]] +name = "dioxus-playwright-liveview-test" +version = "0.0.1" +dependencies = [ + "axum 0.7.5", + "dioxus", + "dioxus-liveview", + "tokio", +] + +[[package]] +name = "dioxus-playwright-static-generation-test" +version = "0.1.0" +dependencies = [ + "dioxus", + "serde", + "tokio", +] + +[[package]] +name = "dioxus-playwright-web-test" +version = "0.0.1" +dependencies = [ + "dioxus", + "serde_json", + "tracing", + "tracing-wasm", +] + +[[package]] +name = "dioxus-router" +version = "0.6.0-alpha.2" +dependencies = [ + "axum 0.7.5", + "base64 0.21.7", + "ciborium", + "console_error_panic_hook", + "criterion", + "dioxus", + "dioxus-document", + "dioxus-fullstack", + "dioxus-lib", + "dioxus-liveview", + "dioxus-router-macro", + "dioxus-ssr", + "gloo", + "gloo-utils 0.1.7", + "http 1.1.0", + "js-sys", + "rustversion", + "serde", + "serde_json", + "tokio", + "tracing", + "url", + "urlencoding", + "wasm-bindgen", + "wasm-bindgen-test", + "web-sys", +] + +[[package]] +name = "dioxus-router-macro" +version = "0.6.0-alpha.2" +dependencies = [ + "dioxus", + "proc-macro2", + "quote", + "slab", + "syn 2.0.77", +] + +[[package]] +name = "dioxus-rsx" +version = "0.6.0-alpha.2" +dependencies = [ + "prettier-please", + "prettyplease", + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn 2.0.77", + "tracing", +] + +[[package]] +name = "dioxus-rsx-hotreload" +version = "0.6.0-alpha.2" +dependencies = [ + "dioxus-core", + "dioxus-core-types", + "dioxus-rsx", + "internment", + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn 2.0.77", + "tracing", +] + +[[package]] +name = "dioxus-rsx-rosetta" +version = "0.6.0-alpha.2" +dependencies = [ + "convert_case 0.6.0", + "dioxus-autofmt", + "dioxus-html", + "dioxus-rsx", + "html_parser", + "pretty_assertions", + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "dioxus-runtime-config" +version = "0.6.0-alpha.2" + +[[package]] +name = "dioxus-server" +version = "0.6.0-alpha.2" + +[[package]] +name = "dioxus-signals" +version = "0.6.0-alpha.2" +dependencies = [ + "dioxus", + "dioxus-core", + "futures-channel", + "futures-util", + "generational-box", + "once_cell", + "parking_lot", + "rand 0.8.5", + "reqwest", + "rustc-hash 1.1.0", + "serde", + "simple_logger", + "tokio", + "tracing", + "tracing-subscriber", + "warnings", +] + +[[package]] +name = "dioxus-ssr" +version = "0.6.0-alpha.2" +dependencies = [ + "askama_escape", + "dioxus", + "dioxus-core", + "dioxus-core-types", + "dioxus-html", + "rustc-hash 1.1.0", +] + +[[package]] +name = "dioxus-static-site-generation" +version = "0.6.0-alpha.2" +dependencies = [ + "axum 0.7.5", + "criterion", + "dioxus", + "dioxus-devtools", + "dioxus-fullstack", + "dioxus-isrg", + "dioxus-lib", + "dioxus-router", + "dioxus-ssr", + "dioxus-web", + "http 1.1.0", + "tokio", + "tower", + "tower-http", + "tracing", +] + +[[package]] +name = "dioxus-web" +version = "0.6.0-alpha.2" +dependencies = [ + "async-trait", + "ciborium", + "console_error_panic_hook", + "dioxus", + "dioxus-core", + "dioxus-core-types", + "dioxus-devtools", + "dioxus-document", + "dioxus-html", + "dioxus-interpreter-js", + "dioxus-signals", + "dioxus-ssr", + "dioxus-web", + "futures-channel", + "futures-util", + "generational-box", + "gloo-timers 0.3.0", + "js-sys", + "lazy-js-bundle", + "rustc-hash 1.1.0", + "serde", + "serde-wasm-bindgen 0.6.5", + "serde_json", + "tracing", + "tracing-wasm", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-bindgen-test", + "web-sys", +] + +[[package]] +name = "dioxus_server_macro" +version = "0.6.0-alpha.2" +dependencies = [ + "proc-macro2", + "quote", + "server_fn_macro", + "syn 2.0.77", +] + +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + +[[package]] +name = "dissimilar" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59f8e79d1fbf76bdfbde321e902714bf6c49df88a7dda6fc682fc2979226962d" + +[[package]] +name = "dlopen2" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1297103d2bbaea85724fcee6294c2d50b1081f9ad47d0f6f6f61eda65315a6" +dependencies = [ + "dlopen2_derive", + "libc", + "once_cell", + "winapi", +] + +[[package]] +name = "dlopen2_derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + +[[package]] +name = "dpi" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" + +[[package]] +name = "dsa" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48bc224a9084ad760195584ce5abb3c2c34a225fa312a128ad245a6b412b7689" +dependencies = [ + "digest", + "num-bigint-dig", + "num-traits", + "pkcs8", + "rfc6979", + "sha2", + "signature", + "zeroize", +] + +[[package]] +name = "dtoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" + +[[package]] +name = "dtoa-short" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87" +dependencies = [ + "dtoa", +] + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +dependencies = [ + "curve25519-dalek", + "ed25519", + "serde", + "sha2", + "subtle", + "zeroize", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +dependencies = [ + "serde", +] + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array 0.14.7", + "group", + "hkdf", + "pem-rfc7468", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + +[[package]] +name = "encoding_rs" +version = "0.8.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "endi" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" + +[[package]] +name = "enum-display-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f16ef37b2a9b242295d61a154ee91ae884afff6b8b933b486b12481cc58310ca" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "enum-primitive-derive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba7795da175654fe16979af73f81f26a8ea27638d8d9823d317016888a63dc4c" +dependencies = [ + "num-traits", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "enumflags2" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d232db7f5956f3f14313dc2f87985c58bd2c695ce124c8cdd984e08e15ac133d" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[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 0.20.10", + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "env_filter" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "humantime", + "log", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "erased-serde" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24e2389d65ab4fab27dc2a5de7b191e1f6617d1f1c8855c0dc569c94a4cbb18d" +dependencies = [ + "serde", + "typeid", +] + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "etcetera" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" +dependencies = [ + "cfg-if", + "home", + "windows-sys 0.48.0", +] + +[[package]] +name = "euclid" +version = "0.22.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad9cdb4b747e485a12abb0e6566612956c7a1bafa3bdb8d682c5b6d403589e48" +dependencies = [ + "num-traits", + "serde", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "event-listener" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +dependencies = [ + "event-listener 5.3.1", + "pin-project-lite", +] + +[[package]] +name = "execute" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a82608ee96ce76aeab659e9b8d3c2b787bffd223199af88c674923d861ada10" +dependencies = [ + "execute-command-macro", + "execute-command-tokens", + "generic-array 1.1.0", +] + +[[package]] +name = "execute-command-macro" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90dec53d547564e911dc4ff3ecb726a64cf41a6fa01a2370ebc0d95175dd08bd" +dependencies = [ + "execute-command-macro-impl", +] + +[[package]] +name = "execute-command-macro-impl" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce8cd46a041ad005ab9c71263f9a0ff5b529eac0fe4cc9b4a20f4f0765d8cf4b" +dependencies = [ + "execute-command-tokens", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "execute-command-tokens" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69dc321eb6be977f44674620ca3aa21703cb20ffbe560e1ae97da08401ffbcad" + +[[package]] +name = "exitcode" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de853764b47027c2e862a995c34978ffa63c1501f2e15f987ba11bd4f9bba193" + +[[package]] +name = "exr" +version = "1.72.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4" +dependencies = [ + "bit_field", + "flume", + "half", + "lebe", + "miniz_oxide 0.7.4", + "rayon-core", + "smallvec", + "zune-inflate", +] + +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "faster-hex" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2a2b11eda1d40935b26cf18f6833c526845ae8c41e58d09af6adeb6f0269183" + +[[package]] +name = "fastrand" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" + +[[package]] +name = "fdeflate" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "field-offset" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" +dependencies = [ + "memoffset", + "rustc_version", +] + +[[package]] +name = "filetime" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" +dependencies = [ + "cfg-if", + "libc", + "libredox", + "windows-sys 0.59.0", +] + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "flate2" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" +dependencies = [ + "crc32fast", + "miniz_oxide 0.8.0", +] + +[[package]] +name = "fluent-uri" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17c704e9dbe1ddd863da1e6ff3567795087b1eb201ce80d8fa81162e1516500d" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +dependencies = [ + "futures-core", + "futures-sink", + "spin", +] + +[[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.77", +] + +[[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 = "forwarded-header-value" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835f84f38484cc86f110a805655697908257fb9a7af005234060891557198e9" +dependencies = [ + "nonempty", + "thiserror", +] + +[[package]] +name = "fs-err" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a41f105fe1d5b6b34b2055e3dc59bb79b46b48b2040b9e6c7b4b5de097aa41" +dependencies = [ + "autocfg", +] + +[[package]] +name = "fs_at" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14af6c9694ea25db25baa2a1788703b9e7c6648dcaeeebeb98f7561b5384c036" +dependencies = [ + "aligned", + "cfg-if", + "cvt", + "libc", + "nix 0.29.0", + "windows-sys 0.52.0", +] + +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + +[[package]] +name = "fsevent-sys" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" +dependencies = [ + "libc", +] + +[[package]] +name = "fullstack-auth-example" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "axum 0.7.5", + "axum_session", + "axum_session_auth", + "dioxus", + "dioxus-fullstack", + "dioxus-web", + "execute", + "http 1.1.0", + "serde", + "simple_logger", + "sqlx", + "tokio", + "tower", + "tower-http", +] + +[[package]] +name = "fullstack-desktop-example" +version = "0.1.0" +dependencies = [ + "dioxus", + "serde", +] + +[[package]] +name = "fullstack-hackernews-example" +version = "0.1.0" +dependencies = [ + "chrono", + "dioxus", + "reqwest", + "serde", + "tracing", + "tracing-subscriber", + "tracing-wasm", +] + +[[package]] +name = "fullstack-hello-world-example" +version = "0.1.0" +dependencies = [ + "dioxus", + "reqwest", + "serde", + "simple_logger", + "tracing", + "tracing-subscriber", + "tracing-wasm", +] + +[[package]] +name = "fullstack-router-example" +version = "0.1.0" +dependencies = [ + "axum 0.7.5", + "dioxus", + "serde", + "tokio", +] + +[[package]] +name = "fullstack-streaming-example" +version = "0.1.0" +dependencies = [ + "dioxus", + "futures", + "futures-util", + "once_cell", + "serde", + "simple_logger", + "tokio", + "tracing", + "tracing-subscriber", + "tracing-wasm", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" +dependencies = [ + "mac", + "new_debug_unreachable", +] + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-intrusive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" +dependencies = [ + "futures-core", + "lock_api", + "parking_lot", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-lite" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "gdk" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5ba081bdef3b75ebcdbfc953699ed2d7417d6bd853347a42a37d76406a33646" +dependencies = [ + "cairo-rs", + "gdk-pixbuf", + "gdk-sys", + "gio", + "glib", + "libc", + "pango", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" +dependencies = [ + "gdk-pixbuf-sys", + "gio", + "glib", + "libc", + "once_cell", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gdk-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31ff856cb3386dae1703a920f803abafcc580e9b5f711ca62ed1620c25b51ff2" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkwayland-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a90fbf5c033c65d93792192a49a8efb5bb1e640c419682a58bb96f5ae77f3d4a" +dependencies = [ + "gdk-sys", + "glib-sys", + "gobject-sys", + "libc", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkx11" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2ea8a4909d530f79921290389cbd7c34cb9d623bfe970eaae65ca5f9cd9cce" +dependencies = [ + "gdk", + "gdkx11-sys", + "gio", + "glib", + "libc", + "x11", +] + +[[package]] +name = "gdkx11-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fee8f00f4ee46cad2939b8990f5c70c94ff882c3028f3cc5abf950fa4ab53043" +dependencies = [ + "gdk-sys", + "glib-sys", + "libc", + "system-deps", + "x11", +] + +[[package]] +name = "generational-box" +version = "0.6.0-alpha.2" +dependencies = [ + "criterion", + "parking_lot", + "rand 0.8.5", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "generic-array" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96512db27971c2c3eece70a1e106fbe6c87760234e31e8f7e5634912fe52794a" +dependencies = [ + "typenum", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", +] + +[[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.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" +dependencies = [ + "fallible-iterator", + "indexmap 1.9.3", + "stable_deref_trait", +] + +[[package]] +name = "gimli" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" + +[[package]] +name = "gio" +version = "0.18.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys", + "glib", + "libc", + "once_cell", + "pin-project-lite", + "smallvec", + "thiserror", +] + +[[package]] +name = "gio-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", + "winapi", +] + +[[package]] +name = "git2" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b903b73e45dc0c6c596f2d37eccece7c1c8bb6e4407b001096387c63d0d93724" +dependencies = [ + "bitflags 2.6.0", + "libc", + "libgit2-sys", + "log", + "openssl-probe", + "openssl-sys", + "url", +] + +[[package]] +name = "github-pages-static-generation" +version = "0.1.0" +dependencies = [ + "dioxus", + "tower", + "tracing-subscriber", +] + +[[package]] +name = "gix-actor" +version = "0.31.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0e454357e34b833cc3a00b6efbbd3dd4d18b24b9fb0c023876ec2645e8aa3f2" +dependencies = [ + "bstr", + "gix-date", + "gix-utils", + "itoa 1.0.11", + "thiserror", + "winnow 0.6.18", +] + +[[package]] +name = "gix-config" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fafe42957e11d98e354a66b6bd70aeea00faf2f62dd11164188224a507c840" +dependencies = [ + "bstr", + "gix-config-value", + "gix-features", + "gix-glob", + "gix-path", + "gix-ref", + "gix-sec", + "memchr", + "once_cell", + "smallvec", + "thiserror", + "unicode-bom", + "winnow 0.6.18", +] + +[[package]] +name = "gix-config-value" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03f76169faa0dec598eac60f83d7fcdd739ec16596eca8fb144c88973dbe6f8c" +dependencies = [ + "bitflags 2.6.0", + "bstr", + "gix-path", + "libc", + "thiserror", +] + +[[package]] +name = "gix-date" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eed6931f21491ee0aeb922751bd7ec97b4b2fe8fbfedcb678e2a2dce5f3b8c0" +dependencies = [ + "bstr", + "itoa 1.0.11", + "thiserror", + "time", +] + +[[package]] +name = "gix-features" +version = "0.38.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac7045ac9fe5f9c727f38799d002a7ed3583cd777e3322a7c4b43e3cf437dc69" +dependencies = [ + "gix-hash", + "gix-trace", + "gix-utils", + "libc", + "prodash", + "sha1_smol", + "walkdir", +] + +[[package]] +name = "gix-fs" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2bfe6249cfea6d0c0e0990d5226a4cb36f030444ba9e35e0639275db8f98575" +dependencies = [ + "fastrand", + "gix-features", + "gix-utils", +] + +[[package]] +name = "gix-glob" +version = "0.16.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74908b4bbc0a0a40852737e5d7889f676f081e340d5451a16e5b4c50d592f111" +dependencies = [ + "bitflags 2.6.0", + "bstr", + "gix-features", + "gix-path", +] + +[[package]] +name = "gix-hash" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93d7df7366121b5018f947a04d37f034717e113dcf9ccd85c34b58e57a74d5e" +dependencies = [ + "faster-hex", + "thiserror", +] + +[[package]] +name = "gix-lock" +version = "14.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bc7fe297f1f4614774989c00ec8b1add59571dc9b024b4c00acb7dedd4e19d" +dependencies = [ + "gix-tempfile", + "gix-utils", + "thiserror", +] + +[[package]] +name = "gix-object" +version = "0.42.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25da2f46b4e7c2fa7b413ce4dffb87f69eaf89c2057e386491f4c55cadbfe386" +dependencies = [ + "bstr", + "gix-actor", + "gix-date", + "gix-features", + "gix-hash", + "gix-utils", + "gix-validate", + "itoa 1.0.11", + "smallvec", + "thiserror", + "winnow 0.6.18", +] + +[[package]] +name = "gix-path" +version = "0.10.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebfc4febd088abdcbc9f1246896e57e37b7a34f6909840045a1767c6dafac7af" +dependencies = [ + "bstr", + "gix-trace", + "home", + "once_cell", + "thiserror", +] + +[[package]] +name = "gix-ref" +version = "0.44.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3394a2997e5bc6b22ebc1e1a87b41eeefbcfcff3dbfa7c4bd73cb0ac8f1f3e2e" +dependencies = [ + "gix-actor", + "gix-date", + "gix-features", + "gix-fs", + "gix-hash", + "gix-lock", + "gix-object", + "gix-path", + "gix-tempfile", + "gix-utils", + "gix-validate", + "memmap2", + "thiserror", + "winnow 0.6.18", +] + +[[package]] +name = "gix-sec" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fe4d52f30a737bbece5276fab5d3a8b276dc2650df963e293d0673be34e7a5f" +dependencies = [ + "bitflags 2.6.0", + "gix-path", + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "gix-tempfile" +version = "14.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046b4927969fa816a150a0cda2e62c80016fe11fb3c3184e4dddf4e542f108aa" +dependencies = [ + "gix-fs", + "libc", + "once_cell", + "parking_lot", + "tempfile", +] + +[[package]] +name = "gix-trace" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cae0e8661c3ff92688ce1c8b8058b3efb312aba9492bbe93661a21705ab431b" + +[[package]] +name = "gix-utils" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35192df7fd0fa112263bad8021e2df7167df4cc2a6e6d15892e1e55621d3d4dc" +dependencies = [ + "fastrand", + "unicode-normalization", +] + +[[package]] +name = "gix-validate" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82c27dd34a49b1addf193c92070bcbf3beaf6e10f16a78544de6372e146a0acf" +dependencies = [ + "bstr", + "thiserror", +] + +[[package]] +name = "glib" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" +dependencies = [ + "bitflags 2.6.0", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "memchr", + "once_cell", + "smallvec", + "thiserror", +] + +[[package]] +name = "glib-macros" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" +dependencies = [ + "heck 0.4.1", + "proc-macro-crate 2.0.0", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "glib-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" +dependencies = [ + "libc", + "system-deps", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "global-hotkey" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1b75248f33c73df1ed69673f6cb36d2e048ae84d29aa1d3e53199d138ebb1df" +dependencies = [ + "crossbeam-channel", + "keyboard-types", + "objc2", + "objc2-app-kit", + "once_cell", + "thiserror", + "windows-sys 0.59.0", + "x11-dl", +] + +[[package]] +name = "globset" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15f1ce686646e7f1e19bf7d5533fe443a45dbfb990e00629110797578b42fb19" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", +] + +[[package]] +name = "gloo" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28999cda5ef6916ffd33fb4a7b87e1de633c47c0dc6d97905fee1cdaa142b94d" +dependencies = [ + "gloo-console", + "gloo-dialogs", + "gloo-events", + "gloo-file", + "gloo-history", + "gloo-net 0.3.1", + "gloo-render", + "gloo-storage", + "gloo-timers 0.2.6", + "gloo-utils 0.1.7", + "gloo-worker", +] + +[[package]] +name = "gloo-console" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b7ce3c05debe147233596904981848862b068862e9ec3e34be446077190d3f" +dependencies = [ + "gloo-utils 0.1.7", + "js-sys", + "serde", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-dialogs" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67062364ac72d27f08445a46cab428188e2e224ec9e37efdba48ae8c289002e6" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-events" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b107f8abed8105e4182de63845afcc7b69c098b7852a813ea7462a320992fc" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-file" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d5564e570a38b43d78bdc063374a0c3098c4f0d64005b12f9bbe87e869b6d7" +dependencies = [ + "gloo-events", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-history" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85725d90bf0ed47063b3930ef28e863658a7905989e9929a8708aab74a1d5e7f" +dependencies = [ + "gloo-events", + "gloo-utils 0.1.7", + "serde", + "serde-wasm-bindgen 0.5.0", + "serde_urlencoded", + "thiserror", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-net" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a66b4e3c7d9ed8d315fd6b97c8b1f74a7c6ecbbc2320e65ae7ed38b7068cc620" +dependencies = [ + "futures-channel", + "futures-core", + "futures-sink", + "gloo-utils 0.1.7", + "http 0.2.12", + "js-sys", + "pin-project", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-net" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06f627b1a58ca3d42b45d6104bf1e1a03799df472df00988b6ba21accc10580" +dependencies = [ + "futures-channel", + "futures-core", + "futures-sink", + "gloo-utils 0.2.0", + "http 1.1.0", + "js-sys", + "pin-project", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-render" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd9306aef67cfd4449823aadcd14e3958e0800aa2183955a309112a84ec7764" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-storage" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6ab60bf5dbfd6f0ed1f7843da31b41010515c745735c970e821945ca91e480" +dependencies = [ + "gloo-utils 0.1.7", + "js-sys", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-timers" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "gloo-timers" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "gloo-utils" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037fcb07216cb3a30f7292bd0176b050b7b9a052ba830ef7d5d65f6dc64ba58e" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-utils" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-worker" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13471584da78061a28306d1359dd0178d8d6fc1c7c80e5e35d27260346e0516a" +dependencies = [ + "anymap2", + "bincode", + "gloo-console", + "gloo-utils 0.1.7", + "js-sys", + "serde", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gobject-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "gtk" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93c4f5e0e20b60e10631a5f06da7fe3dda744b05ad0ea71fee2f47adf865890c" +dependencies = [ + "atk", + "cairo-rs", + "field-offset", + "futures-channel", + "gdk", + "gdk-pixbuf", + "gio", + "glib", + "gtk-sys", + "gtk3-macros", + "libc", + "pango", + "pkg-config", +] + +[[package]] +name = "gtk-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771437bf1de2c1c0b496c11505bdf748e26066bbe942dfc8f614c9460f6d7722" +dependencies = [ + "atk-sys", + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "system-deps", +] + +[[package]] +name = "gtk3-macros" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6063efb63db582968fb7df72e1ae68aa6360dcfb0a75143f34fc7d616bad75e" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap 2.5.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "h2" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.1.0", + "indexmap 2.5.0", + "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", + "crunchy", +] + +[[package]] +name = "handlebars" +version = "5.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d08485b96a0e6393e9e4d1b8d48cf74ad6c063cd905eb33f42c1ce3f0377539b" +dependencies = [ + "log", + "pest", + "pest_derive", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.8", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash 0.8.11", + "allocator-api2", + "serde", +] + +[[package]] +name = "hashlink" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" +dependencies = [ + "hashbrown 0.14.5", +] + +[[package]] +name = "hdrhistogram" +version = "7.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "765c9198f173dd59ce26ff9f95ef0aafd0a0fe01fb9d72841bc5066a4c06511d" +dependencies = [ + "base64 0.21.7", + "byteorder", + "flate2", + "nom", + "num-traits", +] + +[[package]] +name = "headers" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322106e6bd0cba2d5ead589ddb8150a13d7c4217cf80d7c4f682ca994ccc6aa9" +dependencies = [ + "base64 0.21.7", + "bytes", + "headers-core", + "http 1.1.0", + "httpdate", + "mime", + "sha1", +] + +[[package]] +name = "headers-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" +dependencies = [ + "http 1.1.0", +] + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +dependencies = [ + "unicode-segmentation", +] + +[[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.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hermit-abi" +version = "0.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 = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "html5ever" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" +dependencies = [ + "log", + "mac", + "markup5ever", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "html_parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f56db07b6612644f6f7719f8ef944f75fff9d6378fdf3d316fd32194184abd" +dependencies = [ + "doc-comment", + "pest", + "pest_derive", + "serde", + "serde_derive", + "serde_json", + "thiserror", +] + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa 1.0.11", +] + +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa 1.0.11", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http 1.1.0", +] + +[[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 1.1.0", + "http-body 1.0.1", + "pin-project-lite", +] + +[[package]] +name = "http-range" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573" + +[[package]] +name = "http-range-header" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08a397c49fec283e3d6211adbe480be95aae5f304cfb923e9970e08956d5168a" + +[[package]] +name = "httparse" +version = "1.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa 1.0.11", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2 0.4.6", + "http 1.1.0", + "http-body 1.0.1", + "httparse", + "httpdate", + "itoa 1.0.11", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" +dependencies = [ + "futures-util", + "http 1.1.0", + "hyper 1.4.1", + "hyper-util", + "log", + "rustls 0.23.13", + "rustls-native-certs", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", + "webpki-roots 0.26.5", +] + +[[package]] +name = "hyper-timeout" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" +dependencies = [ + "hyper 0.14.30", + "pin-project-lite", + "tokio", + "tokio-io-timeout", +] + +[[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 1.4.1", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da62f120a8a37763efb0cf8fdf264b884c7b8b9ac8660b900c8661030c00e6ba" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "hyper 1.4.1", + "pin-project-lite", + "socket2", + "tokio", + "tower", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +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 = "id-arena" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" + +[[package]] +name = "idea" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "075557004419d7f2031b8bb7f44bb43e55a83ca7b63076a8fb8fe75753836477" +dependencies = [ + "cipher", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "ignore" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata 0.4.7", + "same-file", + "walkdir", + "winapi-util", +] + +[[package]] +name = "image" +version = "0.24.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "exr", + "gif", + "jpeg-decoder", + "num-traits", + "png", + "qoi", + "tiff", +] + +[[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.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +dependencies = [ + "equivalent", + "hashbrown 0.14.5", + "serde", +] + +[[package]] +name = "indicatif" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3" +dependencies = [ + "console", + "instant", + "number_prefix", + "portable-atomic", + "unicode-width", +] + +[[package]] +name = "indoc" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" + +[[package]] +name = "infer" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc150e5ce2330295b8616ce0e3f53250e53af31759a9dbedad1621ba29151847" +dependencies = [ + "cfb", +] + +[[package]] +name = "inotify" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" +dependencies = [ + "bitflags 1.3.2", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "internment" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04e8e537b529b8674e97e9fb82c10ff168a290ac3867a0295f112061ffbca1ef" +dependencies = [ + "hashbrown 0.14.5", + "parking_lot", +] + +[[package]] +name = "inventory" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f958d3d68f4167080a18141e10381e7634563984a537f2a49a30fd8e53ac5767" + +[[package]] +name = "ipnet" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4" + +[[package]] +name = "ipnetwork" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf466541e9d546596ee94f9f69590f89473455f88372423e0008fc1a7daf100e" +dependencies = [ + "serde", +] + +[[package]] +name = "iri-string" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e0f755bd3806e06ad4f366f92639415d99a339a2c7ecf8c26ccea2097c11cb6" +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-terminal" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" +dependencies = [ + "hermit-abi 0.4.0", + "libc", + "windows-sys 0.52.0", +] + +[[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_ci" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "iter-read" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071ed4cc1afd86650602c7b11aa2e1ce30762a1c27193201cb5cee9c6ebb1294" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +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 = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "javascriptcore-rs" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca5671e9ffce8ffba57afc24070e906da7fc4b1ba66f2cabebf61bf2ea257fcc" +dependencies = [ + "bitflags 1.3.2", + "glib", + "javascriptcore-rs-sys", +] + +[[package]] +name = "javascriptcore-rs-sys" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1be78d14ffa4b75b66df31840478fef72b51f8c2465d4ca7c194da9f7a5124" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror", + "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" +dependencies = [ + "rayon", +] + +[[package]] +name = "js-sys" +version = "0.3.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "json-patch" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b1fb8864823fad91877e6caea0baca82e49e8db50f8e5c9f9a453e27d3330fc" +dependencies = [ + "jsonptr", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "jsonptr" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c6e529149475ca0b2820835d3dce8fcc41c6b943ca608d32f35b449255e4627" +dependencies = [ + "fluent-uri", + "serde", + "serde_json", +] + +[[package]] +name = "k256" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2", + "signature", +] + +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "keyboard-types" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" +dependencies = [ + "bitflags 2.6.0", + "serde", + "unicode-segmentation", +] + +[[package]] +name = "kqueue" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +dependencies = [ + "bitflags 1.3.2", + "libc", +] + +[[package]] +name = "krates" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "866bd5c1d0fc9c130a2469a1b3086803ea67b93b35683ae8ab2ec8a970120a65" +dependencies = [ + "camino", + "cfg-expr 0.16.0", + "petgraph", + "semver", + "serde", + "serde_json", +] + +[[package]] +name = "kstring" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "558bf9508a558512042d3095138b1f7b8fe90c5467d94f9f1da28b3731c5dbd1" +dependencies = [ + "serde", + "static_assertions", +] + +[[package]] +name = "kuchikiki" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e4755b7b995046f510a7520c42b2fed58b77bd94d5a87a8eb43d2fd126da8" +dependencies = [ + "cssparser", + "html5ever", + "indexmap 1.9.3", + "matches", + "selectors", +] + +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + +[[package]] +name = "lazy-js-bundle" +version = "0.6.0-alpha.2" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "leb128" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" + +[[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.158" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" + +[[package]] +name = "libgit2-sys" +version = "0.17.0+1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10472326a8a6477c3c20a64547b0059e4b0d086869eee31e6d7da728a8eb7224" +dependencies = [ + "cc", + "libc", + "libssh2-sys", + "libz-sys", + "openssl-sys", + "pkg-config", +] + +[[package]] +name = "libloading" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +dependencies = [ + "cfg-if", + "windows-targets 0.52.6", +] + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.6.0", + "libc", + "redox_syscall", +] + +[[package]] +name = "libsqlite3-sys" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4e226dcd58b4be396f7bd3c20da8fdee2911400705297ba7d2d7cc2c30f716" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "libssh2-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dc8a030b787e2119a731f1951d6a773e2280c660f8ec4b0f5e1505a386e71ee" +dependencies = [ + "cc", + "libc", + "libz-sys", + "openssl-sys", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "libxdo" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00333b8756a3d28e78def82067a377de7fa61b24909000aeaa2b446a948d14db" +dependencies = [ + "libxdo-sys", +] + +[[package]] +name = "libxdo-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db23b9e7e2b7831bbd8aac0bbeeeb7b68cbebc162b227e7052e8e55829a09212" +dependencies = [ + "libc", + "x11", +] + +[[package]] +name = "libz-sys" +version = "1.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "link-cplusplus" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d240c6f7e1ba3a28b0249f774e6a9dd0175054b52dfbb61b16eb8505c3785c9" +dependencies = [ + "cc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "liquid" +version = "0.26.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cdcc72b82748f47c2933c172313f5a9aea5b2c4eb3fa4c66b4ea55bb60bb4b1" +dependencies = [ + "doc-comment", + "liquid-core", + "liquid-derive", + "liquid-lib", + "serde", +] + +[[package]] +name = "liquid-core" +version = "0.26.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2752e978ffc53670f3f2e8b3ef09f348d6f7b5474a3be3f8a5befe5382e4effb" +dependencies = [ + "anymap2", + "itertools 0.13.0", + "kstring", + "liquid-derive", + "num-traits", + "pest", + "pest_derive", + "regex", + "serde", + "time", +] + +[[package]] +name = "liquid-derive" +version = "0.26.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b51f1d220e3fa869e24cfd75915efe3164bd09bb11b3165db3f37f57bf673e3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "liquid-lib" +version = "0.26.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59b1a298d3d2287ee5b1e43840d885b8fdfc37d3f4e90d82aacfd04d021618da" +dependencies = [ + "itertools 0.13.0", + "liquid-core", + "once_cell", + "percent-encoding", + "regex", + "time", + "unicode-segmentation", +] + +[[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 = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +dependencies = [ + "value-bag", +] + +[[package]] +name = "longest-increasing-subsequence" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3bd0dd2cd90571056fdb71f6275fada10131182f84899f4b2a916e565d81d86" + +[[package]] +name = "lru" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" +dependencies = [ + "hashbrown 0.14.5", +] + +[[package]] +name = "lzma-sys" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] +name = "mac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" + +[[package]] +name = "mac_address" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8836fae9d0d4be2c8b4efcdd79e828a2faa058a90d005abf42f91cac5493a08e" +dependencies = [ + "nix 0.28.0", + "winapi", +] + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "manganis" +version = "0.6.0-alpha.2" +dependencies = [ + "anyhow", + "base64 0.22.1", + "dioxus-core-types", + "dunce", + "manganis-macro", + "once_cell", + "serde", +] + +[[package]] +name = "manganis-core" +version = "0.6.0-alpha.2" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "manganis-macro" +version = "0.6.0-alpha.2" +dependencies = [ + "manganis-core", + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn 2.0.77", +] + +[[package]] +name = "markup5ever" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" +dependencies = [ + "log", + "phf 0.10.1", + "phf_codegen 0.10.0", + "string_cache", + "string_cache_codegen", + "tendril", +] + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + +[[package]] +name = "md5" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" + +[[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 = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "minicov" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c71e683cd655513b99affab7d317deb690528255a0d5f717f1024093c12b169" +dependencies = [ + "cc", + "walkdir", +] + +[[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", + "simd-adler32", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.48.0", +] + +[[package]] +name = "mio" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +dependencies = [ + "hermit-abi 0.3.9", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.52.0", +] + +[[package]] +name = "mirai-annotations" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9be0862c1b3f26a88803c4a49de6889c10e608b3ee9344e6ef5b45fb37ad3d1" + +[[package]] +name = "muda" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba8ac4080fb1e097c2c22acae467e46e4da72d941f02e82b67a87a2a89fa38b1" +dependencies = [ + "cocoa", + "crossbeam-channel", + "dpi", + "gtk", + "keyboard-types", + "libxdo", + "objc", + "once_cell", + "png", + "thiserror", + "windows-sys 0.59.0", +] + +[[package]] +name = "multer" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b" +dependencies = [ + "bytes", + "encoding_rs", + "futures-util", + "http 1.1.0", + "httparse", + "memchr", + "mime", + "spin", + "version_check", +] + +[[package]] +name = "names" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bddcd3bf5144b6392de80e04c347cd7fab2508f6df16a85fc496ecd5cec39bc" +dependencies = [ + "rand 0.8.5", +] + +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "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.6.0", + "jni-sys", + "log", + "ndk-sys", + "num_enum", + "raw-window-handle 0.6.2", + "thiserror", +] + +[[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 = "nested-suspense" +version = "0.1.0" +dependencies = [ + "dioxus", + "serde", + "tokio", +] + +[[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.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "cfg_aliases 0.1.1", + "libc", + "memoffset", +] + +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "cfg_aliases 0.2.1", + "libc", + "memoffset", +] + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + +[[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 = "nonempty" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9e591e719385e6ebaeb5ce5d3887f7d5676fceca6411d1925ccc95745f3d6f7" + +[[package]] +name = "normpath" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8911957c4b1549ac0dc74e30db9c8b0e66ddcd6d7acc33098f4c63a64a6d7ed" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "notify" +version = "6.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" +dependencies = [ + "bitflags 2.6.0", + "crossbeam-channel", + "filetime", + "fsevent-sys", + "inotify", + "kqueue", + "libc", + "log", + "mio 0.8.11", + "serde", + "walkdir", + "windows-sys 0.48.0", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[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-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand 0.8.5", + "serde", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "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.77", +] + +[[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-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "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", + "libm", +] + +[[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 3.2.0", + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + +[[package]] +name = "number_prefix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", + "objc_exception", +] + +[[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.6.0", + "block2", + "libc", + "objc2", + "objc2-core-data", + "objc2-core-image", + "objc2-foundation", + "objc2-quartz-core", +] + +[[package]] +name = "objc2-core-data" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" +dependencies = [ + "bitflags 2.6.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-encode" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7891e71393cd1f227313c9379a26a584ff3d7e6e7159e988851f0934c993f0f8" + +[[package]] +name = "objc2-foundation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" +dependencies = [ + "bitflags 2.6.0", + "block2", + "libc", + "objc2", +] + +[[package]] +name = "objc2-metal" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" +dependencies = [ + "bitflags 2.6.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.6.0", + "block2", + "objc2", + "objc2-foundation", + "objc2-metal", +] + +[[package]] +name = "objc_exception" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" +dependencies = [ + "cc", +] + +[[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.36.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" +dependencies = [ + "flate2", + "memchr", + "ruzstd", + "wasmparser 0.216.0", +] + +[[package]] +name = "once-cell-regex" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3de7e389a5043420c8f2b95ed03f3f104ad6f4c41f7d7e27298f033abc253e8" +dependencies = [ + "once_cell", + "regex", +] + +[[package]] +name = "once_cell" +version = "1.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ea5043e58958ee56f3e15a90aee535795cd7dfd319846288d93c5b57d85cbe" + +[[package]] +name = "oorandom" +version = "11.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "open" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61a877bf6abd716642a53ef1b89fb498923a4afca5c754f9050b4d081c05c4b3" +dependencies = [ + "is-wsl", + "libc", + "pathdiff", +] + +[[package]] +name = "openssl" +version = "0.10.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "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.77", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +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 = "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 = "os_pipe" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ffd2b0a5634335b135d5728d84c5e0fd726954b87111f7506a61c502280d982" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "owo-colors" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb37767f6569cd834a413442455e0f066d0d522de8630436e2a1761d9726ba56" +dependencies = [ + "supports-color 2.1.0", + "supports-color 3.0.1", +] + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "p384" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "pango" +version = "0.18.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" +dependencies = [ + "gio", + "glib", + "libc", + "once_cell", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.12.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", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "password-hash" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" +dependencies = [ + "base64ct", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "path-absolutize" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4af381fe79fa195b4909485d99f73a80792331df0625188e707854f0b3383f5" +dependencies = [ + "path-dedot", +] + +[[package]] +name = "path-dedot" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07ba0ad7e047712414213ff67533e6dd477af0a4e1d14fb52343e53d30ea9397" +dependencies = [ + "once_cell", +] + +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + +[[package]] +name = "pbkdf2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest", + "hmac", + "password-hash", + "sha2", +] + +[[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 = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pest" +version = "2.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c73c26c01b8c87956cea613c907c9d6ecffd8d18a2a5908e5de0adfaa185cea" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "664d22978e2815783adbdd2c588b455b1bd625299ce36b2a99881ac9627e6d8d" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2d5487022d5d33f4c30d91c22afa240ce2a644e87fe08caad974d4eab6badbe" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "pest_meta" +version = "2.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0091754bbd0ea592c4deb3a122ce8ecbb0753b738aa82bc055fcc2eccc8d8174" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + +[[package]] +name = "petgraph" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset", + "indexmap 2.5.0", +] + +[[package]] +name = "pgp" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "031fa1e28c4cb54c90502ef0642a44ef10ec8349349ebe6372089f1b1ef4f297" +dependencies = [ + "aes", + "base64 0.21.7", + "bitfield", + "block-padding", + "blowfish", + "bstr", + "buffer-redux", + "byteorder", + "camellia", + "cast5", + "cfb-mode", + "chrono", + "cipher", + "const-oid", + "crc24", + "curve25519-dalek", + "derive_builder", + "des", + "digest", + "dsa", + "ed25519-dalek", + "elliptic-curve", + "flate2", + "generic-array 0.14.7", + "hex", + "idea", + "iter-read", + "k256", + "log", + "md-5", + "nom", + "num-bigint-dig", + "num-traits", + "num_enum", + "p256", + "p384", + "rand 0.8.5", + "ripemd", + "rsa", + "sha1", + "sha2", + "sha3", + "signature", + "smallvec", + "thiserror", + "twofish", + "x25519-dalek", + "zeroize", +] + +[[package]] +name = "phf" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" +dependencies = [ + "phf_macros 0.8.0", + "phf_shared 0.8.0", + "proc-macro-hack", +] + +[[package]] +name = "phf" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" +dependencies = [ + "phf_shared 0.10.0", +] + +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_macros 0.11.2", + "phf_shared 0.11.2", +] + +[[package]] +name = "phf_codegen" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" +dependencies = [ + "phf_generator 0.8.0", + "phf_shared 0.8.0", +] + +[[package]] +name = "phf_codegen" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", +] + +[[package]] +name = "phf_generator" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" +dependencies = [ + "phf_shared 0.8.0", + "rand 0.7.3", +] + +[[package]] +name = "phf_generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +dependencies = [ + "phf_shared 0.10.0", + "rand 0.8.5", +] + +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared 0.11.2", + "rand 0.8.5", +] + +[[package]] +name = "phf_macros" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" +dependencies = [ + "phf_generator 0.8.0", + "phf_shared 0.8.0", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "phf_macros" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +dependencies = [ + "phf_generator 0.11.2", + "phf_shared 0.11.2", + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "phf_shared" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" +dependencies = [ + "siphasher", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[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 = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "plist" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42cf17e9a1800f5f396bc67d193dc9411b59012a5876445ef450d449881e1016" +dependencies = [ + "base64 0.22.1", + "indexmap 2.5.0", + "quick-xml", + "serde", + "time", +] + +[[package]] +name = "plotters" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "plotters-svg" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "png" +version = "0.17.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide 0.7.4", +] + +[[package]] +name = "polling" +version = "3.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2790cd301dec6cd3b7a025e4815cf825724a51c98dccfe6a3e55f05ffb6511" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi 0.4.0", + "pin-project-lite", + "rustix", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "pollster" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2" + +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "portable-atomic" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" + +[[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 = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "prettier-please" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32db37eb2b0ec0af154e9c1b33425902d8cd9481e35167c4e9ffb28fec3916bb" +dependencies = [ + "proc-macro2", + "syn 2.0.77", +] + +[[package]] +name = "pretty_assertions" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" +dependencies = [ + "diff", + "yansi", +] + +[[package]] +name = "prettyplease" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" +dependencies = [ + "proc-macro2", + "syn 2.0.77", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" +dependencies = [ + "toml_edit 0.20.7", +] + +[[package]] +name = "proc-macro-crate" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +dependencies = [ + "toml_edit 0.22.20", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +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.77", + "version_check", +] + +[[package]] +name = "prodash" +version = "28.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "744a264d26b88a6a7e37cbad97953fa233b94d585236310bcbc88474b4092d79" + +[[package]] +name = "prost" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" +dependencies = [ + "anyhow", + "itertools 0.12.1", + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "prost-types" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0" +dependencies = [ + "prost", +] + +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "quick-xml" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2" +dependencies = [ + "memchr", +] + +[[package]] +name = "quinn" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c7c5fdde3cdae7203427dc4f0a68fe0ed09833edc525a03456b153b79828684" +dependencies = [ + "bytes", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash 2.0.0", + "rustls 0.23.13", + "socket2", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "quinn-proto" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6" +dependencies = [ + "bytes", + "rand 0.8.5", + "ring", + "rustc-hash 2.0.0", + "rustls 0.23.13", + "slab", + "thiserror", + "tinyvec", + "tracing", +] + +[[package]] +name = "quinn-udp" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fe68c2e9e1a1234e218683dbdf9f9dfcb094113c5ac2b938dfcb9bab4c4140b" +dependencies = [ + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", + "rand_pcg", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[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 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.15", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "ratatui" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d16546c5b5962abf8ce6e2881e722b4e0ae3b6f1a08a26ae3573c55853ca68d3" +dependencies = [ + "bitflags 2.6.0", + "cassowary", + "compact_str", + "crossterm", + "itertools 0.13.0", + "lru", + "paste", + "stability", + "strum 0.26.3", + "strum_macros 0.26.4", + "unicode-segmentation", + "unicode-truncate", + "unicode-width", +] + +[[package]] +name = "raw-window-handle" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" + +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "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.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" +dependencies = [ + "bitflags 2.6.0", +] + +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom 0.2.15", + "libredox", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.4", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[package]] +name = "remove_dir_all" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c914caef075f03e9d5c568e2e71b3d3cf17dc61a5481ff379bb744721be0a75a" +dependencies = [ + "cfg-if", + "cvt", + "fs_at", + "libc", + "normpath", + "windows-sys 0.52.0", +] + +[[package]] +name = "rend" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" +dependencies = [ + "bytecheck", +] + +[[package]] +name = "reqwest" +version = "0.12.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63" +dependencies = [ + "base64 0.22.1", + "bytes", + "encoding_rs", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.4.6", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.4.1", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "mime_guess", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls 0.23.13", + "rustls-pemfile 2.1.3", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 1.0.1", + "system-configuration", + "tokio", + "tokio-native-tls", + "tokio-rustls", + "tokio-util", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "webpki-roots 0.26.5", + "windows-registry 0.2.0", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "rfd" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a73a7337fc24366edfca76ec521f51877b114e42dab584008209cca6719251" +dependencies = [ + "ashpd", + "block", + "dispatch", + "js-sys", + "log", + "objc", + "objc-foundation", + "objc_id", + "pollster", + "raw-window-handle 0.6.2", + "urlencoding", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-sys 0.48.0", +] + +[[package]] +name = "rhai" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61797318be89b1a268a018a92a7657096d83f3ecb31418b9e9c16dcbb043b702" +dependencies = [ + "ahash 0.8.11", + "bitflags 2.6.0", + "instant", + "num-traits", + "once_cell", + "rhai_codegen", + "smallvec", + "smartstring", + "thin-vec", +] + +[[package]] +name = "rhai_codegen" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5a11a05ee1ce44058fa3d5961d05194fdbe3ad6b40f904af764d81b86450e6b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.15", + "libc", + "spin", + "untrusted 0.9.0", + "windows-sys 0.52.0", +] + +[[package]] +name = "ripemd" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" +dependencies = [ + "digest", +] + +[[package]] +name = "rkyv" +version = "0.7.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9008cd6385b9e161d8229e1f6549dd23c3d022f132a2ea37ac3a10ac4935779b" +dependencies = [ + "bitvec", + "bytecheck", + "bytes", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "503d1d27590a2b0a3a4ca4c94755aa2875657196ecbf401a42eff41d7de532c0" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "router-static-generation" +version = "0.1.0" +dependencies = [ + "dioxus", + "tracing-subscriber", +] + +[[package]] +name = "rpm" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e68a0d60350e5f4229cd69f08ec8f373e34424701702d1ee51a89aee1e9adcd1" +dependencies = [ + "bitflags 2.6.0", + "bzip2", + "chrono", + "cpio", + "digest", + "enum-display-derive", + "enum-primitive-derive", + "flate2", + "hex", + "itertools 0.12.1", + "log", + "md-5", + "nom", + "num", + "num-derive", + "num-traits", + "pgp", + "sha1", + "sha2", + "thiserror", + "xz2", + "zstd 0.13.2", +] + +[[package]] +name = "rsa" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" +dependencies = [ + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core 0.6.4", + "signature", + "spki", + "subtle", + "zeroize", +] + +[[package]] +name = "rust_decimal" +version = "1.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b082d80e3e3cc52b2ed634388d436fe1f4de6af5786cc2de9ba9737527bdf555" +dependencies = [ + "arrayvec", + "borsh", + "bytes", + "num-traits", + "rand 0.8.5", + "rkyv", + "serde", + "serde_json", +] + +[[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.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.38.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.21.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +dependencies = [ + "ring", + "rustls-webpki 0.101.7", + "sct", +] + +[[package]] +name = "rustls" +version = "0.23.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8" +dependencies = [ + "aws-lc-rs", + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki 0.102.8", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcaf18a4f2be7326cd874a5fa579fae794320a0f388d365dca7e480e55f83f8a" +dependencies = [ + "openssl-probe", + "rustls-pemfile 2.1.3", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", +] + +[[package]] +name = "rustls-pemfile" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" +dependencies = [ + "base64 0.22.1", + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted 0.9.0", +] + +[[package]] +name = "rustls-webpki" +version = "0.102.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted 0.9.0", +] + +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + +[[package]] +name = "ruzstd" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99c3938e133aac070997ddc684d4b393777d293ba170f2988c8fd5ea2ad4ce21" +dependencies = [ + "twox-hash", +] + +[[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 = "sanitize-filename" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ed72fbaf78e6f2d41744923916966c4fbe3d7c74e3037a8ee482f1115572603" +dependencies = [ + "lazy_static", + "regex", +] + +[[package]] +name = "schannel" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" +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 = "scratch" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3cf7c11c38cb994f3d40e8a8cde3bbd1f72a435e4c49e85d6553d8312306152" + +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted 0.9.0", +] + +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array 0.14.7", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.6.0", + "core-foundation 0.9.4", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "selectors" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe" +dependencies = [ + "bitflags 1.3.2", + "cssparser", + "derive_more", + "fxhash", + "log", + "matches", + "phf 0.8.0", + "phf_codegen 0.8.0", + "precomputed-hash", + "servo_arc", + "smallvec", + "thin-slice", +] + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +dependencies = [ + "serde", +] + +[[package]] +name = "send_wrapper" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" +dependencies = [ + "futures-core", +] + +[[package]] +name = "separator" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f97841a747eef040fcd2e7b3b9a220a7205926e60488e673d9e4926d27772ce5" + +[[package]] +name = "serde" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-untagged" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2676ba99bd82f75cae5cbd2c8eda6fa0b8760f18978ea840e980dd5567b5c5b6" +dependencies = [ + "erased-serde", + "serde", + "typeid", +] + +[[package]] +name = "serde-wasm-bindgen" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3b143e2833c57ab9ad3ea280d21fd34e285a42837aeb0ee301f4f41890fa00e" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "serde-wasm-bindgen" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "serde_derive" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "serde_json" +version = "1.0.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +dependencies = [ + "itoa 1.0.11", + "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 1.0.11", + "serde", +] + +[[package]] +name = "serde_qs" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0431a35568651e363364210c91983c1da5eb29404d9f0928b67d4ebcfa7d330c" +dependencies = [ + "percent-encoding", + "serde", + "thiserror", +] + +[[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.77", +] + +[[package]] +name = "serde_spanned" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" +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 1.0.11", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "3.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cecfa94848272156ea67b2b1a53f20fc7bc638c4a46d2f8abde08f05f4b857" +dependencies = [ + "base64 0.22.1", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.5.0", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8fee4991ef4f274617a51ad4af30519438dacb2f56ac773b08a1922ff743350" +dependencies = [ + "darling 0.20.10", + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "server_fn" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fae7a3038a32e5a34ba32c6c45eb4852f8affaf8b794ebfcd4b1099e2d62ebe" +dependencies = [ + "axum 0.7.5", + "bytes", + "const_format", + "dashmap", + "futures", + "gloo-net 0.6.0", + "http 1.1.0", + "http-body-util", + "hyper 1.4.1", + "inventory", + "js-sys", + "once_cell", + "reqwest", + "send_wrapper", + "serde", + "serde_json", + "serde_qs", + "server_fn_macro_default", + "thiserror", + "tower", + "tower-layer", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "xxhash-rust", +] + +[[package]] +name = "server_fn_macro" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faaaf648c6967aef78177c0610478abb5a3455811f401f3c62d10ae9bd3901a1" +dependencies = [ + "const_format", + "convert_case 0.6.0", + "proc-macro2", + "quote", + "syn 2.0.77", + "xxhash-rust", +] + +[[package]] +name = "server_fn_macro_default" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f2aa8119b558a17992e0ac1fd07f080099564f24532858811ce04f742542440" +dependencies = [ + "server_fn_macro", + "syn 2.0.77", +] + +[[package]] +name = "servo_arc" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432" +dependencies = [ + "nodrop", + "stable_deref_trait", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha1_smol" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + +[[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 = "shell-words" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" +dependencies = [ + "libc", + "mio 0.8.11", + "signal-hook", +] + +[[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 = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core 0.6.4", +] + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "simdutf8" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" + +[[package]] +name = "simple-static-generation" +version = "0.1.0" +dependencies = [ + "dioxus", + "tracing-subscriber", +] + +[[package]] +name = "simple_logger" +version = "4.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e7e46c8c90251d47d08b28b8a419ffb4aede0f87c2eea95e17d1d5bacbf3ef1" +dependencies = [ + "colored", + "log", + "time", + "windows-sys 0.48.0", +] + +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "sledgehammer_bindgen" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49e83e178d176459c92bc129cfd0958afac3ced925471b889b3a75546cfc4133" +dependencies = [ + "sledgehammer_bindgen_macro", + "wasm-bindgen", +] + +[[package]] +name = "sledgehammer_bindgen_macro" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33a1b4f13e2bbf2f5b29d09dfebc9de69229ffee245aed80e3b70f9b5fd28c06" +dependencies = [ + "quote", + "syn 2.0.77", +] + +[[package]] +name = "sledgehammer_utils" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "debdd4b83524961983cea3c55383b3910fd2f24fd13a188f5b091d2d504a61ae" +dependencies = [ + "rustc-hash 1.1.0", +] + +[[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 = "smartstring" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29" +dependencies = [ + "autocfg", + "static_assertions", + "version_check", +] + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "socks" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c3dbbd9ae980613c6dd8e28a9407b50509d3803b57624d5dfe8315218cd58b" +dependencies = [ + "byteorder", + "libc", + "winapi", +] + +[[package]] +name = "soup3" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "471f924a40f31251afc77450e781cb26d55c0b650842efafc9c6cbd2f7cc4f9f" +dependencies = [ + "futures-channel", + "gio", + "glib", + "libc", + "soup3-sys", +] + +[[package]] +name = "soup3-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebe8950a680a12f24f15ebe1bf70db7af98ad242d9db43596ad3108aab86c27" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "sqlformat" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bba3a93db0cc4f7bdece8bb09e77e2e785c20bfebf79eb8340ed80708048790" +dependencies = [ + "nom", + "unicode_categories", +] + +[[package]] +name = "sqlx" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9a2ccff1a000a5a59cd33da541d9f2fdcd9e6e8229cc200565942bff36d0aaa" +dependencies = [ + "sqlx-core", + "sqlx-macros", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", +] + +[[package]] +name = "sqlx-core" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24ba59a9342a3d9bab6c56c118be528b27c9b60e490080e9711a04dccac83ef6" +dependencies = [ + "ahash 0.8.11", + "atoi", + "bigdecimal", + "bit-vec", + "byteorder", + "bytes", + "chrono", + "crc", + "crossbeam-queue", + "either", + "event-listener 2.5.3", + "futures-channel", + "futures-core", + "futures-intrusive", + "futures-io", + "futures-util", + "hashlink", + "hex", + "indexmap 2.5.0", + "ipnetwork", + "log", + "mac_address", + "memchr", + "native-tls", + "once_cell", + "paste", + "percent-encoding", + "rust_decimal", + "rustls 0.21.12", + "rustls-pemfile 1.0.4", + "serde", + "serde_json", + "sha2", + "smallvec", + "sqlformat", + "thiserror", + "time", + "tokio", + "tokio-stream", + "tracing", + "url", + "uuid", + "webpki-roots 0.25.4", +] + +[[package]] +name = "sqlx-macros" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea40e2345eb2faa9e1e5e326db8c34711317d2b5e08d0d5741619048a803127" +dependencies = [ + "proc-macro2", + "quote", + "sqlx-core", + "sqlx-macros-core", + "syn 1.0.109", +] + +[[package]] +name = "sqlx-macros-core" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5833ef53aaa16d860e92123292f1f6a3d53c34ba8b1969f152ef1a7bb803f3c8" +dependencies = [ + "dotenvy", + "either", + "heck 0.4.1", + "hex", + "once_cell", + "proc-macro2", + "quote", + "serde", + "serde_json", + "sha2", + "sqlx-core", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", + "syn 1.0.109", + "tempfile", + "tokio", + "url", +] + +[[package]] +name = "sqlx-mysql" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ed31390216d20e538e447a7a9b959e06ed9fc51c37b514b46eb758016ecd418" +dependencies = [ + "atoi", + "base64 0.21.7", + "bigdecimal", + "bitflags 2.6.0", + "byteorder", + "bytes", + "chrono", + "crc", + "digest", + "dotenvy", + "either", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "generic-array 0.14.7", + "hex", + "hkdf", + "hmac", + "itoa 1.0.11", + "log", + "md-5", + "memchr", + "once_cell", + "percent-encoding", + "rand 0.8.5", + "rsa", + "rust_decimal", + "serde", + "sha1", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror", + "time", + "tracing", + "uuid", + "whoami", +] + +[[package]] +name = "sqlx-postgres" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c824eb80b894f926f89a0b9da0c7f435d27cdd35b8c655b114e58223918577e" +dependencies = [ + "atoi", + "base64 0.21.7", + "bigdecimal", + "bit-vec", + "bitflags 2.6.0", + "byteorder", + "chrono", + "crc", + "dotenvy", + "etcetera", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "hex", + "hkdf", + "hmac", + "home", + "ipnetwork", + "itoa 1.0.11", + "log", + "mac_address", + "md-5", + "memchr", + "num-bigint", + "once_cell", + "rand 0.8.5", + "rust_decimal", + "serde", + "serde_json", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror", + "time", + "tracing", + "uuid", + "whoami", +] + +[[package]] +name = "sqlx-sqlite" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b244ef0a8414da0bed4bb1910426e890b19e5e9bccc27ada6b797d05c55ae0aa" +dependencies = [ + "atoi", + "chrono", + "flume", + "futures-channel", + "futures-core", + "futures-executor", + "futures-intrusive", + "futures-util", + "libsqlite3-sys", + "log", + "percent-encoding", + "serde", + "sqlx-core", + "time", + "tracing", + "url", + "urlencoding", + "uuid", +] + +[[package]] +name = "stability" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d904e7009df136af5297832a3ace3370cd14ff1546a232f4f185036c2736fcac" +dependencies = [ + "quote", + "syn 2.0.77", +] + +[[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 = "string_cache" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" +dependencies = [ + "new_debug_unreachable", + "once_cell", + "parking_lot", + "phf_shared 0.10.0", + "precomputed-hash", + "serde", +] + +[[package]] +name = "string_cache_codegen" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", + "proc-macro2", + "quote", +] + +[[package]] +name = "stringprep" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" +dependencies = [ + "unicode-bidi", + "unicode-normalization", + "unicode-properties", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" + +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros 0.26.4", +] + +[[package]] +name = "strum_macros" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "rustversion", + "syn 1.0.109", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.77", +] + +[[package]] +name = "subprocess" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c2e86926081dda636c546d8c5e641661049d7562a68f5488be4a1f7f66f6086" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "supports-color" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6398cde53adc3c4557306a96ce67b302968513830a77a95b2b17305d9719a89" +dependencies = [ + "is-terminal", + "is_ci", +] + +[[package]] +name = "supports-color" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8775305acf21c96926c900ad056abeef436701108518cf890020387236ac5a77" +dependencies = [ + "is_ci", +] + +[[package]] +name = "suspense-carousel" +version = "0.6.0-alpha.2" +dependencies = [ + "dioxus", + "gloo-timers 0.3.0", + "serde", + "tokio", +] + +[[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.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn_derive" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1329189c02ff984e9736652b1631330da25eaa6bc639089ed4915d25446cbe7b" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "sync_wrapper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +dependencies = [ + "futures-core", +] + +[[package]] +name = "sysctl" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225e483f02d0ad107168dc57381a8a40c3aeea6abe47f37506931f861643cfa8" +dependencies = [ + "bitflags 1.3.2", + "byteorder", + "libc", + "thiserror", + "walkdir", +] + +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags 2.6.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 0.15.8", + "heck 0.5.0", + "pkg-config", + "toml", + "version-compare", +] + +[[package]] +name = "tao" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a93f2c6b8fdaeb7f417bda89b5bc767999745c3052969664ae1fa65892deb7e" +dependencies = [ + "bitflags 2.6.0", + "cocoa", + "core-foundation 0.10.0", + "core-graphics", + "crossbeam-channel", + "dispatch", + "dlopen2", + "dpi", + "gdkwayland-sys", + "gdkx11-sys", + "gtk", + "instant", + "jni", + "lazy_static", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "objc", + "once_cell", + "parking_lot", + "raw-window-handle 0.5.2", + "raw-window-handle 0.6.2", + "scopeguard", + "tao-macros", + "unicode-segmentation", + "url", + "windows", + "windows-core 0.58.0", + "windows-version", + "x11-dl", +] + +[[package]] +name = "tao-macros" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tar" +version = "0.4.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb797dad5fb5b76fcf519e702f4a589483b5ef06567f160c392832c1f5e44909" +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 = "tauri-bundler" +version = "2.0.0-rc.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afefaa18bdaa49afff89a299d6afb05de7ffc2017d84d717239455e6645d21ea" +dependencies = [ + "anyhow", + "ar", + "bitness", + "dirs", + "dunce", + "flate2", + "glob", + "handlebars", + "heck 0.5.0", + "hex", + "image", + "log", + "md5", + "os_pipe", + "plist", + "regex", + "rpm", + "semver", + "serde", + "serde_json", + "sha1", + "sha2", + "strsim 0.11.1", + "tar", + "tauri-icns", + "tauri-macos-sign", + "tauri-utils", + "tempfile", + "thiserror", + "time", + "ureq", + "uuid", + "walkdir", + "windows-registry 0.1.2", + "windows-sys 0.52.0", + "zip", +] + +[[package]] +name = "tauri-icns" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b7eb4d0d43724ba9ba6a6717420ee68aee377816a3edbb45db8c18862b1431" +dependencies = [ + "byteorder", + "png", +] + +[[package]] +name = "tauri-macos-sign" +version = "0.1.0-beta.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a689cc26acd1e619b317f7cec95db2eb9fa1c69888030162a546a7fafa7eb3d" +dependencies = [ + "anyhow", + "dirs-next", + "once-cell-regex", + "os_pipe", + "plist", + "rand 0.8.5", + "serde", + "serde_json", + "tempfile", + "x509-certificate", +] + +[[package]] +name = "tauri-utils" +version = "2.0.0-rc.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3019641087c9039b57ebfca95fa42a93c07056845b7d8d57c8966061bcee83b4" +dependencies = [ + "ctor", + "dunce", + "glob", + "html5ever", + "infer", + "json-patch", + "kuchikiki", + "log", + "memchr", + "phf 0.11.2", + "regex", + "semver", + "serde", + "serde-untagged", + "serde_json", + "serde_with", + "thiserror", + "toml", + "url", + "urlpattern", + "walkdir", +] + +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys 0.52.0", +] + +[[package]] +name = "tendril" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" +dependencies = [ + "futf", + "mac", + "utf-8", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "terminal-prompt" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "572818b3472910acbd5dff46a3413715c18e934b071ab2ba464a7b2c2af16376" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "terminal_size" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" +dependencies = [ + "rustix", + "windows-sys 0.48.0", +] + +[[package]] +name = "thin-slice" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" + +[[package]] +name = "thin-vec" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a38c90d48152c236a3ab59271da4f4ae63d678c5d7ad6b7714d7cb9760be5e4b" + +[[package]] +name = "thiserror" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "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.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "itoa 1.0.11", + "libc", + "num-conv", + "num_threads", + "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.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio 1.0.2", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-io-timeout" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" +dependencies = [ + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +dependencies = [ + "rustls 0.23.13", + "rustls-pki-types", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", + "tokio-util", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite 0.21.0", +] + +[[package]] +name = "tokio-util" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +dependencies = [ + "bytes", + "futures-core", + "futures-io", + "futures-sink", + "futures-util", + "hashbrown 0.14.5", + "pin-project-lite", + "slab", + "tokio", +] + +[[package]] +name = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "indexmap 2.5.0", + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.22.20", +] + +[[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.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.5.0", + "toml_datetime", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.20.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" +dependencies = [ + "indexmap 2.5.0", + "toml_datetime", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.22.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" +dependencies = [ + "indexmap 2.5.0", + "serde", + "serde_spanned", + "toml_datetime", + "winnow 0.6.18", +] + +[[package]] +name = "tonic" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76c4eb7a4e9ef9d4763600161f12f5070b92a578e1b634db88a6887844c91a13" +dependencies = [ + "async-stream", + "async-trait", + "axum 0.6.20", + "base64 0.21.7", + "bytes", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.30", + "hyper-timeout", + "percent-encoding", + "pin-project", + "prost", + "tokio", + "tokio-stream", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "indexmap 1.9.3", + "pin-project", + "pin-project-lite", + "rand 0.8.5", + "slab", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" +dependencies = [ + "async-compression", + "base64 0.21.7", + "bitflags 2.6.0", + "bytes", + "futures-core", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "http-range-header", + "httpdate", + "iri-string", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite", + "tokio", + "tokio-util", + "tower", + "tower-layer", + "tower-service", + "tracing", + "uuid", +] + +[[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.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-fluent-assertions" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12de1a8c6bcfee614305e836308b596bbac831137a04c61f7e5b0b0bf2cfeaf6" +dependencies = [ + "tracing", + "tracing-core", + "tracing-subscriber", +] + +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project", + "tracing", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "tracing-wasm" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4575c663a174420fa2d78f4108ff68f65bf2fbb7dd89f33749b6e826b3626e07" +dependencies = [ + "tracing", + "tracing-subscriber", + "wasm-bindgen", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "trybuild" +version = "1.0.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "207aa50d36c4be8d8c6ea829478be44a372c6a77669937bb39c698e52f1491e8" +dependencies = [ + "dissimilar", + "glob", + "serde", + "serde_derive", + "serde_json", + "termcolor", + "toml", +] + +[[package]] +name = "tungstenite" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http 1.1.0", + "httparse", + "log", + "rand 0.8.5", + "sha1", + "thiserror", + "url", + "utf-8", +] + +[[package]] +name = "tungstenite" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e2ce1e47ed2994fd43b04c8f618008d4cabdd5ee34027cf14f9d918edd9c8" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http 1.1.0", + "httparse", + "log", + "rand 0.8.5", + "sha1", + "thiserror", + "utf-8", +] + +[[package]] +name = "twofish" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a78e83a30223c757c3947cd144a31014ff04298d8719ae10d03c31c0448c8013" +dependencies = [ + "cipher", +] + +[[package]] +name = "twox-hash" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" +dependencies = [ + "cfg-if", + "static_assertions", +] + +[[package]] +name = "typeid" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e13db2e0ccd5e14a544e8a246ba2312cd25223f616442d7f2cb0e3db614236e" + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + +[[package]] +name = "uds_windows" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" +dependencies = [ + "memoffset", + "tempfile", + "winapi", +] + +[[package]] +name = "uname" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b72f89f0ca32e4db1c04e2a72f5345d59796d4866a1ee0609084569f73683dc8" +dependencies = [ + "libc", +] + +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-ucd-ident" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + +[[package]] +name = "unicase" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-bom" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eec5d1121208364f6793f7d2e222bf75a915c19557537745b195b253dd64217" + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-properties" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ea75f83c0137a9b98608359a5f1af8144876eb67bcb1ce837368e906a9f524" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-truncate" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" +dependencies = [ + "itertools 0.13.0", + "unicode-segmentation", + "unicode-width", +] + +[[package]] +name = "unicode-width" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" + +[[package]] +name = "unicode-xid" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a" + +[[package]] +name = "unicode_categories" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "ureq" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b74fc6b57825be3373f7054754755f03ac3a8f5d70015ccad699ba2029956f4a" +dependencies = [ + "base64 0.22.1", + "log", + "once_cell", + "rustls 0.23.13", + "rustls-pki-types", + "socks", + "url", + "webpki-roots 0.26.5", +] + +[[package]] +name = "url" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +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 = "urlpattern" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70acd30e3aa1450bc2eece896ce2ad0d178e9c079493819301573dae3c37ba6d" +dependencies = [ + "regex", + "serde", + "unic-ucd-ident", + "url", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "uuid" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" +dependencies = [ + "getrandom 0.2.15", + "serde", + "sha1_smol", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "value-bag" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a84c137d37ab0142f0f2ddfe332651fdbf252e7b7dbb4e67b6c1f1b2e925101" + +[[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 = "walrus" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "160c3708e3ad718ab4d84bec8de8c3d3450cd2902bd6c3ee3bbf50ad7529c2ad" +dependencies = [ + "anyhow", + "gimli 0.26.2", + "id-arena", + "leb128", + "log", + "walrus-macro", + "wasm-encoder", + "wasmparser 0.212.0", +] + +[[package]] +name = "walrus-macro" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e5bd22c71e77d60140b0bd5be56155a37e5bd14e24f5f87298040d0cc40d7" +dependencies = [ + "heck 0.3.3", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0c672c7629eeed21c37d7a96ee9c0287b86a5e29b5730773117e4261d1a73ca" +dependencies = [ + "pin-project", + "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.77", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.77", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-cli-support" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a7f49ca6e7da74d53d6d716b868828a47d1c3808a8f676e2104fadd3b9874bb" +dependencies = [ + "anyhow", + "base64 0.22.1", + "log", + "rustc-demangle", + "serde_json", + "tempfile", + "unicode-ident", + "walrus", + "wasm-bindgen-externref-xform", + "wasm-bindgen-multi-value-xform", + "wasm-bindgen-shared", + "wasm-bindgen-threads-xform", + "wasm-bindgen-wasm-conventions", + "wasm-bindgen-wasm-interpreter", +] + +[[package]] +name = "wasm-bindgen-externref-xform" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c1f37f2705f4177cc87e5b2763115d078d39e4843e351438b34b085d53a8930" +dependencies = [ + "anyhow", + "walrus", + "wasm-bindgen-wasm-conventions", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-multi-value-xform" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2864e3f221fef3869992b541b238e55f53f99b43c4ce220422a6e1ec9458dc21" +dependencies = [ + "anyhow", + "walrus", + "wasm-bindgen-wasm-conventions", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" + +[[package]] +name = "wasm-bindgen-test" +version = "0.3.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68497a05fb21143a08a7d24fc81763384a3072ee43c44e86aad1744d6adef9d9" +dependencies = [ + "console_error_panic_hook", + "js-sys", + "minicov", + "scoped-tls", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-bindgen-test-macro", +] + +[[package]] +name = "wasm-bindgen-test-macro" +version = "0.3.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b8220be1fa9e4c889b30fd207d4906657e7e90b12e0e6b0c8b8d8709f5de021" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "wasm-bindgen-threads-xform" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e9ca60ee029d64cf6f63a630050935360c3877844f6de38e8287afb8f1d2715" +dependencies = [ + "anyhow", + "walrus", + "wasm-bindgen-wasm-conventions", +] + +[[package]] +name = "wasm-bindgen-wasm-conventions" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e429e00149de60ffc768e6e1f0563778a237e7c11cc01801edf9734bff8161f" +dependencies = [ + "anyhow", + "leb128", + "log", + "walrus", + "wasmparser 0.212.0", +] + +[[package]] +name = "wasm-bindgen-wasm-interpreter" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771c49db324f195f221796bf3868247dd91cca4a85604dcb3729213e439abe59" +dependencies = [ + "anyhow", + "log", + "walrus", + "wasm-bindgen-wasm-conventions", +] + +[[package]] +name = "wasm-encoder" +version = "0.212.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501940df4418b8929eb6d52f1aade1fdd15a5b86c92453cb696e3c906bd3fc33" +dependencies = [ + "leb128", +] + +[[package]] +name = "wasm-opt" +version = "0.116.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd87a4c135535ffed86123b6fb0f0a5a0bc89e50416c942c5f0662c645f679c" +dependencies = [ + "anyhow", + "libc", + "strum 0.24.1", + "strum_macros 0.24.3", + "tempfile", + "thiserror", + "wasm-opt-cxx-sys", + "wasm-opt-sys", +] + +[[package]] +name = "wasm-opt-cxx-sys" +version = "0.116.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c57b28207aa724318fcec6575fe74803c23f6f266fce10cbc9f3f116762f12e" +dependencies = [ + "anyhow", + "cxx", + "cxx-build", + "wasm-opt-sys", +] + +[[package]] +name = "wasm-opt-sys" +version = "0.116.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a1cce564dc768dacbdb718fc29df2dba80bd21cb47d8f77ae7e3d95ceb98cbe" +dependencies = [ + "anyhow", + "cc", + "cxx", + "cxx-build", +] + +[[package]] +name = "wasm-streams" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "wasmparser" +version = "0.212.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d28bc49ba1e5c5b61ffa7a2eace10820443c4b7d1c0b144109261d14570fdf8" +dependencies = [ + "ahash 0.8.11", + "bitflags 2.6.0", + "hashbrown 0.14.5", + "indexmap 2.5.0", + "semver", + "serde", +] + +[[package]] +name = "wasmparser" +version = "0.216.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcdee6bea3619d311fb4b299721e89a986c3470f804b6d534340e412589028e3" +dependencies = [ + "bitflags 2.6.0", +] + +[[package]] +name = "web-sys" +version = "0.3.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" +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", + "wasm-bindgen", +] + +[[package]] +name = "webbrowser" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "425ba64c1e13b1c6e8c5d2541c8fac10022ca584f33da781db01b5756aef1f4e" +dependencies = [ + "block2", + "core-foundation 0.9.4", + "home", + "jni", + "log", + "ndk-context", + "objc2", + "objc2-foundation", + "url", + "web-sys", +] + +[[package]] +name = "webkit2gtk" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76b1bc1e54c581da1e9f179d0b38512ba358fb1af2d634a1affe42e37172361a" +dependencies = [ + "bitflags 1.3.2", + "cairo-rs", + "gdk", + "gdk-sys", + "gio", + "gio-sys", + "glib", + "glib-sys", + "gobject-sys", + "gtk", + "gtk-sys", + "javascriptcore-rs", + "libc", + "once_cell", + "soup3", + "webkit2gtk-sys", +] + +[[package]] +name = "webkit2gtk-sys" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62daa38afc514d1f8f12b8693d30d5993ff77ced33ce30cd04deebc267a6d57c" +dependencies = [ + "bitflags 1.3.2", + "cairo-sys-rs", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "gtk-sys", + "javascriptcore-rs-sys", + "libc", + "pkg-config", + "soup3-sys", + "system-deps", +] + +[[package]] +name = "webpki-roots" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + +[[package]] +name = "webpki-roots" +version = "0.26.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bd24728e5af82c6c4ec1b66ac4844bdf8156257fccda846ec58b42cd0cdbe6a" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "webview2-com" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f61ff3d9d0ee4efcb461b14eb3acfda2702d10dc329f339303fc3e57215ae2c" +dependencies = [ + "webview2-com-macros", + "webview2-com-sys", + "windows", + "windows-core 0.58.0", + "windows-implement", + "windows-interface", +] + +[[package]] +name = "webview2-com-macros" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d228f15bba3b9d56dde8bddbee66fa24545bd17b48d5128ccf4a8742b18e431" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "webview2-com-sys" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3a3e2eeb58f82361c93f9777014668eb3d07e7d174ee4c819575a9208011886" +dependencies = [ + "thiserror", + "windows", + "windows-core 0.58.0", +] + +[[package]] +name = "weezl" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + +[[package]] +name = "whoami" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d" +dependencies = [ + "redox_syscall", + "wasite", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.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.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.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result 0.2.0", + "windows-strings", + "windows-targets 0.52.6", +] + +[[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.77", +] + +[[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.77", +] + +[[package]] +name = "windows-registry" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acc134c90a0318d873ec962b13149e9c862ff0d2669082a709a4810167a3c6ee" +dependencies = [ + "windows-result 0.1.2", + "windows-targets 0.52.6", +] + +[[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-version" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6998aa457c9ba8ff2fb9f13e9d2a930dabcea28f1d0ab94d687d8b3654844515" +dependencies = [ + "windows-targets 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 = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +dependencies = [ + "memchr", +] + +[[package]] +name = "wry" +version = "0.43.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4d715cf5fe88e9647f3d17b207b6d060d4a88e7171d4ccb2d2c657dd1d44728" +dependencies = [ + "base64 0.22.1", + "block", + "cocoa", + "core-graphics", + "crossbeam-channel", + "dpi", + "dunce", + "gdkx11", + "gtk", + "html5ever", + "http 1.1.0", + "javascriptcore-rs", + "jni", + "kuchikiki", + "libc", + "ndk", + "objc", + "objc_id", + "once_cell", + "percent-encoding", + "raw-window-handle 0.6.2", + "sha2", + "soup3", + "tao-macros", + "thiserror", + "webkit2gtk", + "webkit2gtk-sys", + "webview2-com", + "windows", + "windows-core 0.58.0", + "windows-version", + "x11-dl", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "x11" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "x25519-dalek" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" +dependencies = [ + "curve25519-dalek", + "rand_core 0.6.4", + "serde", + "zeroize", +] + +[[package]] +name = "x509-certificate" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66534846dec7a11d7c50a74b7cdb208b9a581cad890b7866430d438455847c85" +dependencies = [ + "bcder", + "bytes", + "chrono", + "der", + "hex", + "pem", + "ring", + "signature", + "spki", + "thiserror", + "zeroize", +] + +[[package]] +name = "xattr" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" +dependencies = [ + "libc", + "linux-raw-sys", + "rustix", +] + +[[package]] +name = "xdg-home" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "xxhash-rust" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a5cbf750400958819fb6178eaa83bee5cd9c29a26a40cc241df8c70fdd46984" + +[[package]] +name = "xz2" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2" +dependencies = [ + "lzma-sys", +] + +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + +[[package]] +name = "zbus" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb97012beadd29e654708a0fdb4c84bc046f537aecfde2c3ee0a9e4b4d48c725" +dependencies = [ + "async-broadcast", + "async-process", + "async-recursion", + "async-trait", + "enumflags2", + "event-listener 5.3.1", + "futures-core", + "futures-sink", + "futures-util", + "hex", + "nix 0.29.0", + "ordered-stream", + "rand 0.8.5", + "serde", + "serde_repr", + "sha1", + "static_assertions", + "tokio", + "tracing", + "uds_windows", + "windows-sys 0.52.0", + "xdg-home", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e" +dependencies = [ + "proc-macro-crate 3.2.0", + "proc-macro2", + "quote", + "syn 2.0.77", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" +dependencies = [ + "serde", + "static_assertions", + "zvariant", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "zip" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" +dependencies = [ + "aes", + "byteorder", + "bzip2", + "constant_time_eq", + "crc32fast", + "crossbeam-utils", + "flate2", + "hmac", + "pbkdf2", + "sha1", + "time", + "zstd 0.11.2+zstd.1.5.2", +] + +[[package]] +name = "zstd" +version = "0.11.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" +dependencies = [ + "zstd-safe 5.0.2+zstd.1.5.2", +] + +[[package]] +name = "zstd" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" +dependencies = [ + "zstd-safe 7.2.1", +] + +[[package]] +name = "zstd-safe" +version = "5.0.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-safe" +version = "7.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.13+zstd.1.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" +dependencies = [ + "cc", + "pkg-config", +] + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "zvariant" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2084290ab9a1c471c38fc524945837734fbf124487e105daec2bb57fd48c81fe" +dependencies = [ + "endi", + "enumflags2", + "serde", + "static_assertions", + "url", + "zvariant_derive", +] + +[[package]] +name = "zvariant_derive" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449" +dependencies = [ + "proc-macro-crate 3.2.0", + "proc-macro2", + "quote", + "syn 2.0.77", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] diff --git a/packages/core-macro/src/lib.rs b/packages/core-macro/src/lib.rs index e02f3a2d94..3ff19d9be2 100644 --- a/packages/core-macro/src/lib.rs +++ b/packages/core-macro/src/lib.rs @@ -13,6 +13,16 @@ mod utils; use dioxus_rsx as rsx; +#[doc = include_str!("../docs/props.md")] +#[proc_macro_derive(Props, attributes(props))] +pub fn derive_props(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as syn::DeriveInput); + match props::impl_my_derive(&input) { + Ok(output) => output.into(), + Err(error) => error.to_compile_error().into(), + } +} + #[doc = include_str!("../docs/rsx.md")] #[proc_macro] pub fn rsx(tokens: TokenStream) -> TokenStream { @@ -29,13 +39,3 @@ pub fn component(_args: TokenStream, input: TokenStream) -> TokenStream { .into_token_stream() .into() } - -#[doc = include_str!("../docs/props.md")] -#[proc_macro_derive(Props, attributes(props))] -pub fn derive_props(input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as syn::DeriveInput); - match props::impl_my_derive(&input) { - Ok(output) => output.into(), - Err(error) => error.to_compile_error().into(), - } -} From efa17011fb346e14bedd6c02141af379ab41ce68 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Mon, 16 Sep 2024 16:52:50 -0700 Subject: [PATCH 117/139] drop random change --- examples/ssg-github-pages/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/ssg-github-pages/src/main.rs b/examples/ssg-github-pages/src/main.rs index 205dc32b00..ba80f9983b 100644 --- a/examples/ssg-github-pages/src/main.rs +++ b/examples/ssg-github-pages/src/main.rs @@ -6,7 +6,7 @@ use dioxus::prelude::*; // Generate all routes and output them to the static path fn main() { - dioxus::builder() + LaunchBuilder::new() .with_cfg(dioxus::static_site_generation::Config::new().github_pages()) .launch(|| { rsx! { From 5b6897a788eccbfdbec0b00aef0e2877db33814d Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Mon, 16 Sep 2024 16:54:41 -0700 Subject: [PATCH 118/139] fix another stray --- packages/ssr/src/renderer.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/ssr/src/renderer.rs b/packages/ssr/src/renderer.rs index 86d24cbbde..85d41003ae 100644 --- a/packages/ssr/src/renderer.rs +++ b/packages/ssr/src/renderer.rs @@ -1,7 +1,6 @@ use super::cache::Segment; use crate::cache::StringCache; use dioxus_core::{prelude::*, AttributeValue, DynamicNode}; -use dioxus_core_types::event_bubbles; use rustc_hash::FxHashMap; use std::fmt::Write; use std::sync::Arc; From ac5b01dab1ff43b91da3143a8c9f548b9dcc78db Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Mon, 16 Sep 2024 17:45:50 -0700 Subject: [PATCH 119/139] revert changes from cli/dev refactor --- packages/cli/Cargo.toml | 92 +- packages/cli/src/assets.rs | 1168 +++-------------- packages/cli/src/builder/builder.rs | 149 --- packages/cli/src/builder/cargo.rs | 431 +++--- packages/cli/src/builder/fullstack.rs | 128 ++ packages/cli/src/builder/mod.rs | 223 +++- packages/cli/src/builder/platform.rs | 105 -- packages/cli/src/builder/prepare_html.rs | 205 +++ packages/cli/src/builder/profiles.rs | 52 - packages/cli/src/builder/progress.rs | 339 +++-- packages/cli/src/builder/request.rs | 129 -- packages/cli/src/builder/web.rs | 428 +++--- packages/cli/src/bundle_utils.rs | 165 --- packages/cli/src/bundler/android.rs | 0 packages/cli/src/bundler/app.rs | 246 ---- packages/cli/src/bundler/deb.rs | 0 packages/cli/src/bundler/format.rs | 23 - packages/cli/src/bundler/ios.rs | 0 packages/cli/src/bundler/mac.rs | 0 packages/cli/src/bundler/mod.rs | 9 - packages/cli/src/bundler/web.rs | 8 - packages/cli/src/bundler/win.rs | 0 packages/cli/src/cli/autoformat.rs | 16 +- packages/cli/src/cli/build.rs | 198 ++- packages/cli/src/cli/bundle.rs | 30 +- packages/cli/src/cli/check.rs | 8 +- packages/cli/src/cli/clean.rs | 4 +- packages/cli/src/cli/config.rs | 10 +- packages/cli/src/cli/create.rs | 6 +- packages/cli/src/cli/doctor.rs | 10 - packages/cli/src/cli/httpserver.rs | 12 - packages/cli/src/cli/init.rs | 6 +- packages/cli/src/cli/link.rs | 70 +- packages/cli/src/cli/mod.rs | 75 +- packages/cli/src/cli/run.rs | 60 - packages/cli/src/cli/serve.rs | 117 +- packages/cli/src/cli/translate.rs | 14 +- packages/cli/src/config.rs | 18 - packages/cli/src/config/app.rs | 37 - packages/cli/src/config/bundle.rs | 105 -- packages/cli/src/config/desktop.rs | 17 - packages/cli/src/config/dioxus_config.rs | 62 - packages/cli/src/config/platform.rs | 0 packages/cli/src/config/serve.rs | 41 - packages/cli/src/config/web.rs | 180 --- packages/cli/src/dioxus_crate.rs | 338 +++-- .../src/{build_info.rs => dx_build_info.rs} | 0 packages/cli/src/error.rs | 4 +- packages/cli/src/fastfs.rs | 126 -- packages/cli/src/main.rs | 114 +- packages/cli/src/metadata.rs | 4 +- packages/cli/src/serve/builder.rs | 188 +++ packages/cli/src/serve/console_widget.rs | 2 - packages/cli/src/serve/detect.rs | 32 - packages/cli/src/serve/handle.rs | 189 --- .../cli/src/serve/hot_reloading_file_map.rs | 38 +- packages/cli/src/serve/mod.rs | 403 +++--- packages/cli/src/serve/output.rs | 461 ++++--- packages/cli/src/serve/output/render.rs | 127 +- packages/cli/src/serve/proxy.rs | 4 +- packages/cli/src/serve/runner.rs | 76 -- packages/cli/src/serve/server.rs | 385 +++--- packages/cli/src/serve/tracer.rs | 104 -- packages/cli/src/serve/update.rs | 49 - packages/cli/src/serve/watcher.rs | 217 +-- packages/cli/src/settings.rs | 22 +- packages/cli/src/tooling/android.rs | 0 packages/cli/src/tooling/ios.rs | 1 - packages/cli/src/tooling/mac.rs | 0 packages/cli/src/tooling/mod.rs | 5 - packages/cli/src/tooling/web.rs | 63 - packages/cli/src/tooling/wsl.rs | 0 packages/cli/src/tracer.rs | 4 +- packages/manganis-core/Cargo.toml | 8 - packages/manganis-core/src/asset.rs | 119 -- packages/manganis-core/src/folder.rs | 0 packages/manganis-core/src/lib.rs | 8 - packages/manganis-core/src/linker.rs | 69 - packages/manganis-core/src/options.rs | 0 packages/manganis-macro/Cargo.toml | 24 - packages/manganis-macro/README.md | 3 - packages/manganis-macro/src/asset.rs | 139 -- packages/manganis-macro/src/asset_options.rs | 77 -- packages/manganis-macro/src/font.rs | 173 --- packages/manganis-macro/src/image.rs | 305 ----- packages/manganis-macro/src/lib.rs | 104 -- packages/manganis-macro/src/linker.rs | 100 -- packages/manganis/Cargo.toml | 41 - packages/manganis/src/asset.rs | 132 -- packages/manganis/src/builder.rs | 111 -- packages/manganis/src/common/asset.rs | 47 - packages/manganis/src/common/asset/error.rs | 62 - packages/manganis/src/common/asset/file.rs | 169 --- packages/manganis/src/common/asset/folder.rs | 114 -- packages/manganis/src/common/asset/meta.rs | 38 - .../manganis/src/common/asset/resource.rs | 535 -------- .../manganis/src/common/asset/tailwind.rs | 31 - packages/manganis/src/common/built.rs | 2 - packages/manganis/src/common/config.rs | 41 - packages/manganis/src/common/file.rs | 574 -------- packages/manganis/src/common/lib.rs | 12 - packages/manganis/src/common/linker.rs | 5 - packages/manganis/src/csss.rs | 65 - packages/manganis/src/files.rs | 16 - packages/manganis/src/folder.rs | 38 - packages/manganis/src/fonts.rs | 75 -- packages/manganis/src/images.rs | 197 --- packages/manganis/src/jsons.rs | 36 - packages/manganis/src/jss.rs | 48 - packages/manganis/src/lib.rs | 14 - packages/manganis/src/new.rs | 141 -- packages/manganis/src/video.rs | 16 - 112 files changed, 2824 insertions(+), 8817 deletions(-) delete mode 100644 packages/cli/src/builder/builder.rs create mode 100644 packages/cli/src/builder/fullstack.rs delete mode 100644 packages/cli/src/builder/platform.rs create mode 100644 packages/cli/src/builder/prepare_html.rs delete mode 100644 packages/cli/src/builder/profiles.rs delete mode 100644 packages/cli/src/builder/request.rs delete mode 100644 packages/cli/src/bundle_utils.rs delete mode 100644 packages/cli/src/bundler/android.rs delete mode 100644 packages/cli/src/bundler/app.rs delete mode 100644 packages/cli/src/bundler/deb.rs delete mode 100644 packages/cli/src/bundler/format.rs delete mode 100644 packages/cli/src/bundler/ios.rs delete mode 100644 packages/cli/src/bundler/mac.rs delete mode 100644 packages/cli/src/bundler/mod.rs delete mode 100644 packages/cli/src/bundler/web.rs delete mode 100644 packages/cli/src/bundler/win.rs delete mode 100644 packages/cli/src/cli/doctor.rs delete mode 100644 packages/cli/src/cli/httpserver.rs delete mode 100644 packages/cli/src/cli/run.rs delete mode 100644 packages/cli/src/config.rs delete mode 100644 packages/cli/src/config/app.rs delete mode 100644 packages/cli/src/config/bundle.rs delete mode 100644 packages/cli/src/config/desktop.rs delete mode 100644 packages/cli/src/config/dioxus_config.rs delete mode 100644 packages/cli/src/config/platform.rs delete mode 100644 packages/cli/src/config/serve.rs delete mode 100644 packages/cli/src/config/web.rs rename packages/cli/src/{build_info.rs => dx_build_info.rs} (100%) delete mode 100644 packages/cli/src/fastfs.rs delete mode 100644 packages/cli/src/serve/console_widget.rs delete mode 100644 packages/cli/src/serve/detect.rs delete mode 100644 packages/cli/src/serve/handle.rs delete mode 100644 packages/cli/src/serve/runner.rs delete mode 100644 packages/cli/src/serve/tracer.rs delete mode 100644 packages/cli/src/serve/update.rs delete mode 100644 packages/cli/src/tooling/android.rs delete mode 100644 packages/cli/src/tooling/ios.rs delete mode 100644 packages/cli/src/tooling/mac.rs delete mode 100644 packages/cli/src/tooling/mod.rs delete mode 100644 packages/cli/src/tooling/web.rs delete mode 100644 packages/cli/src/tooling/wsl.rs delete mode 100644 packages/manganis-core/Cargo.toml delete mode 100644 packages/manganis-core/src/asset.rs delete mode 100644 packages/manganis-core/src/folder.rs delete mode 100644 packages/manganis-core/src/lib.rs delete mode 100644 packages/manganis-core/src/linker.rs delete mode 100644 packages/manganis-core/src/options.rs delete mode 100644 packages/manganis-macro/Cargo.toml delete mode 100644 packages/manganis-macro/README.md delete mode 100644 packages/manganis-macro/src/asset.rs delete mode 100644 packages/manganis-macro/src/asset_options.rs delete mode 100644 packages/manganis-macro/src/font.rs delete mode 100644 packages/manganis-macro/src/image.rs delete mode 100644 packages/manganis-macro/src/lib.rs delete mode 100644 packages/manganis-macro/src/linker.rs delete mode 100644 packages/manganis/Cargo.toml delete mode 100644 packages/manganis/src/asset.rs delete mode 100644 packages/manganis/src/builder.rs delete mode 100644 packages/manganis/src/common/asset.rs delete mode 100644 packages/manganis/src/common/asset/error.rs delete mode 100644 packages/manganis/src/common/asset/file.rs delete mode 100644 packages/manganis/src/common/asset/folder.rs delete mode 100644 packages/manganis/src/common/asset/meta.rs delete mode 100644 packages/manganis/src/common/asset/resource.rs delete mode 100644 packages/manganis/src/common/asset/tailwind.rs delete mode 100644 packages/manganis/src/common/built.rs delete mode 100644 packages/manganis/src/common/config.rs delete mode 100644 packages/manganis/src/common/file.rs delete mode 100644 packages/manganis/src/common/lib.rs delete mode 100644 packages/manganis/src/common/linker.rs delete mode 100644 packages/manganis/src/csss.rs delete mode 100644 packages/manganis/src/files.rs delete mode 100644 packages/manganis/src/folder.rs delete mode 100644 packages/manganis/src/fonts.rs delete mode 100644 packages/manganis/src/images.rs delete mode 100644 packages/manganis/src/jsons.rs delete mode 100644 packages/manganis/src/jss.rs delete mode 100644 packages/manganis/src/lib.rs delete mode 100644 packages/manganis/src/new.rs delete mode 100644 packages/manganis/src/video.rs diff --git a/packages/cli/Cargo.toml b/packages/cli/Cargo.toml index 07cd373547..57c172d42c 100644 --- a/packages/cli/Cargo.toml +++ b/packages/cli/Cargo.toml @@ -11,15 +11,18 @@ rust-version = "1.79.0" [dependencies] # cli core -clap = { workspace = true, features = ["derive", "cargo"] } +clap = { version = "4.2", features = ["derive", "cargo"] } thiserror = { workspace = true } wasm-bindgen-cli-support = "0.2" wasm-bindgen-shared = "0.2" +colored = "2.0.0" +dioxus-cli-config = { workspace = true, features = ["cli"], default-features = false } # features -uuid = { version = "1.3.0", features = ["v4"] } -serde = { workspace = true, features = ["derive"] } -serde_json = { workspace = true } +log = "0.4.14" +fern = { version = "0.6.0", features = ["colored"] } +serde = { version = "1.0.136", features = ["derive"] } +serde_json = "1.0.79" toml = { workspace = true } fs_extra = "1.2.0" cargo_toml = { workspace = true } @@ -27,10 +30,10 @@ futures-util = { workspace = true, features = ["async-await-macro"] } notify = { workspace = true, features = ["serde"] } html_parser = { workspace = true } cargo_metadata = "0.18.1" -tokio = { workspace = true, features = ["fs", "sync", "rt", "macros", "process", "rt-multi-thread"] } +tokio = { version = "1.16.1", features = ["fs", "sync", "rt", "macros", "process", "rt-multi-thread"] } tokio-stream = "0.1.15" atty = "0.2.14" -chrono = { workspace = true } +chrono = "0.4.19" anyhow = "1" hyper = { workspace = true } hyper-util = "0.1.3" @@ -42,7 +45,6 @@ console = "0.15.8" ctrlc = "3.2.3" futures-channel = { workspace = true } krates = { version = "0.17.0" } -cargo-config2 = { workspace = true, optional = true } regex = "1.10.6" axum = { workspace = true, features = ["ws"] } @@ -52,7 +54,7 @@ tower-http = { workspace = true, features = ["full"] } proc-macro2 = { workspace = true, features = ["span-locations"] } syn = { workspace = true, features = ["full", "extra-traits", "visit", "visit-mut"] } -headers = "0.4.0" +headers = "0.3.7" walkdir = "2" # tools download @@ -82,6 +84,7 @@ tauri-bundler = { workspace = true } prettyplease = { workspace = true } # Assets +manganis-cli-support = { workspace = true, features = ["html"] } brotli = "6.0.0" dioxus-autofmt = { workspace = true } @@ -92,11 +95,9 @@ dioxus-rsx-hotreload = { workspace = true } dioxus-html = { workspace = true, features = ["hot-reload-context"] } dioxus-core = { workspace = true, features = ["serialize"] } dioxus-core-types = { workspace = true } -dioxus-devtools-types = { workspace = true } -dioxus-runtime-config = { workspace = true } -dioxus-fullstack = { workspace = true } +dioxus-hot-reload = { workspace = true, features = ["serve"] } ignore = "0.4.22" -env_logger = { workspace = true } +env_logger = "0.11.3" tracing-subscriber = { version = "0.3.18", features = ["std", "env-filter"] } console-subscriber = { version = "0.3.0", optional = true } @@ -106,58 +107,10 @@ ratatui = { version = "0.27.0", features = ["crossterm", "unstable"] } crossterm = { version = "0.27.0", features = ["event-stream"] } ansi-to-tui = "=5.0.0-rc.1" ansi-to-html = "0.2.1" -manganis-core = { workspace = true } -# link intercept -tempfile = "3.3" - - -# # just use the manganis crate directly since it has all the types we need -# manganis = { workspace = true } -# serde = { version = "1.0.183", features = ["derive"] } -# serde_json = {version="1.0.116"} -# anyhow = "1" -# rayon = "1.7.0" -# rustc-hash = "1.1.0" - -# # Tailwind -# railwind = "0.1.5" - -# # Image compression/conversion -# # JPEG -# mozjpeg = { version = "0.10.7", default-features = false, features = ["parallel"] } -# # PNG -# imagequant = "4.2.0" -# png = "0.17.9" -# # Conversion -# image = { version = "0.25" } -# ravif = { version = "0.11", default-features = false } - -# # CSS Minification -# lightningcss = "1.0.0-alpha.44" - -# # # Js minification -# # swc = "=0.283.0" -# # swc_common = "=0.37.1" - - -# Extracting data from an executable -object = {version="0.36.0", features=["wasm"]} -tokio-util = { version = "0.7.11", features = ["full"] } - -# [dev-dependencies] -# tracing-subscriber = "0.3.18" - -# [features] -# default = ["html"] -# html = [] -# # html = ["manganis-common/html"] - -# asm = ["ravif/asm", "mozjpeg/nasm_simd"] - -# # Note: this feature now enables nothing and should be removed in the next major version -# webp = [] -# avif = [] +# on macos, we need to specify the vendored feature on ssl when cross compiling +# [target.'cfg(target_os = "macos")'.dependencies] +# openssl = { version = "0.10", features = ["vendored"] } [build-dependencies] built = { version = "=0.7.4", features = ["git2"] } @@ -168,20 +121,23 @@ plugin = [] tokio-console = ["dep:console-subscriber"] # when releasing dioxus, we want to enable wasm-opt -# and then also maybe developing it too. -# making this optional cuts workspace deps down from 1000 to 500, so it's very nice for workspace adev -optimization = ["wasm-opt", "asset-opt"] -asset-opt = [] wasm-opt = ["dep:wasm-opt"] [[bin]] path = "src/main.rs" name = "dx" - +[dev-dependencies] +tempfile = "3.3" [package.metadata.binstall] +# temporarily, we're going to use the 0.5.0 download page for all binaries pkg-url = "{ repo }/releases/download/v{ version }/dx-{ target }-v{ version }{ archive-suffix }" + +# the old one... +# pkg-url = "{ repo }/releases/download/v0.5.0/dx-{ target }-v{ version }{ archive-suffix }" + +# pkg-url = "{ repo }/releases/download/v{ version }/dx-{ target }{ archive-suffix }" pkg-fmt = "tgz" [package.metadata.binstall.overrides.x86_64-pc-windows-msvc] diff --git a/packages/cli/src/assets.rs b/packages/cli/src/assets.rs index ae7853c109..08b8e55c3a 100644 --- a/packages/cli/src/assets.rs +++ b/packages/cli/src/assets.rs @@ -1,1031 +1,209 @@ +use crate::builder::{BuildRequest, Stage, UpdateBuildProgress, UpdateStage}; use crate::Result; use crate::TraceSrc; use anyhow::Context; use brotli::enc::BrotliEncoderParams; -use manganis_core::{LinkSection, ResourceAsset}; -use object::{read::archive::ArchiveFile, File as ObjectFile, Object, ObjectSection}; -use std::ffi::OsString; +use futures_channel::mpsc::UnboundedSender; +use manganis_cli_support::{process_file, AssetManifest, AssetManifestExt, AssetType}; +use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use std::fs; use std::path::Path; use std::sync::atomic::AtomicUsize; use std::sync::Arc; -use std::{collections::HashMap, path::PathBuf}; +use std::{ffi::OsString, path::PathBuf}; use std::{fs::File, io::Write}; use walkdir::WalkDir; -// use manganis_common::{FileOptions, FolderAsset}; -// use image::{DynamicImage, EncodableLayout}; -// use lightningcss::stylesheet::{MinifyOptions, ParserOptions, PrinterOptions, StyleSheet}; -// use manganis_common::{ -// CssOptions, FileOptions, ImageOptions, ImageType, JsOptions, JsonOptions, ResourceAsset, -// }; -use crate::link::InterceptedArgs; +/// The temp file name for passing manganis json from linker to current exec. +pub const MG_JSON_OUT: &str = "mg-out"; -/// A manifest of all assets collected from dependencies -/// -/// This will be filled in primarly by incremental compilation artifacts. -#[derive(Debug, PartialEq, Default, Clone)] -pub(crate) struct AssetManifest { - pub(crate) assets: HashMap, +pub fn asset_manifest(build: &BuildRequest) -> Option { + let file_path = build.target_out_dir().join(MG_JSON_OUT); + let read = fs::read_to_string(&file_path).ok()?; + _ = fs::remove_file(file_path); + let json: Vec = serde_json::from_str(&read).unwrap(); + + Some(AssetManifest::load(json)) } -impl AssetManifest { - /// Create a new asset manifest pre-populated with the assets from the linker intercept - pub(crate) fn new_from_linker_intercept(args: InterceptedArgs) -> Self { - let mut manifest = Self::default(); - manifest.add_from_linker_intercept(args); - manifest - } +/// Create a head file that contains all of the imports for assets that the user project uses +pub fn create_assets_head(build: &BuildRequest, manifest: &AssetManifest) -> Result<()> { + let out_dir = build.target_out_dir(); + std::fs::create_dir_all(&out_dir)?; + let mut file = File::create(out_dir.join("__assets_head.html"))?; + file.write_all(manifest.head().as_bytes())?; + Ok(()) +} - /// Fill this manifest from the intercepted rustc args used to link the app together - pub(crate) fn add_from_linker_intercept(&mut self, args: InterceptedArgs) { - // Attempt to load the arg as a command file, otherwise just use the args themselves - // This is because windows will pass in `@linkerargs.txt` as a source of linker args - if let Some(command) = args.args.iter().find(|arg| arg.starts_with('@')).cloned() { - self.add_from_command_file(args, &command); - } else { - self.add_from_linker_args(args); - } - } +/// Process any assets collected from the binary +pub(crate) fn process_assets( + build: &BuildRequest, + manifest: &AssetManifest, + progress: &mut UnboundedSender, +) -> anyhow::Result<()> { + let static_asset_output_dir = build.target_out_dir(); + + std::fs::create_dir_all(&static_asset_output_dir) + .context("Failed to create static asset output directory")?; + + let assets_finished = Arc::new(AtomicUsize::new(0)); + let assets = manifest.assets(); + let asset_count = assets.len(); + assets.par_iter().try_for_each_init( + || progress.clone(), + move |progress, asset| { + if let AssetType::File(file_asset) = asset { + match process_file(file_asset, &static_asset_output_dir) { + Ok(_) => { + // Update the progress + tracing::info!(dx_src = ?TraceSrc::Build, "Optimized static asset {file_asset}"); + let assets_finished = + assets_finished.fetch_add(1, std::sync::atomic::Ordering::SeqCst); + _ = progress.start_send(UpdateBuildProgress { + stage: Stage::OptimizingAssets, + update: UpdateStage::SetProgress( + assets_finished as f64 / asset_count as f64, + ), + }); + } + Err(err) => { + tracing::error!(dx_src = ?TraceSrc::Build, "Failed to copy static asset: {}", err); + return Err(err); + } + } + } + Ok::<(), anyhow::Error>(()) + }, + )?; - /// Fill this manifest from the contents of a linker command file. - /// - /// Rustc will pass a file as link args to linkers on windows instead of args directly. - /// - /// We actually need to read that file and then pull out the args directly. - pub(crate) fn add_from_command_file(&mut self, args: InterceptedArgs, arg: &str) { - let path = arg.trim().trim_start_matches('@'); - let file_binary = std::fs::read(path).unwrap(); + Ok(()) +} - // This may be a utf-16le file. Let's try utf-8 first. - let content = match String::from_utf8(file_binary.clone()) { - Ok(s) => s, - Err(_) => { - // Convert Vec to Vec to convert into a String - let binary_u16le: Vec = file_binary - .chunks_exact(2) - .map(|a| u16::from_le_bytes([a[0], a[1]])) - .collect(); +/// A guard that sets up the environment for the web renderer to compile in. This guard sets the location that assets will be served from +pub(crate) struct AssetConfigDropGuard; - String::from_utf16_lossy(&binary_u16le) - } +impl AssetConfigDropGuard { + pub fn new(base_path: Option<&str>) -> Self { + // Set up the collect asset config + let base = match base_path { + Some(base) => format!("/{}/", base.trim_matches('/')), + None => "/".to_string(), }; - - // Gather linker args - let mut linker_args = Vec::new(); - let lines = content.lines(); - for line in lines { - // Remove quotes from the line - windows link args files are quoted - let line_parsed = line.to_string(); - let line_parsed = line_parsed.trim_end_matches('"').to_string(); - let line_parsed = line_parsed.trim_start_matches('"').to_string(); - linker_args.push(line_parsed); - } - - self.add_from_linker_args(InterceptedArgs { - args: linker_args, - ..args - }); + manganis_cli_support::Config::default() + .with_assets_serve_location(base) + .save(); + Self {} } +} - pub(crate) fn add_from_linker_args(&mut self, args: InterceptedArgs) { - // Parse through linker args for `.o` or `.rlib` files. - for item in args.args { - if item.ends_with(".o") || item.ends_with(".rlib") { - self.add_from_object_path(args.work_dir.join(PathBuf::from(item))); - } - } +impl Drop for AssetConfigDropGuard { + fn drop(&mut self) { + // Reset the config + manganis_cli_support::Config::default().save(); } +} - /// Fill this manifest with a file object/rlib files, typically extracted from the linker intercepted - pub(crate) fn add_from_object_path(&mut self, path: PathBuf) { - let Some(ext) = path.extension() else { - return; - }; - - let Some(ext) = ext.to_str() else { - return; - }; - - let data = std::fs::read(path.clone()).expect("Failed to read asset optimization file"); - - match ext { - // Parse an unarchived object file - "o" => { - let object = object::File::parse(&*data).unwrap(); - self.add_from_object_file(&object); +pub(crate) fn copy_dir_to( + src_dir: PathBuf, + dest_dir: PathBuf, + pre_compress: bool, +) -> std::io::Result<()> { + let entries = std::fs::read_dir(&src_dir)?; + let mut children: Vec>> = Vec::new(); + + for entry in entries.flatten() { + let entry_path = entry.path(); + let path_relative_to_src = entry_path.strip_prefix(&src_dir).unwrap(); + let output_file_location = dest_dir.join(path_relative_to_src); + children.push(std::thread::spawn(move || { + if entry.file_type()?.is_dir() { + // If the file is a directory, recursively copy it into the output directory + if let Err(err) = + copy_dir_to(entry_path.clone(), output_file_location, pre_compress) + { + tracing::error!( + dx_src = ?TraceSrc::Build, + "Failed to pre-compress directory {}: {}", + entry_path.display(), + err + ); + } + } else { + // Make sure the directory exists + std::fs::create_dir_all(output_file_location.parent().unwrap())?; + // Copy the file to the output directory + std::fs::copy(&entry_path, &output_file_location)?; + + // Then pre-compress the file if needed + if pre_compress { + if let Err(err) = pre_compress_file(&output_file_location) { + tracing::error!( + dx_src = ?TraceSrc::Build, + "Failed to pre-compress static assets {}: {}", + output_file_location.display(), + err + ); + } + // If pre-compression isn't enabled, we should remove the old compressed file if it exists + } else if let Some(compressed_path) = compressed_path(&output_file_location) { + _ = std::fs::remove_file(compressed_path); + } } - // Parse an rlib as a collection of objects - "rlib" => { - let archive = object::read::archive::ArchiveFile::parse(&*data).unwrap(); - self.add_from_archive_file(&archive, &data); - } - _ => {} - } + Ok(()) + })); } + for child in children { + child.join().unwrap()?; + } + Ok(()) +} - /// Fill this manifest from an rlib / ar file that contains many object files and their entryies - pub(crate) fn add_from_archive_file(&mut self, archive: &ArchiveFile, data: &[u8]) { - // Look through each archive member for object files. - // Read the archive member's binary data (we know it's an object file) - // And parse it with the normal `object::File::parse` to find the manganis string. - for member in archive.members() { - let member = member.unwrap(); - let name = String::from_utf8_lossy(member.name()).to_string(); - - // Check if the archive member is an object file and parse it. - if name.ends_with(".o") { - let data = member.data(&*data).unwrap(); - let object = object::File::parse(data).unwrap(); - self.add_from_object_file(&object); +/// Get the path to the compressed version of a file +fn compressed_path(path: &Path) -> Option { + let new_extension = match path.extension() { + Some(ext) => { + if ext.to_string_lossy().to_lowercase().ends_with("br") { + return None; } + let mut ext = ext.to_os_string(); + ext.push(".br"); + ext } - } - - /// Fill this manifest with whatever tables might come from the object file - pub(crate) fn add_from_object_file(&mut self, obj: &ObjectFile) -> Option<()> { - for section in obj.sections() { - let Ok(section_name) = section.name() else { - continue; - }; + None => OsString::from("br"), + }; + Some(path.with_extension(new_extension)) +} - // Check if the link section matches the asset section for one of the platforms we support. This may not be the current platform if the user is cross compiling - let matches = LinkSection::ALL - .iter() - .any(|x| x.link_section == section_name); +/// pre-compress a file with brotli +pub(crate) fn pre_compress_file(path: &Path) -> std::io::Result<()> { + let Some(compressed_path) = compressed_path(path) else { + return Ok(()); + }; + let file = std::fs::File::open(path)?; + let mut stream = std::io::BufReader::new(file); + let mut buffer = std::fs::File::create(compressed_path)?; + let params = BrotliEncoderParams::default(); + brotli::BrotliCompress(&mut stream, &mut buffer, ¶ms)?; + Ok(()) +} - if !matches { - continue; +/// pre-compress all files in a folder +pub(crate) fn pre_compress_folder(path: &Path, pre_compress: bool) -> std::io::Result<()> { + let walk_dir = WalkDir::new(path); + for entry in walk_dir.into_iter().filter_map(|e| e.ok()) { + let entry_path = entry.path(); + if entry_path.is_file() { + if pre_compress { + if let Err(err) = pre_compress_file(entry_path) { + tracing::error!(dx_src = ?TraceSrc::Build, "Failed to pre-compress file {entry_path:?}: {err}"); + } } - - let bytes = section.uncompressed_data().ok()?; - - let as_str = std::str::from_utf8(&bytes) - .ok()? - .chars() - .filter(|c| !c.is_control()) - .collect::(); - - let stream = serde_json::Deserializer::from_str(&as_str).into_iter::(); - - for as_resource in stream { - let as_resource = as_resource.unwrap(); - - // Some platforms (e.g. macOS) start the manganis section with a null byte, we need to filter that out before we deserialize the JSON - self.assets - .insert(as_resource.absolute.clone(), as_resource); + // If pre-compression isn't enabled, we should remove the old compressed file if it exists + else if let Some(compressed_path) = compressed_path(entry_path) { + _ = std::fs::remove_file(compressed_path); } } - - None - } - - /// Copy the assest from this manifest to a target folder - /// - /// If `optimize` is enabled, then we will run the optimizer for this asset. - /// - /// The output file is guaranteed to be the destination + the ResourceAsset bundle name - /// - /// Will not actually copy the asset if the source asset hasn't changed? - pub(crate) fn copy_asset_to( - &self, - destination: &Path, - target_asset: &Path, - optimize: bool, - _pre_compress: bool, - ) -> anyhow::Result<()> { - let Some(src) = self.assets.get(target_asset) else { - crate::fastfs::copy_asset( - target_asset, - &destination.join(target_asset.file_name().unwrap()), - )?; - - return Ok(()); - }; - - let local = src.absolute.clone(); - - if !local.exists() { - panic!("Specified asset does not exist while trying to copy {target_asset:?} to {destination:?}") - } - - // If there's no optimizaton while copying this asset, we simply std::fs::copy and call it a day - if !optimize { - std::fs::copy(local, destination.join(&src.bundled)).expect("Failed to copy asset"); - return Ok(()); - } - - // Otherwise, let's attempt to optimize the the asset we're copying - - Ok(()) } + Ok(()) } - -// use swc::{config::JsMinifyOptions, try_with_handler, BoolOrDataConfig}; -// use swc_common::{sync::Lrc, FileName}; -// use swc_common::{SourceMap, GLOBALS}; - -// pub(crate) trait Process { -// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()>; -// } - -// /// Process a specific file asset -// pub(crate) fn process_file(file: &ResourceAsset, output_folder: &Path) -> anyhow::Result<()> { -// todo!() -// // let location = file.location(); -// // let source = location.source(); -// // let output_path = output_folder.join(location.unique_name()); -// // file.options().process(source, &output_path) -// } - -// impl Process for FileOptions { -// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { -// if output_path.exists() { -// return Ok(()); -// } -// match self { -// Self::Other { .. } => { -// let bytes = source.read_to_bytes()?; -// std::fs::write(output_path, bytes).with_context(|| { -// format!( -// "Failed to write file to output location: {}", -// output_path.display() -// ) -// })?; -// } -// Self::Css(options) => { -// options.process(source, output_path)?; -// } -// Self::Js(options) => { -// options.process(source, output_path)?; -// } -// Self::Json(options) => { -// options.process(source, output_path)?; -// } -// Self::Image(options) => { -// options.process(source, output_path)?; -// } -// _ => todo!(), -// } - -// Ok(()) -// } -// } - -// impl Process for ImageOptions { -// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { -// let mut image = image::ImageReader::new(std::io::Cursor::new(&*source.read_to_bytes()?)) -// .with_guessed_format()? -// .decode()?; - -// if let Some(size) = self.size() { -// image = image.resize_exact(size.0, size.1, image::imageops::FilterType::Lanczos3); -// } - -// match self.ty() { -// ImageType::Png => { -// compress_png(image, output_path); -// } -// ImageType::Jpg => { -// compress_jpg(image, output_path)?; -// } -// ImageType::Avif => { -// if let Err(error) = image.save(output_path) { -// tracing::error!("Failed to save avif image: {} with path {}. You must have the avif feature enabled to use avif assets", error, output_path.display()); -// } -// } -// ImageType::Webp => { -// if let Err(err) = image.save(output_path) { -// tracing::error!("Failed to save webp image: {}. You must have the avif feature enabled to use webp assets", err); -// } -// } -// } - -// Ok(()) -// } -// } - -// fn compress_jpg(image: DynamicImage, output_location: &Path) -> anyhow::Result<()> { -// let mut comp = mozjpeg::Compress::new(mozjpeg::ColorSpace::JCS_EXT_RGBX); -// let width = image.width() as usize; -// let height = image.height() as usize; - -// comp.set_size(width, height); -// let mut comp = comp.start_compress(Vec::new())?; // any io::Write will work - -// comp.write_scanlines(image.to_rgba8().as_bytes())?; - -// let jpeg_bytes = comp.finish()?; - -// let file = std::fs::File::create(output_location)?; -// let w = &mut BufWriter::new(file); -// w.write_all(&jpeg_bytes)?; -// Ok(()) -// } - -// fn compress_png(image: DynamicImage, output_location: &Path) { -// // Image loading/saving is outside scope of this library -// let width = image.width() as usize; -// let height = image.height() as usize; -// let bitmap: Vec<_> = image -// .into_rgba8() -// .pixels() -// .map(|px| imagequant::RGBA::new(px[0], px[1], px[2], px[3])) -// .collect(); - -// // Configure the library -// let mut liq = imagequant::new(); -// liq.set_speed(5).unwrap(); -// liq.set_quality(0, 99).unwrap(); - -// // Describe the bitmap -// let mut img = liq.new_image(&bitmap[..], width, height, 0.0).unwrap(); - -// // The magic happens in quantize() -// let mut res = match liq.quantize(&mut img) { -// Ok(res) => res, -// Err(err) => panic!("Quantization failed, because: {err:?}"), -// }; - -// let (palette, pixels) = res.remapped(&mut img).unwrap(); - -// let file = std::fs::File::create(output_location).unwrap(); -// let w = &mut BufWriter::new(file); - -// let mut encoder = png::Encoder::new(w, width as u32, height as u32); -// encoder.set_color(png::ColorType::Rgba); -// let mut flattened_palette = Vec::new(); -// let mut alpha_palette = Vec::new(); -// for px in palette { -// flattened_palette.push(px.r); -// flattened_palette.push(px.g); -// flattened_palette.push(px.b); -// alpha_palette.push(px.a); -// } -// encoder.set_palette(flattened_palette); -// encoder.set_trns(alpha_palette); -// encoder.set_depth(png::BitDepth::Eight); -// encoder.set_color(png::ColorType::Indexed); -// encoder.set_compression(png::Compression::Best); -// let mut writer = encoder.write_header().unwrap(); -// writer.write_image_data(&pixels).unwrap(); -// writer.finish().unwrap(); -// } - -// impl Process for CssOptions { -// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { -// let css = source.read_to_string()?; - -// let css = if self.minify() { minify_css(&css) } else { css }; - -// std::fs::write(output_path, css).with_context(|| { -// format!( -// "Failed to write css to output location: {}", -// output_path.display() -// ) -// })?; - -// Ok(()) -// } -// } - -// pub(crate) fn minify_css(css: &str) -> String { -// let mut stylesheet = StyleSheet::parse(css, ParserOptions::default()).unwrap(); -// stylesheet.minify(MinifyOptions::default()).unwrap(); -// let printer = PrinterOptions { -// minify: true, -// ..Default::default() -// }; -// let res = stylesheet.to_css(printer).unwrap(); -// res.code -// } - -// pub(crate) fn minify_js(source: &ResourceAsset) -> anyhow::Result { -// todo!("disabled swc due to semver issues") -// // let cm = Arc::::default(); - -// // let js = source.read_to_string()?; -// // let c = swc::Compiler::new(cm.clone()); -// // let output = GLOBALS -// // .set(&Default::default(), || { -// // try_with_handler(cm.clone(), Default::default(), |handler| { -// // // let filename = Lrc::new(match source { -// // // manganis_common::ResourceAsset::Local(path) => { -// // // FileName::Real(path.canonicalized.clone()) -// // // } -// // // manganis_common::ResourceAsset::Remote(url) => FileName::Url(url.clone()), -// // // }); -// // let filename = todo!(); -// // let fm = cm.new_source_file(filename, js.to_string()); - -// // c.minify( -// // fm, -// // handler, -// // &JsMinifyOptions { -// // compress: BoolOrDataConfig::from_bool(true), -// // mangle: BoolOrDataConfig::from_bool(true), -// // ..Default::default() -// // }, -// // ) -// // .context("failed to minify javascript") -// // }) -// // }) -// // .map(|output| output.code); - -// // match output { -// // Ok(output) => Ok(output), -// // Err(err) => { -// // tracing::error!("Failed to minify javascript: {}", err); -// // Ok(js) -// // } -// // } -// } - -// impl Process for JsOptions { -// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { -// let js = if self.minify() { -// minify_js(source)? -// } else { -// source.read_to_string()? -// }; - -// std::fs::write(output_path, js).with_context(|| { -// format!( -// "Failed to write js to output location: {}", -// output_path.display() -// ) -// })?; - -// Ok(()) -// } -// } - -// pub(crate) fn minify_json(source: &str) -> anyhow::Result { -// // First try to parse the json -// let json: serde_json::Value = serde_json::from_str(source)?; -// // Then print it in a minified format -// let json = serde_json::to_string(&json)?; -// Ok(json) -// } - -// impl Process for JsonOptions { -// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { -// let source = source.read_to_string()?; -// let json = match minify_json(&source) { -// Ok(json) => json, -// Err(err) => { -// tracing::error!("Failed to minify json: {}", err); -// source -// } -// }; - -// std::fs::write(output_path, json).with_context(|| { -// format!( -// "Failed to write json to output location: {}", -// output_path.display() -// ) -// })?; - -// Ok(()) -// } -// } - -// /// Process a folder, optimizing and copying all assets into the output folder -// pub(crate) fn process_folder(folder: &FolderAsset, output_folder: &Path) -> anyhow::Result<()> { -// // Push the unique name of the folder to the output folder -// let output_folder = output_folder.join(folder.unique_name()); - -// if output_folder.exists() { -// return Ok(()); -// } - -// // .location() -// // // .source() -// // .as_path() -// let folder = folder.path(); - -// // Optimize and copy all assets in the folder in parallel -// process_folder_inner(folder, &output_folder) -// } - -// fn process_folder_inner(folder: &Path, output_folder: &Path) -> anyhow::Result<()> { -// // Create the folder -// std::fs::create_dir_all(output_folder)?; - -// // Then optimize children -// let files: Vec<_> = std::fs::read_dir(folder) -// .into_iter() -// .flatten() -// .flatten() -// .collect(); - -// files.par_iter().try_for_each(|file| { -// let file = file.path(); -// let metadata = file.metadata()?; -// let output_path = output_folder.join(file.strip_prefix(folder)?); -// if metadata.is_dir() { -// process_folder_inner(&file, &output_path) -// } else { -// process_file_minimal(&file, &output_path) -// } -// })?; - -// Ok(()) -// } - -// /// Optimize a file without changing any of its contents significantly (e.g. by changing the extension) -// fn process_file_minimal(input_path: &Path, output_path: &Path) -> anyhow::Result<()> { -// todo!() -// // let options = -// // FileOptions::default_for_extension(input_path.extension().and_then(|e| e.to_str())); -// // let source = input_path.to_path_buf(); -// // options.process(&source, output_path)?; -// // Ok(()) -// } - -// use image::{DynamicImage, EncodableLayout}; -// use lightningcss::stylesheet::{MinifyOptions, ParserOptions, PrinterOptions, StyleSheet}; -// use manganis_common::{ -// CssOptions, FileOptions, ImageOptions, ImageType, JsOptions, JsonOptions, ResourceAsset, -// }; - -// use swc::{config::JsMinifyOptions, try_with_handler, BoolOrDataConfig}; -// use swc_common::{sync::Lrc, FileName}; -// use swc_common::{SourceMap, GLOBALS}; - -// pub(crate) trait Process { -// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()>; -// } - -// /// Process a specific file asset -// pub(crate) fn process_file(file: &ResourceAsset, output_folder: &Path) -> anyhow::Result<()> { -// todo!() -// // let location = file.location(); -// // let source = location.source(); -// // let output_path = output_folder.join(location.unique_name()); -// // file.options().process(source, &output_path) -// } - -// impl Process for FileOptions { -// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { -// if output_path.exists() { -// return Ok(()); -// } -// match self { -// Self::Other { .. } => { -// let bytes = source.read_to_bytes()?; -// std::fs::write(output_path, bytes).with_context(|| { -// format!( -// "Failed to write file to output location: {}", -// output_path.display() -// ) -// })?; -// } -// Self::Css(options) => { -// options.process(source, output_path)?; -// } -// Self::Js(options) => { -// options.process(source, output_path)?; -// } -// Self::Json(options) => { -// options.process(source, output_path)?; -// } -// Self::Image(options) => { -// options.process(source, output_path)?; -// } -// _ => todo!(), -// } - -// Ok(()) -// } -// } - -// impl Process for ImageOptions { -// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { -// let mut image = image::ImageReader::new(std::io::Cursor::new(&*source.read_to_bytes()?)) -// .with_guessed_format()? -// .decode()?; - -// if let Some(size) = self.size() { -// image = image.resize_exact(size.0, size.1, image::imageops::FilterType::Lanczos3); -// } - -// match self.ty() { -// ImageType::Png => { -// compress_png(image, output_path); -// } -// ImageType::Jpg => { -// compress_jpg(image, output_path)?; -// } -// ImageType::Avif => { -// if let Err(error) = image.save(output_path) { -// tracing::error!("Failed to save avif image: {} with path {}. You must have the avif feature enabled to use avif assets", error, output_path.display()); -// } -// } -// ImageType::Webp => { -// if let Err(err) = image.save(output_path) { -// tracing::error!("Failed to save webp image: {}. You must have the avif feature enabled to use webp assets", err); -// } -// } -// } - -// Ok(()) -// } -// } - -// fn compress_jpg(image: DynamicImage, output_location: &Path) -> anyhow::Result<()> { -// let mut comp = mozjpeg::Compress::new(mozjpeg::ColorSpace::JCS_EXT_RGBX); -// let width = image.width() as usize; -// let height = image.height() as usize; - -// comp.set_size(width, height); -// let mut comp = comp.start_compress(Vec::new())?; // any io::Write will work - -// comp.write_scanlines(image.to_rgba8().as_bytes())?; - -// let jpeg_bytes = comp.finish()?; - -// let file = std::fs::File::create(output_location)?; -// let w = &mut BufWriter::new(file); -// w.write_all(&jpeg_bytes)?; -// Ok(()) -// } - -// fn compress_png(image: DynamicImage, output_location: &Path) { -// // Image loading/saving is outside scope of this library -// let width = image.width() as usize; -// let height = image.height() as usize; -// let bitmap: Vec<_> = image -// .into_rgba8() -// .pixels() -// .map(|px| imagequant::RGBA::new(px[0], px[1], px[2], px[3])) -// .collect(); - -// // Configure the library -// let mut liq = imagequant::new(); -// liq.set_speed(5).unwrap(); -// liq.set_quality(0, 99).unwrap(); - -// // Describe the bitmap -// let mut img = liq.new_image(&bitmap[..], width, height, 0.0).unwrap(); - -// // The magic happens in quantize() -// let mut res = match liq.quantize(&mut img) { -// Ok(res) => res, -// Err(err) => panic!("Quantization failed, because: {err:?}"), -// }; - -// let (palette, pixels) = res.remapped(&mut img).unwrap(); - -// let file = std::fs::File::create(output_location).unwrap(); -// let w = &mut BufWriter::new(file); - -// let mut encoder = png::Encoder::new(w, width as u32, height as u32); -// encoder.set_color(png::ColorType::Rgba); -// let mut flattened_palette = Vec::new(); -// let mut alpha_palette = Vec::new(); -// for px in palette { -// flattened_palette.push(px.r); -// flattened_palette.push(px.g); -// flattened_palette.push(px.b); -// alpha_palette.push(px.a); -// } -// encoder.set_palette(flattened_palette); -// encoder.set_trns(alpha_palette); -// encoder.set_depth(png::BitDepth::Eight); -// encoder.set_color(png::ColorType::Indexed); -// encoder.set_compression(png::Compression::Best); -// let mut writer = encoder.write_header().unwrap(); -// writer.write_image_data(&pixels).unwrap(); -// writer.finish().unwrap(); -// } - -// impl Process for CssOptions { -// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { -// let css = source.read_to_string()?; - -// let css = if self.minify() { minify_css(&css) } else { css }; - -// std::fs::write(output_path, css).with_context(|| { -// format!( -// "Failed to write css to output location: {}", -// output_path.display() -// ) -// })?; - -// Ok(()) -// } -// } - -// pub(crate) fn minify_css(css: &str) -> String { -// let mut stylesheet = StyleSheet::parse(css, ParserOptions::default()).unwrap(); -// stylesheet.minify(MinifyOptions::default()).unwrap(); -// let printer = PrinterOptions { -// minify: true, -// ..Default::default() -// }; -// let res = stylesheet.to_css(printer).unwrap(); -// res.code -// } - -// pub(crate) fn minify_js(source: &ResourceAsset) -> anyhow::Result { -// todo!("disabled swc due to semver issues") -// // let cm = Arc::::default(); - -// // let js = source.read_to_string()?; -// // let c = swc::Compiler::new(cm.clone()); -// // let output = GLOBALS -// // .set(&Default::default(), || { -// // try_with_handler(cm.clone(), Default::default(), |handler| { -// // // let filename = Lrc::new(match source { -// // // manganis_common::ResourceAsset::Local(path) => { -// // // FileName::Real(path.canonicalized.clone()) -// // // } -// // // manganis_common::ResourceAsset::Remote(url) => FileName::Url(url.clone()), -// // // }); -// // let filename = todo!(); -// // let fm = cm.new_source_file(filename, js.to_string()); - -// // c.minify( -// // fm, -// // handler, -// // &JsMinifyOptions { -// // compress: BoolOrDataConfig::from_bool(true), -// // mangle: BoolOrDataConfig::from_bool(true), -// // ..Default::default() -// // }, -// // ) -// // .context("failed to minify javascript") -// // }) -// // }) -// // .map(|output| output.code); - -// // match output { -// // Ok(output) => Ok(output), -// // Err(err) => { -// // tracing::error!("Failed to minify javascript: {}", err); -// // Ok(js) -// // } -// // } -// } - -// impl Process for JsOptions { -// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { -// let js = if self.minify() { -// minify_js(source)? -// } else { -// source.read_to_string()? -// }; - -// std::fs::write(output_path, js).with_context(|| { -// format!( -// "Failed to write js to output location: {}", -// output_path.display() -// ) -// })?; - -// Ok(()) -// } -// } - -// pub(crate) fn minify_json(source: &str) -> anyhow::Result { -// // First try to parse the json -// let json: serde_json::Value = serde_json::from_str(source)?; -// // Then print it in a minified format -// let json = serde_json::to_string(&json)?; -// Ok(json) -// } - -// impl Process for JsonOptions { -// fn process(&self, source: &ResourceAsset, output_path: &Path) -> anyhow::Result<()> { -// let source = source.read_to_string()?; -// let json = match minify_json(&source) { -// Ok(json) => json, -// Err(err) => { -// tracing::error!("Failed to minify json: {}", err); -// source -// } -// }; - -// std::fs::write(output_path, json).with_context(|| { -// format!( -// "Failed to write json to output location: {}", -// output_path.display() -// ) -// })?; - -// Ok(()) -// } -// } - -// /// Returns the HTML that should be injected into the head of the page -// pub(crate) fn head(&self) -> String { -// let mut head = String::new(); -// for asset in &self.assets { -// if let crate::AssetType::Resource(file) = asset { -// match file.options() { -// crate::FileOptions::Css(css_options) => { -// if css_options.preload() { -// if let Ok(asset_path) = file.served_location() { -// head.push_str(&format!( -// "\n" -// )) -// } -// } -// } -// crate::FileOptions::Image(image_options) => { -// if image_options.preload() { -// if let Ok(asset_path) = file.served_location() { -// head.push_str(&format!( -// "\n" -// )) -// } -// } -// } -// crate::FileOptions::Js(js_options) => { -// if js_options.preload() { -// if let Ok(asset_path) = file.served_location() { -// head.push_str(&format!( -// "\n" -// )) -// } -// } -// } -// _ => {} -// } -// } -// } -// head -// } - -// /// An extension trait CLI support for the asset manifest -// pub(crate) trait AssetManifestExt { -// /// Load a manifest from a list of Manganis JSON strings. -// /// -// /// The asset descriptions are stored inside a manifest file that is produced when the linker is intercepted. -// fn load(json: Vec) -> Self; -// /// Load a manifest from the assets propogated through object files. -// /// -// /// The asset descriptions are stored inside a manifest file that is produced when the linker is intercepted. -// fn load_from_objects(object_paths: Vec) -> Self; -// /// Optimize and copy all assets in the manifest to a folder -// fn copy_static_assets_to(&self, location: impl Into) -> anyhow::Result<()>; -// /// Collect all tailwind classes and generate string with the output css -// fn collect_tailwind_css( -// &self, -// include_preflight: bool, -// warnings: &mut Vec, -// ) -> String; -// } - -// impl AssetManifestExt for AssetManifest { -// fn load(json: Vec) -> Self { -// let mut all_assets = Vec::new(); - -// // Collect all assets for each manganis string found. -// for item in json { -// let mut assets = deserialize_assets(item.as_str()); -// all_assets.append(&mut assets); -// } - -// // If we don't see any manganis assets used in the binary, just return an empty manifest -// if all_assets.is_empty() { -// return Self::default(); -// }; - -// Self::new(all_assets) -// } - -// fn load_from_objects(object_files: Vec) -> Self { -// let json = get_json_from_object_files(object_files); -// Self::load(json) -// } - -// fn copy_static_assets_to(&self, location: impl Into) -> anyhow::Result<()> { -// let location = location.into(); -// match std::fs::create_dir_all(&location) { -// Ok(_) => {} -// Err(err) => { -// tracing::error!("Failed to create directory for static assets: {}", err); -// return Err(err.into()); -// } -// } - -// self.assets().iter().try_for_each(|asset| { -// match asset { -// AssetType::Resource(file_asset) => { -// tracing::info!("Optimizing and bundling {:?}", file_asset); -// tracing::trace!("Copying asset from {:?} to {:?}", file_asset, location); -// match process_file(file_asset, &location) { -// Ok(_) => {} -// Err(err) => { -// tracing::error!("Failed to copy static asset: {}", err); -// return Err(err); -// } -// } - -// // tracing::info!("Copying folder asset {}", folder_asset); -// // match process_folder(folder_asset, &location) { -// // Ok(_) => {} -// // Err(err) => { -// // tracing::error!("Failed to copy static asset: {}", err); -// // return Err(err); -// // } -// // } -// } - -// _ => {} -// } -// Ok::<(), anyhow::Error>(()) -// }) -// } - -// // fn collect_tailwind_css( -// // self: &AssetManifest, -// // include_preflight: bool, -// // warnings: &mut Vec, -// // ) -> String { -// // let mut all_classes = String::new(); - -// // for asset in self.assets() { -// // if let AssetType::Tailwind(classes) = asset { -// // all_classes.push_str(classes.classes()); -// // all_classes.push(' '); -// // } -// // } - -// // let source = railwind::Source::String(all_classes, railwind::CollectionOptions::String); - -// // let css = railwind::parse_to_string(source, include_preflight, warnings); - -// // crate::file::minify_css(&css) -// // } -// } - -// The temp file name for passing manganis json from linker to current exec. -// pub(crate) const MG_JSON_OUT: &str = "mg-out"; - -// /// Create a head file that contains all of the imports for assets that the user project uses -// pub(crate) fn create_assets_head(build: &BuildRequest, manifest: &AssetManifest) -> Result<()> { -// let out_dir = build.target_out_dir(); -// std::fs::create_dir_all(&out_dir)?; -// let mut file = File::create(out_dir.join("__assets_head.html"))?; -// file.write_all(manifest.head().as_bytes())?; -// Ok(()) -// } - -// use crate::file::Process; - -// /// Process a folder, optimizing and copying all assets into the output folder -// pub(crate) fn process_folder(folder: &FolderAsset, output_folder: &Path) -> anyhow::Result<()> { -// // Push the unique name of the folder to the output folder -// let output_folder = output_folder.join(folder.unique_name()); - -// if output_folder.exists() { -// return Ok(()); -// } - -// // .location() -// // // .source() -// // .as_path() -// let folder = folder.path(); - -// // Optimize and copy all assets in the folder in parallel -// process_folder_inner(folder, &output_folder) -// } - -// fn process_folder_inner(folder: &Path, output_folder: &Path) -> anyhow::Result<()> { -// // Create the folder -// std::fs::create_dir_all(output_folder)?; - -// // Then optimize children -// let files: Vec<_> = std::fs::read_dir(folder) -// .into_iter() -// .flatten() -// .flatten() -// .collect(); - -// files.par_iter().try_for_each(|file| { -// let file = file.path(); -// let metadata = file.metadata()?; -// let output_path = output_folder.join(file.strip_prefix(folder)?); -// if metadata.is_dir() { -// process_folder_inner(&file, &output_path) -// } else { -// process_file_minimal(&file, &output_path) -// } -// })?; - -// Ok(()) -// } - -// /// Optimize a file without changing any of its contents significantly (e.g. by changing the extension) -// fn process_file_minimal(input_path: &Path, output_path: &Path) -> anyhow::Result<()> { -// todo!() -// // let options = -// // FileOptions::default_for_extension(input_path.extension().and_then(|e| e.to_str())); -// // let source = input_path.to_path_buf(); -// // options.process(&source, output_path)?; -// // Ok(()) -// } diff --git a/packages/cli/src/builder/builder.rs b/packages/cli/src/builder/builder.rs deleted file mode 100644 index 28b092650c..0000000000 --- a/packages/cli/src/builder/builder.rs +++ /dev/null @@ -1,149 +0,0 @@ -use crate::builder::*; -use crate::dioxus_crate::DioxusCrate; -use crate::Result; -use crate::{build::BuildArgs, bundler::AppBundle}; -use futures_util::StreamExt; -use progress::{BuildUpdateProgress, ProgressRx, ProgressTx}; -use tokio::task::JoinSet; - -/// A handle to ongoing builds and then the spawned tasks themselves -pub(crate) struct Builder { - /// The application we are building - krate: DioxusCrate, - - /// Ongoing builds - building: JoinSet<(Platform, Result)>, - - /// Messages from the build engine will be sent to this channel - channel: (ProgressTx, ProgressRx), -} - -pub(crate) enum BuildUpdate { - Progress(BuildUpdateProgress), - - BuildReady { - target: Platform, - result: AppBundle, - }, - - BuildFailed { - target: Platform, - err: crate::Error, - }, - - /// All builds have finished and there's nothing left to do - AllFinished, -} - -impl Builder { - /// Create a new builder that can accept multiple simultaneous builds - pub(crate) fn new(krate: &DioxusCrate) -> Self { - Self { - channel: futures_channel::mpsc::unbounded(), - krate: krate.clone(), - building: Default::default(), - } - } - - /// Create a new builder and immediately start a build - pub(crate) fn start(krate: &DioxusCrate, args: BuildArgs) -> Result { - let mut builder = Self::new(krate); - builder.build(args)?; - Ok(builder) - } - - /// Start a new build - killing the current one if it exists - pub(crate) fn build(&mut self, args: BuildArgs) -> Result<()> { - self.abort_all(); - - super::profiles::initialize_profiles(&self.krate)?; - - let mut requests = vec![ - // At least one request for the target app - BuildRequest::new_client(&self.krate, args.clone(), self.channel.0.clone()), - ]; - - // And then the fullstack app if we're building a fullstack app - if args.fullstack { - let server = BuildRequest::new_server(&self.krate, args.clone(), self.tx()); - requests.push(server); - } - - // Queue the builds on the joinset, being careful to not panic, so we can unwrap - for build_request in requests { - let platform = build_request.platform(); - tracing::info!("Spawning build request for {platform:?}"); - self.building.spawn(async move { - // Run the build, but in a protected spawn, ensuring we can't produce panics and thus, joinerrors - let res = tokio::spawn(build_request.build()) - .await - .unwrap_or_else(|err| { - Err(crate::Error::Unique(format!( - "Panic while building project: {err:?}" - ))) - }); - - (platform, res) - }); - } - - Ok(()) - } - - /// Wait for the build to finish - pub(crate) async fn wait_for_finish(&mut self) -> Result> { - let mut results = vec![]; - - loop { - let next = self.wait().await; - - match next { - BuildUpdate::Progress(_) => {} - BuildUpdate::BuildReady { target, result } => { - results.push(result); - tracing::info!("Build ready for target {target:?}"); - } - BuildUpdate::BuildFailed { target, err } => { - tracing::error!("Build failed for target {target:?}: {err}"); - return Err(err); - } - BuildUpdate::AllFinished => { - tracing::info!("All builds finished!"); - return Ok(results); - } - } - } - } - - /// Wait for any new updates to the builder - either it completed or gave us a message etc - /// - /// Also listen for any input from the app's handle - /// - /// Returns immediately with `Finished` if there are no more builds to run - don't poll-loop this! - pub(crate) async fn wait(&mut self) -> BuildUpdate { - if self.building.is_empty() { - return BuildUpdate::AllFinished; - } - - tokio::select! { - Some(update) = self.channel.1.next() => BuildUpdate::Progress(update), - Some(Ok((target, result))) = self.building.join_next() => { - match result { - Ok(result) => BuildUpdate::BuildReady { target, result }, - Err(err) => BuildUpdate::BuildFailed { target, err }, - } - } - } - } - - /// Shutdown the current build process - /// - /// todo: might want to use a cancellation token here to allow cleaner shutdowns - pub(crate) fn abort_all(&mut self) { - self.building.abort_all(); - } - - fn tx(&self) -> ProgressTx { - self.channel.0.clone() - } -} diff --git a/packages/cli/src/builder/cargo.rs b/packages/cli/src/builder/cargo.rs index 85db7a7931..8043918184 100644 --- a/packages/cli/src/builder/cargo.rs +++ b/packages/cli/src/builder/cargo.rs @@ -1,222 +1,48 @@ +use super::web::install_web_build_tooling; use super::BuildRequest; -use crate::{assets::AssetManifest, link::LINK_OUTPUT_ENV_VAR}; -use crate::{builder::Platform, bundler::AppBundle}; -use crate::{link::InterceptedArgs, Result}; +use super::BuildResult; +use super::TargetPlatform; +use crate::assets::copy_dir_to; +use crate::assets::create_assets_head; +use crate::assets::{asset_manifest, process_assets, AssetConfigDropGuard}; +use crate::builder::progress::build_cargo; +use crate::builder::progress::CargoBuildResult; +use crate::builder::progress::Stage; +use crate::builder::progress::UpdateBuildProgress; +use crate::builder::progress::UpdateStage; +use crate::link::LinkCommand; +use crate::Result; +use crate::TraceSrc; use anyhow::Context; -use serde::Deserialize; -use std::{path::PathBuf, process::Stdio}; -use tokio::{io::AsyncBufReadExt, process::Command}; +use dioxus_cli_config::Platform; +use futures_channel::mpsc::UnboundedSender; +use manganis_cli_support::AssetManifest; +use manganis_cli_support::ManganisSupportGuard; +use std::fs::create_dir_all; +use std::path::PathBuf; +use tracing::error; impl BuildRequest { - pub(crate) async fn build(self) -> Result { - tracing::info!("🚅 Running build command..."); - - // Install any tooling that might be required for this build. - self.verify_tooling().await?; - - // Run the build command with a pretty loader, returning the executable output location - let executable = self.build_cargo().await?; - - // Extract out the asset manifest from the executable using our linker tricks - let assets = self.collect_assets().await?; - - // Assemble a bundle from everything - AppBundle::new(self, assets, executable).await - } - - pub(crate) async fn verify_tooling(&self) -> Result<()> { - match self.platform() { - // If this is a web, build make sure we have the web build tooling set up - Platform::Web => {} - - // Make sure we have mobile tooling if need be - Platform::Ios => {} - Platform::Android => {} - - // Make sure we have the required deps for desktop. More important for linux - Platform::Desktop => {} - - // Generally nothing for the server, pretty simple - Platform::Server => {} - Platform::Liveview => {} - } - - Ok(()) - } - - /// Run `cargo`, returning the location of the final exectuable - /// - /// todo: add some stats here, like timing reports, crate-graph optimizations, etc - pub(crate) async fn build_cargo(&self) -> anyhow::Result { - // Extract the unit count of the crate graph so build_cargo has more accurate data - let crate_count = self.get_unit_count_estimate().await; - - self.status_starting_build(); - - let mut child = Command::new("cargo") - .arg("rustc") - .envs( - self.custom_target_dir - .as_ref() - .map(|dir| ("CARGO_TARGET_DIR", dir)), - ) - .current_dir(self.krate.crate_dir()) - .arg("--message-format") - .arg("json-diagnostic-rendered-ansi") - .args(&self.build_arguments()) - .arg("--") - .args(self.rust_flags.clone()) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .spawn() - .context("Failed to spawn cargo build")?; - - let stdout = tokio::io::BufReader::new(child.stdout.take().unwrap()); - let stderr = tokio::io::BufReader::new(child.stderr.take().unwrap()); - - let mut output_location = None; - let mut stdout = stdout.lines(); - let mut stderr = stderr.lines(); - let mut units_compiled = 0; - let mut errors = Vec::new(); - - loop { - use cargo_metadata::Message; - - let line = tokio::select! { - Ok(Some(line)) = stdout.next_line() => line, - Ok(Some(line)) = stderr.next_line() => line, - else => break, - }; - - let mut deserializer = serde_json::Deserializer::from_str(line.trim()); - deserializer.disable_recursion_limit(); - - let message = - Message::deserialize(&mut deserializer).unwrap_or(Message::TextLine(line)); - - match message { - Message::BuildScriptExecuted(_) => units_compiled += 1, - Message::TextLine(line) => self.status_build_message(line), - Message::CompilerMessage(msg) => { - let message = msg.message; - self.status_build_diagnostic(&message); - const WARNING_LEVELS: &[cargo_metadata::diagnostic::DiagnosticLevel] = &[ - cargo_metadata::diagnostic::DiagnosticLevel::Help, - cargo_metadata::diagnostic::DiagnosticLevel::Note, - cargo_metadata::diagnostic::DiagnosticLevel::Warning, - cargo_metadata::diagnostic::DiagnosticLevel::Error, - cargo_metadata::diagnostic::DiagnosticLevel::FailureNote, - cargo_metadata::diagnostic::DiagnosticLevel::Ice, - ]; - const FATAL_LEVELS: &[cargo_metadata::diagnostic::DiagnosticLevel] = &[ - cargo_metadata::diagnostic::DiagnosticLevel::Error, - cargo_metadata::diagnostic::DiagnosticLevel::FailureNote, - cargo_metadata::diagnostic::DiagnosticLevel::Ice, - ]; - if WARNING_LEVELS.contains(&message.level) { - if let Some(rendered) = message.rendered { - errors.push(rendered); - } - } - if FATAL_LEVELS.contains(&message.level) { - return Err(anyhow::anyhow!(errors.join("\n"))); - } - } - Message::CompilerArtifact(artifact) => { - units_compiled += 1; - match artifact.executable { - Some(executable) => output_location = Some(executable.into()), - None => { - self.status_build_progress(units_compiled as f64 / crate_count as f64) - } - } - } - Message::BuildFinished(finished) => { - if !finished.success { - return Err(anyhow::anyhow!("Build failed")); - } - } - _ => {} - } - } - - output_location.context("Build did not return an executable") - } - - /// Run the linker intercept and then fill in our AssetManifest from the incremental artifacts - /// - /// This will execute `dx` with an env var set to force `dx` to operate as a linker, and then - /// traverse the .o and .rlib files rustc passes that new `dx` instance, collecting the link - /// tables marked by manganis and parsing them as a ResourceAsset. - pub(crate) async fn collect_assets(&self) -> anyhow::Result { - // If this is the server build, the client build already copied any assets we need - if self.platform() == Platform::Server { - return Ok(AssetManifest::default()); - } - - // If assets are skipped, we don't need to collect them - if self.build.skip_assets { - return Ok(AssetManifest::default()); - } - - // Create a temp file to put the output of the args - // We need to do this since rustc won't actually print the link args to stdout, so we need to - // give `dx` a file to dump its env::args into - let tmp_file = tempfile::NamedTempFile::new()?; - - // Run `cargo rustc` again, but this time with a custom linker (dx) and an env var to force - // `dx` to act as a linker - // - // Pass in the tmp_file as the env var itself - // - // NOTE: that -Csave-temps=y is needed to prevent rustc from deleting the incremental cache... - // This might not be a "stable" way of keeping artifacts around, but it's in stable rustc, so we use it - Command::new("cargo") - .arg("rustc") - .args(self.build_arguments()) - .arg("--offline") /* don't use the network, should already be resolved */ - .arg("--") - .arg(format!("-Clinker={}", std::env::current_exe().unwrap().display())) /* pass ourselves in */ - .env(LINK_OUTPUT_ENV_VAR, tmp_file.path()) /* but with the env var pointing to the temp file */ - .arg("-Csave-temps=y") /* don't delete the incremental cache */ - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .output() - .await?; - - // Read the contents of the temp file - let args = - std::fs::read_to_string(tmp_file.path()).context("Failed to read linker output")?; - - // Parse them as a Vec which is just our informal format for link args in the cli - // Todo: this might be wrong-ish on windows? The format is weird - let args = serde_json::from_str::(&args) - .context("Failed to parse linker output")?; - - Ok(AssetManifest::new_from_linker_intercept(args)) - } - /// Create a list of arguments for cargo builds pub(crate) fn build_arguments(&self) -> Vec { let mut cargo_args = Vec::new(); - if self.build.release { + if self.build_arguments.release { cargo_args.push("--release".to_string()); } - if self.build.verbose { + if self.build_arguments.verbose { cargo_args.push("--verbose".to_string()); } else { cargo_args.push("--quiet".to_string()); } - if let Some(custom_profile) = &self.build.profile { + if let Some(custom_profile) = &self.build_arguments.profile { cargo_args.push("--profile".to_string()); cargo_args.push(custom_profile.to_string()); } - if !self.build.target_args.features.is_empty() { - let features_str = self.build.target_args.features.join(" "); + if !self.build_arguments.target_args.features.is_empty() { + let features_str = self.build_arguments.target_args.features.join(" "); cargo_args.push("--features".to_string()); cargo_args.push(features_str); } @@ -224,20 +50,20 @@ impl BuildRequest { if let Some(target) = self .targeting_web() .then_some("wasm32-unknown-unknown") - .or(self.build.target_args.target.as_deref()) + .or(self.build_arguments.target_args.target.as_deref()) { cargo_args.push("--target".to_string()); cargo_args.push(target.to_string()); } - if let Some(ref platform) = self.build.target_args.package { + if let Some(ref platform) = self.build_arguments.target_args.package { cargo_args.push(String::from("-p")); cargo_args.push(platform.clone()); } - cargo_args.append(&mut self.build.cargo_args.clone()); + cargo_args.append(&mut self.build_arguments.cargo_args.clone()); - match self.krate.executable_type() { + match self.dioxus_crate.executable_type() { krates::cm::TargetKind::Bin => { cargo_args.push("--bin".to_string()); } @@ -249,9 +75,202 @@ impl BuildRequest { } _ => {} }; - - cargo_args.push(self.krate.executable_name().to_string()); + cargo_args.push(self.dioxus_crate.executable_name().to_string()); cargo_args } + + /// Create a build command for cargo + fn prepare_build_command(&self) -> Result<(tokio::process::Command, Vec)> { + let mut cmd = tokio::process::Command::new("cargo"); + cmd.arg("rustc"); + if let Some(target_dir) = &self.target_dir { + cmd.env("CARGO_TARGET_DIR", target_dir); + } + cmd.current_dir(self.dioxus_crate.crate_dir()) + .arg("--message-format") + .arg("json-diagnostic-rendered-ansi"); + + let cargo_args = self.build_arguments(); + cmd.args(&cargo_args); + + cmd.arg("--").args(self.rust_flags.clone()); + + Ok((cmd, cargo_args)) + } + + pub(crate) async fn build( + &self, + mut progress: UnboundedSender, + ) -> Result { + tracing::info!( + dx_src = ?TraceSrc::Build, + "Running build [{}] command...", + self.target_platform, + ); + + // Set up runtime guards + let mut dioxus_version = crate::dx_build_info::PKG_VERSION.to_string(); + if let Some(hash) = crate::dx_build_info::GIT_COMMIT_HASH_SHORT { + let hash = &hash.trim_start_matches('g')[..4]; + dioxus_version.push_str(&format!("-{hash}")); + } + let _guard = dioxus_cli_config::__private::save_config( + &self.dioxus_crate.dioxus_config, + &dioxus_version, + ); + let _manganis_support = ManganisSupportGuard::default(); + let _asset_guard = + AssetConfigDropGuard::new(self.dioxus_crate.dioxus_config.web.app.base_path.as_deref()); + + // If this is a web, build make sure we have the web build tooling set up + if self.targeting_web() { + install_web_build_tooling(&mut progress).await?; + } + + // Create the build command + let (cmd, cargo_args) = self.prepare_build_command()?; + + // Run the build command with a pretty loader + let crate_count = self.get_unit_count_estimate().await; + let cargo_result = build_cargo(crate_count, cmd, &mut progress).await?; + + // Post process the build result + let build_result = self + .post_process_build(cargo_args, &cargo_result, &mut progress) + .await + .context("Failed to post process build")?; + + tracing::info!( + dx_src = ?TraceSrc::Build, + "Build completed: [{}]", + self.dioxus_crate.out_dir().display(), + ); + + _ = progress.start_send(UpdateBuildProgress { + stage: Stage::Finished, + update: UpdateStage::Start, + }); + + Ok(build_result) + } + + async fn post_process_build( + &self, + cargo_args: Vec, + cargo_build_result: &CargoBuildResult, + progress: &mut UnboundedSender, + ) -> Result { + _ = progress.start_send(UpdateBuildProgress { + stage: Stage::OptimizingAssets, + update: UpdateStage::Start, + }); + + let assets = self.collect_assets(cargo_args, progress).await?; + + let file_name = self.dioxus_crate.executable_name(); + + // Move the final output executable into the dist folder + let out_dir = self.target_out_dir(); + if !out_dir.is_dir() { + create_dir_all(&out_dir)?; + } + let mut output_path = out_dir.join(file_name); + if self.targeting_web() { + output_path.set_extension("wasm"); + } else if cfg!(windows) { + output_path.set_extension("exe"); + } + if let Some(res_path) = &cargo_build_result.output_location { + std::fs::copy(res_path, &output_path)?; + } + + self.copy_assets_dir()?; + + // Create the build result + let build_result = BuildResult { + executable: output_path, + target_platform: self.target_platform, + }; + + // If this is a web build, run web post processing steps + if self.targeting_web() { + self.post_process_web_build(&build_result, assets.as_ref(), progress) + .await?; + } + + Ok(build_result) + } + + async fn collect_assets( + &self, + cargo_args: Vec, + progress: &mut UnboundedSender, + ) -> anyhow::Result> { + // If this is the server build, the client build already copied any assets we need + if self.target_platform == TargetPlatform::Server { + return Ok(None); + } + // If assets are skipped, we don't need to collect them + if self.build_arguments.skip_assets { + return Ok(None); + } + + // Start Manganis linker intercept. + let linker_args = vec![format!("{}", self.target_out_dir().display())]; + + // Don't block the main thread - manganis should not be running its own std process but it's + // fine to wrap it here at the top + let build = self.clone(); + let mut progress = progress.clone(); + tokio::task::spawn_blocking(move || { + manganis_cli_support::start_linker_intercept( + &LinkCommand::command_name(), + cargo_args, + Some(linker_args), + )?; + let Some(assets) = asset_manifest(&build) else { + error!(dx_src = ?TraceSrc::Build, "the asset manifest was not provided by manganis and we were not able to collect assets"); + return Err(anyhow::anyhow!("asset manifest was not provided by manganis")); + }; + // Collect assets from the asset manifest the linker intercept created + process_assets(&build, &assets, &mut progress)?; + // Create the __assets_head.html file for bundling + create_assets_head(&build, &assets)?; + + Ok(Some(assets)) + }) + .await + .map_err(|e| anyhow::anyhow!(e))? + } + + pub fn copy_assets_dir(&self) -> anyhow::Result<()> { + tracing::info!(dx_src = ?TraceSrc::Build, "Copying public assets to the output directory..."); + let out_dir = self.target_out_dir(); + let asset_dir = self.dioxus_crate.asset_dir(); + + if asset_dir.is_dir() { + // Only pre-compress the assets from the web build. Desktop assets are not served, so they don't need to be pre_compressed + let pre_compress = self.targeting_web() + && self + .dioxus_crate + .should_pre_compress_web_assets(self.build_arguments.release); + + copy_dir_to(asset_dir, out_dir, pre_compress)?; + } + Ok(()) + } + + /// Get the output directory for a specific built target + pub fn target_out_dir(&self) -> PathBuf { + let out_dir = self.dioxus_crate.out_dir(); + match self.build_arguments.platform { + Some(Platform::Fullstack | Platform::StaticGeneration) => match self.target_platform { + TargetPlatform::Web => out_dir.join("public"), + TargetPlatform::Desktop => out_dir.join("desktop"), + _ => out_dir, + }, + _ => out_dir, + } + } } diff --git a/packages/cli/src/builder/fullstack.rs b/packages/cli/src/builder/fullstack.rs new file mode 100644 index 0000000000..4cfdea9a47 --- /dev/null +++ b/packages/cli/src/builder/fullstack.rs @@ -0,0 +1,128 @@ +use toml_edit::Item; + +use crate::builder::Build; +use crate::dioxus_crate::DioxusCrate; + +use crate::builder::BuildRequest; +use std::io::Write; + +use super::TargetPlatform; + +static CLIENT_PROFILE: &str = "dioxus-client"; +static SERVER_PROFILE: &str = "dioxus-server"; + +// The `opt-level=2` increases build times, but can noticeably decrease time +// between saving changes and being able to interact with an app. The "overall" +// time difference (between having and not having the optimization) can be +// almost imperceptible (~1 s) but also can be very noticeable (~6 s) — depends +// on setup (hardware, OS, browser, idle load). +// Find or create the client and server profiles in the .cargo/config.toml file +fn initialize_profiles(config: &DioxusCrate) -> crate::Result<()> { + let config_path = config.workspace_dir().join(".cargo/config.toml"); + let mut config = match std::fs::read_to_string(&config_path) { + Ok(config) => config.parse::().map_err(|e| { + crate::Error::Other(anyhow::anyhow!("Failed to parse .cargo/config.toml: {}", e)) + })?, + Err(_) => Default::default(), + }; + + if let Item::Table(table) = config + .as_table_mut() + .entry("profile") + .or_insert(Item::Table(Default::default())) + { + if let toml_edit::Entry::Vacant(entry) = table.entry(CLIENT_PROFILE) { + let mut client = toml_edit::Table::new(); + client.insert("inherits", Item::Value("dev".into())); + client.insert("opt-level", Item::Value(2.into())); + entry.insert(Item::Table(client)); + } + + if let toml_edit::Entry::Vacant(entry) = table.entry(SERVER_PROFILE) { + let mut server = toml_edit::Table::new(); + server.insert("inherits", Item::Value("dev".into())); + server.insert("opt-level", Item::Value(2.into())); + entry.insert(Item::Table(server)); + } + } + + // Write the config back to the file + if let Some(parent) = config_path.parent() { + std::fs::create_dir_all(parent)?; + } + let file = std::fs::File::create(config_path)?; + let mut buf_writer = std::io::BufWriter::new(file); + write!(buf_writer, "{}", config)?; + + Ok(()) +} + +impl BuildRequest { + pub(crate) fn new_fullstack( + config: DioxusCrate, + build_arguments: Build, + serve: bool, + ) -> Result, crate::Error> { + initialize_profiles(&config)?; + + Ok(vec![ + Self::new_client(serve, &config, &build_arguments), + Self::new_server(serve, &config, &build_arguments), + ]) + } + + fn new_with_target_directory_rust_flags_and_features( + serve: bool, + config: &DioxusCrate, + build: &Build, + feature: Option, + target_platform: TargetPlatform, + ) -> Self { + let config = config.clone(); + let mut build = build.clone(); + // Add the server feature to the features we pass to the build + if let Some(feature) = feature { + build.target_args.features.push(feature); + } + + // Add the server flags to the build arguments + Self { + serve, + build_arguments: build.clone(), + dioxus_crate: config, + rust_flags: Default::default(), + target_dir: None, + target_platform, + } + } + + fn new_server(serve: bool, config: &DioxusCrate, build: &Build) -> Self { + let mut build = build.clone(); + if build.profile.is_none() { + build.profile = Some(CLIENT_PROFILE.to_string()); + } + let client_feature = build.auto_detect_server_feature(config); + Self::new_with_target_directory_rust_flags_and_features( + serve, + config, + &build, + build.target_args.server_feature.clone().or(client_feature), + TargetPlatform::Server, + ) + } + + fn new_client(serve: bool, config: &DioxusCrate, build: &Build) -> Self { + let mut build = build.clone(); + if build.profile.is_none() { + build.profile = Some(SERVER_PROFILE.to_string()); + } + let (client_feature, client_platform) = build.auto_detect_client_platform(config); + Self::new_with_target_directory_rust_flags_and_features( + serve, + config, + &build, + build.target_args.client_feature.clone().or(client_feature), + client_platform, + ) + } +} diff --git a/packages/cli/src/builder/mod.rs b/packages/cli/src/builder/mod.rs index e80c031b1e..82e702a409 100644 --- a/packages/cli/src/builder/mod.rs +++ b/packages/cli/src/builder/mod.rs @@ -1,18 +1,213 @@ -/// The primary entrypoint for our build + optimize + bundle engine -/// -/// Handles multiple ongoing tasks and allows you to queue up builds from interactive and non-interactive contexts -/// -/// Uses a request -> response architecture that allows you to monitor the progress with an optional message -/// receiver. -mod builder; +use crate::cli::serve::ServeArguments; +use crate::dioxus_crate::DioxusCrate; +use crate::Result; +use crate::{build::Build, TraceSrc}; +use dioxus_cli_config::{Platform, RuntimeCLIArguments}; +use futures_util::stream::select_all; +use futures_util::StreamExt; +use std::net::SocketAddr; +use std::str::FromStr; +use std::{path::PathBuf, process::Stdio}; +use tokio::process::{Child, Command}; + mod cargo; -mod platform; -mod profiles; +mod fullstack; +mod prepare_html; mod progress; -mod request; mod web; +pub use progress::{Stage, UpdateBuildProgress, UpdateStage}; + +/// The target platform for the build +/// This is very similar to the Platform enum, but we need to be able to differentiate between the +/// server and web targets for the fullstack platform +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum TargetPlatform { + Web, + Desktop, + Server, + Liveview, +} + +impl FromStr for TargetPlatform { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + "web" => Ok(Self::Web), + "desktop" => Ok(Self::Desktop), + "axum" | "server" => Ok(Self::Server), + "liveview" => Ok(Self::Liveview), + _ => Err(()), + } + } +} + +impl std::fmt::Display for TargetPlatform { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + TargetPlatform::Web => write!(f, "web"), + TargetPlatform::Desktop => write!(f, "desktop"), + TargetPlatform::Server => write!(f, "server"), + TargetPlatform::Liveview => write!(f, "liveview"), + } + } +} + +/// A request for a project to be built +#[derive(Clone)] +pub struct BuildRequest { + /// Whether the build is for serving the application + pub serve: bool, + /// The configuration for the crate we are building + pub dioxus_crate: DioxusCrate, + /// The target platform for the build + pub target_platform: TargetPlatform, + /// The arguments for the build + pub build_arguments: Build, + /// The rustc flags to pass to the build + pub rust_flags: Vec, + /// The target directory for the build + pub target_dir: Option, +} + +impl BuildRequest { + pub fn create( + serve: bool, + dioxus_crate: &DioxusCrate, + build_arguments: impl Into, + ) -> crate::Result> { + let build_arguments = build_arguments.into(); + let platform = build_arguments.platform(); + let single_platform = |platform| { + let dioxus_crate = dioxus_crate.clone(); + vec![Self { + serve, + dioxus_crate, + build_arguments: build_arguments.clone(), + target_platform: platform, + rust_flags: Default::default(), + target_dir: Default::default(), + }] + }; + Ok(match platform { + Platform::Liveview => single_platform(TargetPlatform::Liveview), + Platform::Web => single_platform(TargetPlatform::Web), + Platform::Desktop => single_platform(TargetPlatform::Desktop), + Platform::StaticGeneration | Platform::Fullstack => { + Self::new_fullstack(dioxus_crate.clone(), build_arguments, serve)? + } + _ => unimplemented!("Unknown platform: {platform:?}"), + }) + } + + pub(crate) async fn build_all_parallel( + build_requests: Vec, + ) -> Result> { + let multi_platform_build = build_requests.len() > 1; + let mut build_progress = Vec::new(); + let mut set = tokio::task::JoinSet::new(); + for build_request in build_requests { + let (tx, rx) = futures_channel::mpsc::unbounded(); + build_progress.push((build_request.build_arguments.platform(), rx)); + set.spawn(async move { build_request.build(tx).await }); + } + + // Watch the build progress as it comes in + loop { + let mut next = select_all( + build_progress + .iter_mut() + .map(|(platform, rx)| rx.map(move |update| (*platform, update))), + ); + match next.next().await { + Some((platform, update)) => { + if multi_platform_build { + print!("{platform} build: "); + update.to_std_out(); + } else { + update.to_std_out(); + } + } + None => { + break; + } + } + } + + let mut all_results = Vec::new(); + + while let Some(result) = set.join_next().await { + let result = result + .map_err(|_| crate::Error::Unique("Failed to build project".to_owned()))??; + all_results.push(result); + } + + Ok(all_results) + } + + /// Check if the build is targeting the web platform + pub fn targeting_web(&self) -> bool { + self.target_platform == TargetPlatform::Web + } +} + +#[derive(Debug, Clone)] +pub(crate) struct BuildResult { + pub executable: PathBuf, + pub target_platform: TargetPlatform, +} + +impl BuildResult { + /// Open the executable if this is a native build + pub fn open( + &self, + serve: &ServeArguments, + fullstack_address: Option, + workspace: &std::path::Path, + ) -> std::io::Result> { + match self.target_platform { + TargetPlatform::Web => { + tracing::info!(dx_src = ?TraceSrc::Dev, "Serving web app on http://{} 🎉", serve.address.address()); + return Ok(None); + } + TargetPlatform::Desktop => { + tracing::info!(dx_src = ?TraceSrc::Dev, "Launching desktop app at {} 🎉", self.executable.display()); + } + TargetPlatform::Server => { + if let Some(fullstack_address) = fullstack_address { + tracing::info!( + dx_src = ?TraceSrc::Dev, + "Launching fullstack server on http://{:?} 🎉", + fullstack_address + ); + } + } + TargetPlatform::Liveview => { + if let Some(fullstack_address) = fullstack_address { + tracing::info!( + dx_src = ?TraceSrc::Dev, + "Launching liveview server on http://{:?} 🎉", + fullstack_address + ); + } + } + } + + tracing::info!(dx_src = ?TraceSrc::Dev, "Press [o] to open the app manually."); -pub(crate) use builder::*; -pub(crate) use platform::*; -pub(crate) use progress::*; -pub(crate) use request::*; + let arguments = RuntimeCLIArguments::new(serve.address.address(), fullstack_address); + let executable = self.executable.canonicalize()?; + let mut cmd = Command::new(executable); + cmd + // When building the fullstack server, we need to forward the serve arguments (like port) to the fullstack server through env vars + .env( + dioxus_cli_config::__private::SERVE_ENV, + serde_json::to_string(&arguments).unwrap(), + ) + .stderr(Stdio::piped()) + .stdout(Stdio::piped()) + .kill_on_drop(true) + .current_dir(workspace); + Ok(Some(cmd.spawn()?)) + } +} diff --git a/packages/cli/src/builder/platform.rs b/packages/cli/src/builder/platform.rs deleted file mode 100644 index d72fcbe119..0000000000 --- a/packages/cli/src/builder/platform.rs +++ /dev/null @@ -1,105 +0,0 @@ -use serde::{Deserialize, Serialize}; -use std::fmt::Display; -use std::str::FromStr; - -#[derive( - Copy, - Clone, - Hash, - PartialEq, - Eq, - PartialOrd, - Ord, - Serialize, - Deserialize, - Debug, - Default, - clap::ValueEnum, -)] -#[non_exhaustive] -pub(crate) enum Platform { - /// Targeting the web platform using WASM - #[clap(name = "web")] - #[serde(rename = "web")] - #[default] - Web, - - /// Targeting the desktop platform using Tao/Wry-based webview - /// - /// Will only build for your native architecture - to do cross builds you need to use a VM. - /// Read more about cross-builds on the Dioxus Website. - #[clap(name = "desktop")] - #[serde(rename = "desktop")] - Desktop, - - /// Targeting the ios platform - /// - /// Can't work properly if you're not building from an Apple device. - #[clap(name = "ios")] - #[serde(rename = "ios")] - Ios, - - /// Targeting the android platform - #[clap(name = "android")] - #[serde(rename = "android")] - Android, - - /// Targetting the server platform using Axum and Dioxus-Fullstack - /// - /// This is implicitly passed if `fullstack` is enabled as a feature. Using this variant simply - /// means you're only building the server variant without the `.wasm` to serve. - #[clap(name = "server")] - #[serde(rename = "server")] - Server, - - /// Targeting the static generation platform using SSR and Dioxus-Fullstack - #[clap(name = "liveview")] - #[serde(rename = "liveview")] - Liveview, -} - -/// An error that occurs when a platform is not recognized -pub(crate) struct UnknownPlatformError; - -impl std::fmt::Display for UnknownPlatformError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Unknown platform") - } -} - -impl FromStr for Platform { - type Err = UnknownPlatformError; - - fn from_str(s: &str) -> Result { - match s { - "web" => Ok(Self::Web), - "desktop" => Ok(Self::Desktop), - "liveview" => Ok(Self::Liveview), - "server" => Ok(Self::Server), - "ios" => Ok(Self::Ios), - "android" => Ok(Self::Android), - _ => Err(UnknownPlatformError), - } - } -} - -impl Display for Platform { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let feature = self.feature_name(); - f.write_str(feature) - } -} - -impl Platform { - /// Get the feature name for the platform in the dioxus crate - pub(crate) fn feature_name(&self) -> &str { - match self { - Platform::Web => "web", - Platform::Desktop => "desktop", - Platform::Liveview => "liveview", - Platform::Ios => "ios", - Platform::Android => "android", - Platform::Server => "server", - } - } -} diff --git a/packages/cli/src/builder/prepare_html.rs b/packages/cli/src/builder/prepare_html.rs new file mode 100644 index 0000000000..7d99dc0e16 --- /dev/null +++ b/packages/cli/src/builder/prepare_html.rs @@ -0,0 +1,205 @@ +//! Build the HTML file to load a web application. The index.html file may be created from scratch or modified from the `index.html` file in the crate root. + +use super::{BuildRequest, UpdateBuildProgress}; +use crate::Result; +use crate::TraceSrc; +use futures_channel::mpsc::UnboundedSender; +use manganis_cli_support::AssetManifest; +use std::fmt::Write; +use std::path::{Path, PathBuf}; + +const DEFAULT_HTML: &str = include_str!("../../assets/index.html"); +const TOAST_HTML: &str = include_str!("../../assets/toast.html"); + +impl BuildRequest { + pub(crate) fn prepare_html( + &self, + assets: Option<&AssetManifest>, + _progress: &mut UnboundedSender, + ) -> Result { + let mut html = html_or_default(&self.dioxus_crate.crate_dir()); + + // Inject any resources from the config into the html + self.inject_resources(&mut html, assets)?; + + // Inject loading scripts if they are not already present + self.inject_loading_scripts(&mut html); + + // Replace any special placeholders in the HTML with resolved values + self.replace_template_placeholders(&mut html); + + let title = self.dioxus_crate.dioxus_config.web.app.title.clone(); + + replace_or_insert_before("{app_title}", ") -> Result<()> { + // Collect all resources into a list of styles and scripts + let resources = &self.dioxus_crate.dioxus_config.web.resource; + let mut style_list = resources.style.clone().unwrap_or_default(); + let mut script_list = resources.script.clone().unwrap_or_default(); + + if self.serve { + style_list.extend(resources.dev.style.iter().cloned()); + script_list.extend(resources.dev.script.iter().cloned()); + } + + let mut head_resources = String::new(); + // Add all styles to the head + for style in &style_list { + writeln!( + &mut head_resources, + "", + &style.to_str().unwrap(), + )?; + } + + if !style_list.is_empty() { + self.send_resource_deprecation_warning(style_list, ResourceType::Style); + } + + // Add all scripts to the head + for script in &script_list { + writeln!( + &mut head_resources, + "", + &script.to_str().unwrap(), + )?; + } + + if !script_list.is_empty() { + self.send_resource_deprecation_warning(script_list, ResourceType::Script); + } + + // Inject any resources from manganis into the head + if let Some(assets) = assets { + head_resources.push_str(&assets.head()); + } + + replace_or_insert_before("{style_include}", " + // We can't use a module script here because we need to start the script immediately when streaming + import("/{base_path}/assets/dioxus/{app_name}.js").then( + ({ default: init }) => { + init("/{base_path}/assets/dioxus/{app_name}_bg.wasm").then((wasm) => { + if (wasm.__wbindgen_start == undefined) { + wasm.main(); + } + }); + } + ); + + {DX_TOAST_UTILITIES} + html.replace("{DX_TOAST_UTILITIES}", TOAST_HTML), + false => html.replace("{DX_TOAST_UTILITIES}", ""), + }; + + // And try to insert preload links for the wasm and js files + *html = html.replace( + " + + , variant: ResourceType) { + const RESOURCE_DEPRECATION_MESSAGE: &str = r#"The `web.resource` config has been deprecated in favor of head components and will be removed in a future release."#; + + let replacement_components = paths + .iter() + .map(|path| { + let path = if path.exists() { + path.to_path_buf() + } else { + // If the path is absolute, make it relative to the current directory before we join it + // The path is actually a web path which is relative to the root of the website + let path = path.strip_prefix("/").unwrap_or(path); + let asset_dir_path = self.dioxus_crate.asset_dir().join(path); + if let Ok(absolute_path) = asset_dir_path.canonicalize() { + let absolute_crate_root = + self.dioxus_crate.crate_dir().canonicalize().unwrap(); + PathBuf::from("./") + .join(absolute_path.strip_prefix(absolute_crate_root).unwrap()) + } else { + path.to_path_buf() + } + }; + match variant { + ResourceType::Style => format!( + " head::Link {{ rel: \"stylesheet\", href: asset!(css(\"{}\")) }}", + path.display() + ), + ResourceType::Script => { + format!(" Script {{ src: asset!(file(\"{}\")) }}", path.display()) + } + } + }) + .collect::>(); + let replacement_components = format!("rsx! {{\n{}\n}}", replacement_components.join("\n")); + let section_name = match variant { + ResourceType::Style => "web.resource.style", + ResourceType::Script => "web.resource.script", + }; + + let message = format!( + "{RESOURCE_DEPRECATION_MESSAGE}\nTo migrate to head components, remove `{section_name}` and include the following rsx in your root component:\n```rust\n{replacement_components}\n```" + ); + + tracing::warn!(dx_src = ?TraceSrc::Build, "{}", message); + } +} + +enum ResourceType { + Style, + Script, +} + +/// Read the html file from the crate root or use the default html file +fn html_or_default(crate_root: &Path) -> String { + let custom_html_file = crate_root.join("index.html"); + std::fs::read_to_string(custom_html_file).unwrap_or_else(|_| String::from(DEFAULT_HTML)) +} + +/// Replace a string or insert the new contents before a marker +fn replace_or_insert_before( + replace: &str, + or_insert_before: &str, + with: &str, + content: &mut String, +) { + if content.contains(replace) { + *content = content.replace(replace, with); + } else if let Some(pos) = content.find(or_insert_before) { + content.insert_str(pos, with); + } +} diff --git a/packages/cli/src/builder/profiles.rs b/packages/cli/src/builder/profiles.rs deleted file mode 100644 index 480e4ad03f..0000000000 --- a/packages/cli/src/builder/profiles.rs +++ /dev/null @@ -1,52 +0,0 @@ -use crate::dioxus_crate::DioxusCrate; -use std::io::Write; -use toml_edit::Item; - -pub(crate) static CLIENT_PROFILE: &str = "dioxus-client"; -pub(crate) static SERVER_PROFILE: &str = "dioxus-server"; - -// The `opt-level=2` increases build times, but can noticeably decrease time -// between saving changes and being able to interact with an app. The "overall" -// time difference (between having and not having the optimization) can be -// almost imperceptible (~1 s) but also can be very noticeable (~6 s) — depends -// on setup (hardware, OS, browser, idle load). -// Find or create the client and server profiles in the .cargo/config.toml file -pub(crate) fn initialize_profiles(krate: &DioxusCrate) -> crate::Result<()> { - let config_path = krate.workspace_dir().join(".cargo/config.toml"); - let mut config = match std::fs::read_to_string(&config_path) { - Ok(config) => config.parse::().map_err(|e| { - crate::Error::Other(anyhow::anyhow!("Failed to parse .cargo/config.toml: {}", e)) - })?, - Err(_) => Default::default(), - }; - - if let Item::Table(table) = config - .as_table_mut() - .entry("profile") - .or_insert(Item::Table(Default::default())) - { - if let toml_edit::Entry::Vacant(entry) = table.entry(CLIENT_PROFILE) { - let mut client = toml_edit::Table::new(); - client.insert("inherits", Item::Value("dev".into())); - client.insert("opt-level", Item::Value(2.into())); - entry.insert(Item::Table(client)); - } - - if let toml_edit::Entry::Vacant(entry) = table.entry(SERVER_PROFILE) { - let mut server = toml_edit::Table::new(); - server.insert("inherits", Item::Value("dev".into())); - server.insert("opt-level", Item::Value(2.into())); - entry.insert(Item::Table(server)); - } - } - - // Write the config back to the file - if let Some(parent) = config_path.parent() { - std::fs::create_dir_all(parent)?; - } - let file = std::fs::File::create(config_path)?; - let mut buf_writer = std::io::BufWriter::new(file); - write!(buf_writer, "{}", config)?; - - Ok(()) -} diff --git a/packages/cli/src/builder/progress.rs b/packages/cli/src/builder/progress.rs index c096d6e8ca..d4966859b6 100644 --- a/packages/cli/src/builder/progress.rs +++ b/packages/cli/src/builder/progress.rs @@ -1,141 +1,18 @@ //! Report progress about the build to the user. We use channels to report progress back to the CLI. -use super::{BuildRequest, Platform}; +use crate::TraceSrc; + +use super::BuildRequest; use anyhow::Context; -use cargo_metadata::{diagnostic::Diagnostic, Message}; -use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender}; +use cargo_metadata::Message; +use futures_channel::mpsc::UnboundedSender; use serde::Deserialize; use std::ops::Deref; use std::path::PathBuf; use std::process::Stdio; -use std::{fmt::Display, path::Path}; -use tokio::{io::AsyncBufReadExt, process::Command}; -use tracing::Level; - -impl BuildRequest { - pub(crate) fn status_build_diagnostic(&self, message: &Diagnostic) { - // _ = self.progress.unbounded_send(BuildUpdateProgress { - // stage: Stage::Compiling, - // update: UpdateStage::AddMessage(message.clone().into()), - // platform: self.platform(), - // }); - } - - pub(crate) fn status_build_message(&self, line: String) { - // _ = self.progress.unbounded_send(BuildUpdateProgress { - // platform: self.platform(), - // stage: Stage::Compiling, - // update: UpdateStage::AddMessage(BuildMessage { - // level: Level::DEBUG, - // message: MessageType::Text(line), - // source: MessageSource::Build, - // }), - // }); - } - - pub(crate) fn status_build_progress(&self, build_progress: f64) { - _ = self.progress.unbounded_send(BuildUpdateProgress { - platform: self.platform(), - stage: Stage::Compiling, - update: UpdateStage::SetProgress((build_progress).clamp(0.0, 1.00)), - }); - } - - pub(crate) fn status_starting_build(&self) { - _ = self.progress.unbounded_send(BuildUpdateProgress { - stage: Stage::Compiling, - update: UpdateStage::Start, - platform: self.platform(), - }); - } - - /// Try to get the unit graph for the crate. This is a nightly only feature which may not be available with the current version of rustc the user has installed. - pub(crate) async fn get_unit_count(&self) -> Option { - #[derive(Debug, Deserialize)] - struct UnitGraph { - units: Vec, - } - - let output = tokio::process::Command::new("cargo") - .arg("+nightly") - .arg("build") - .arg("--unit-graph") - .arg("-Z") - .arg("unstable-options") - .args(self.build_arguments()) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .output() - .await - .ok()?; - - if !output.status.success() { - return None; - } - - let output_text = String::from_utf8(output.stdout).ok()?; - let graph: UnitGraph = serde_json::from_str(&output_text).ok()?; - - Some(graph.units.len()) - } - - /// Get an estimate of the number of units in the crate. If nightly rustc is not available, this will return an estimate of the number of units in the crate based on cargo metadata. - /// TODO: always use https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#unit-graph once it is stable - pub(crate) async fn get_unit_count_estimate(&self) -> usize { - // Try to get it from nightly - self.get_unit_count().await.unwrap_or_else(|| { - // Otherwise, use cargo metadata - (self - .krate - .krates - .krates_filtered(krates::DepKind::Dev) - .iter() - .map(|k| k.targets.len()) - .sum::() as f64 - / 3.5) as usize - }) - } - - pub(crate) fn status_build_finished(&self) { - tracing::info!("🚩 Build completed: [{}]", self.krate.out_dir().display()); - - _ = self.progress.unbounded_send(BuildUpdateProgress { - platform: self.platform(), - stage: Stage::Finished, - update: UpdateStage::Start, - }); - } - - pub(crate) fn status_copying_asset(&self, cur: usize, total: usize, asset: &Path) { - // Update the progress - // _ = self.progress.unbounded_send(UpdateBuildProgress { - // stage: Stage::OptimizingAssets, - // update: UpdateStage::AddMessage(BuildMessage { - // level: Level::INFO, - // message: MessageType::Text(format!( - // "Optimized static asset {}", - // asset.display() - // )), - // source: MessageSource::Build, - // }), - // platform: self.target_platform, - // }); - } - - pub(crate) fn status_finished_asset(&self, idx: usize, total: usize, asset: &Path) { - // Update the progress - // _ = self.progress.unbounded_send(UpdateBuildProgress { - // stage: Stage::OptimizingAssets, - // update: UpdateStage::SetProgress(finished as f64 / asset_count as f64), - // platform: self.target_platform, - // }); - } -} - -pub(crate) type ProgressTx = UnboundedSender; -pub(crate) type ProgressRx = UnboundedReceiver; +use tokio::io::AsyncBufReadExt; #[derive(Default, Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Copy)] -pub(crate) enum Stage { +pub enum Stage { #[default] Initializing = 0, InstallingWasmTooling = 1, @@ -167,14 +44,13 @@ impl std::fmt::Display for Stage { } #[derive(Debug, Clone)] -pub(crate) struct BuildUpdateProgress { - pub(crate) stage: Stage, - pub(crate) update: UpdateStage, - pub(crate) platform: Platform, +pub struct UpdateBuildProgress { + pub stage: Stage, + pub update: UpdateStage, } -impl BuildUpdateProgress { - pub(crate) fn to_std_out(&self) { +impl UpdateBuildProgress { + pub fn to_std_out(&self) { match &self.update { UpdateStage::Start => println!("--- {} ---", self.stage), UpdateStage::SetProgress(progress) => { @@ -188,69 +64,162 @@ impl BuildUpdateProgress { } #[derive(Debug, Clone, PartialEq)] -pub(crate) enum UpdateStage { +pub enum UpdateStage { Start, SetProgress(f64), Failed(String), } -#[derive(Debug, Clone, PartialEq)] -pub(crate) struct BuildMessage { - pub(crate) level: Level, - pub(crate) message: MessageType, - pub(crate) source: MessageSource, +pub(crate) async fn build_cargo( + crate_count: usize, + mut cmd: tokio::process::Command, + progress: &mut UnboundedSender, +) -> anyhow::Result { + _ = progress.start_send(UpdateBuildProgress { + stage: Stage::Compiling, + update: UpdateStage::Start, + }); + + let mut child = cmd + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .context("Failed to spawn cargo build")?; + let stdout = child.stdout.take().unwrap(); + let stderr = child.stderr.take().unwrap(); + let stdout = tokio::io::BufReader::new(stdout); + let stderr = tokio::io::BufReader::new(stderr); + let mut output_location = None; + + let mut stdout = stdout.lines(); + let mut stderr = stderr.lines(); + let mut units_compiled = 0; + let mut errors = Vec::new(); + loop { + let line = tokio::select! { + line = stdout.next_line() => { + line + } + line = stderr.next_line() => { + line + } + }; + let Some(line) = line? else { + break; + }; + let mut deserializer = serde_json::Deserializer::from_str(line.trim()); + deserializer.disable_recursion_limit(); + + let message = Message::deserialize(&mut deserializer).unwrap_or(Message::TextLine(line)); + match message { + Message::CompilerMessage(msg) => { + let message = msg.message; + tracing::info!(dx_src = ?TraceSrc::Cargo, dx_no_fmt = true, "{}", message.to_string()); + + const WARNING_LEVELS: &[cargo_metadata::diagnostic::DiagnosticLevel] = &[ + cargo_metadata::diagnostic::DiagnosticLevel::Help, + cargo_metadata::diagnostic::DiagnosticLevel::Note, + cargo_metadata::diagnostic::DiagnosticLevel::Warning, + cargo_metadata::diagnostic::DiagnosticLevel::Error, + cargo_metadata::diagnostic::DiagnosticLevel::FailureNote, + cargo_metadata::diagnostic::DiagnosticLevel::Ice, + ]; + const FATAL_LEVELS: &[cargo_metadata::diagnostic::DiagnosticLevel] = &[ + cargo_metadata::diagnostic::DiagnosticLevel::Error, + cargo_metadata::diagnostic::DiagnosticLevel::FailureNote, + cargo_metadata::diagnostic::DiagnosticLevel::Ice, + ]; + if WARNING_LEVELS.contains(&message.level) { + if let Some(rendered) = message.rendered { + errors.push(rendered); + } + } + if FATAL_LEVELS.contains(&message.level) { + return Err(anyhow::anyhow!(errors.join("\n"))); + } + } + Message::CompilerArtifact(artifact) => { + units_compiled += 1; + if let Some(executable) = artifact.executable { + output_location = Some(executable.into()); + } else { + let build_progress = units_compiled as f64 / crate_count as f64; + _ = progress.start_send(UpdateBuildProgress { + stage: Stage::Compiling, + update: UpdateStage::SetProgress((build_progress).clamp(0.0, 1.00)), + }); + } + } + Message::BuildScriptExecuted(_) => { + units_compiled += 1; + } + Message::BuildFinished(finished) => { + if !finished.success { + return Err(anyhow::anyhow!("Build failed")); + } + } + Message::TextLine(line) => { + tracing::info!(dx_src = ?TraceSrc::Cargo, dx_no_fmt = true, "{}", line); + } + _ => { + // Unknown message + } + } + } + + Ok(CargoBuildResult { output_location }) } -#[derive(Debug, Clone, PartialEq)] -pub(crate) enum MessageType { - Cargo(Diagnostic), - Text(String), +pub(crate) struct CargoBuildResult { + pub(crate) output_location: Option, } -/// Represents the source of where a message came from. -/// -/// The CLI will render a prefix according to the message type -/// but this prefix, [`MessageSource::to_string()`] shouldn't be used if a strict message source is required. -#[derive(Debug, Clone, PartialEq)] -pub(crate) enum MessageSource { - /// Represents any message from the running application. Renders `[app]` - App, +impl BuildRequest { + /// Try to get the unit graph for the crate. This is a nightly only feature which may not be available with the current version of rustc the user has installed. + async fn get_unit_count(&self) -> Option { + #[derive(Debug, Deserialize)] + struct UnitGraph { + units: Vec, + } - /// Represents any generic message from the CLI. Renders `[dev]` - /// - /// Usage of Tracing inside of the CLI will be routed to this type. - Dev, + let mut cmd = tokio::process::Command::new("cargo"); + cmd.arg("+nightly"); + cmd.arg("build"); + cmd.arg("--unit-graph"); + cmd.arg("-Z").arg("unstable-options"); - /// Represents a message from the build process. Renders `[bld]` - /// - /// This is anything emitted from a build process such as cargo and optimizations. - Build, -} + cmd.args(self.build_arguments()); -impl Display for MessageSource { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::App => write!(f, "app"), - Self::Dev => write!(f, "dev"), - Self::Build => write!(f, "bld"), + let output = cmd + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .output() + .await + .ok()?; + if !output.status.success() { + return None; } + + let output_text = String::from_utf8(output.stdout).ok()?; + let graph: UnitGraph = serde_json::from_str(&output_text).ok()?; + + Some(graph.units.len()) } -} -impl From for BuildMessage { - fn from(message: Diagnostic) -> Self { - Self { - level: match message.level { - cargo_metadata::diagnostic::DiagnosticLevel::Ice - | cargo_metadata::diagnostic::DiagnosticLevel::FailureNote - | cargo_metadata::diagnostic::DiagnosticLevel::Error => Level::ERROR, - cargo_metadata::diagnostic::DiagnosticLevel::Warning => Level::WARN, - cargo_metadata::diagnostic::DiagnosticLevel::Note => Level::INFO, - cargo_metadata::diagnostic::DiagnosticLevel::Help => Level::DEBUG, - _ => Level::DEBUG, - }, - source: MessageSource::Build, - message: MessageType::Cargo(message), - } + /// Get an estimate of the number of units in the crate. If nightly rustc is not available, this will return an estimate of the number of units in the crate based on cargo metadata. + /// TODO: always use https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#unit-graph once it is stable + pub(crate) async fn get_unit_count_estimate(&self) -> usize { + // Try to get it from nightly + self.get_unit_count().await.unwrap_or_else(|| { + // Otherwise, use cargo metadata + (self + .dioxus_crate + .krates + .krates_filtered(krates::DepKind::Dev) + .iter() + .map(|k| k.targets.len()) + .sum::() as f64 + / 3.5) as usize + }) } } diff --git a/packages/cli/src/builder/request.rs b/packages/cli/src/builder/request.rs deleted file mode 100644 index 211a9880e7..0000000000 --- a/packages/cli/src/builder/request.rs +++ /dev/null @@ -1,129 +0,0 @@ -use super::progress::ProgressTx; -use super::{platform, profiles::*}; -use crate::build::BuildArgs; -use crate::builder::Platform; -use crate::dioxus_crate::DioxusCrate; -use std::path::PathBuf; - -/// An app that's built, bundled, processed, and a handle to its running app, if it exists -/// -/// As the build progresses, we'll fill in fields like assets, executable, entitlements, etc -/// -/// If the app needs to be bundled, we'll add the bundle info here too -pub(crate) struct BuildRequest { - /// The configuration for the crate we are building - pub(crate) krate: DioxusCrate, - - /// The arguments for the build - pub(crate) build: BuildArgs, - - /// The rustc flags to pass to the build - pub(crate) rust_flags: Vec, - - /// The target directory for the build - pub(crate) custom_target_dir: Option, - - /// Status channel to send our progress updates to - pub(crate) progress: ProgressTx, -} - -impl BuildRequest { - pub(crate) fn new_client( - krate: &DioxusCrate, - mut build: BuildArgs, - progress: ProgressTx, - ) -> Self { - if build.profile.is_none() { - build.profile = Some(CLIENT_PROFILE.to_string()); - } - - let (client_feature, client_platform) = build.auto_detect_client_platform(krate); - - let client_feature = match build.platform { - Some(platform::Platform::Ios) => Some("mobile".to_string()), - Some(platform::Platform::Android) => Some("android".to_string()), - Some(plat) => Some(plat.to_string()), - None => client_feature, - }; - - let features = build.target_args.client_feature.clone().or(client_feature); - - tracing::info!("Client feature: {features:?}"); - - let mut build = Self::new_with_target_directory_rust_flags_and_features( - krate, &build, features, progress, - ); - - build.build.platform = build.build.platform.or(Some(client_platform)); - build - } - - pub(crate) fn new_server( - krate: &DioxusCrate, - mut build: BuildArgs, - progress: ProgressTx, - ) -> Self { - if build.profile.is_none() { - build.profile = Some(SERVER_PROFILE.to_string()); - } - - let client_feature = build.auto_detect_server_feature(krate); - let features = build.target_args.server_feature.clone().or(client_feature); - tracing::info!("Server feature: {features:?}"); - let mut build = Self::new_with_target_directory_rust_flags_and_features( - krate, &build, features, progress, - ); - - build.build.platform = Some(platform::Platform::Server); - build - } - - fn new_with_target_directory_rust_flags_and_features( - krate: &DioxusCrate, - build: &BuildArgs, - feature: Option, - progress: ProgressTx, - ) -> Self { - let config = krate.clone(); - let mut build = build.clone(); - - // Add the server feature to the features we pass to the build - if let Some(feature) = feature { - build.target_args.features.push(feature); - } - - // Add the server flags to the build arguments - Self { - build: build.clone(), - krate: config, - rust_flags: Default::default(), - custom_target_dir: None, - progress, - } - } - - /// Get the platform for this build - pub(crate) fn platform(&self) -> Platform { - self.build - .platform - .unwrap_or_else(|| self.krate.dioxus_config.application.default_platform) - } - - /// The final output name of the app, primarly to be used when bundled - /// - /// Needs to be very disambiguated - /// Eg: my-app-web-macos-x86_64.app - /// {app_name}-{platform}-{arch} - /// - /// Does not include the extension - pub(crate) fn app_name(&self) -> String { - match self.platform() { - Platform::Web => "web".to_string(), - Platform::Desktop => todo!(), - Platform::Ios => todo!(), - Platform::Server => "server".to_string(), - Platform::Android => todo!(), - Platform::Liveview => todo!(), - } - } -} diff --git a/packages/cli/src/builder/web.rs b/packages/cli/src/builder/web.rs index 270b61f079..0354d86375 100644 --- a/packages/cli/src/builder/web.rs +++ b/packages/cli/src/builder/web.rs @@ -1,37 +1,87 @@ -use super::{BuildRequest, Platform}; +use super::BuildRequest; +use super::BuildResult; +use crate::assets::pre_compress_folder; +use crate::builder::progress::Stage; +use crate::builder::progress::UpdateBuildProgress; +use crate::builder::progress::UpdateStage; use crate::error::{Error, Result}; -use crate::{ - builder::progress::{ - BuildMessage, BuildUpdateProgress, MessageSource, MessageType, Stage, UpdateStage, - }, - TraceSrc, -}; -use anyhow::Context; -use std::fmt::Write; -use std::path::{Path, PathBuf}; +use crate::TraceSrc; +use futures_channel::mpsc::UnboundedSender; +use manganis_cli_support::AssetManifest; +use std::path::Path; use tokio::process::Command; -use tracing::Level; use wasm_bindgen_cli_support::Bindgen; -const DEFAULT_HTML: &str = include_str!("../../assets/index.html"); -const TOAST_HTML: &str = include_str!("../../assets/toast.html"); +// Attempt to automatically recover from a bindgen failure by updating the wasm-bindgen version +async fn update_wasm_bindgen_version() -> Result<()> { + let cli_bindgen_version = wasm_bindgen_shared::version(); + tracing::info!(dx_src = ?TraceSrc::Build, "Attempting to recover from bindgen failure by setting the wasm-bindgen version to {cli_bindgen_version}..."); + + let output = Command::new("cargo") + .args([ + "update", + "-p", + "wasm-bindgen", + "--precise", + &cli_bindgen_version, + ]) + .output() + .await; + let mut error_message = None; + if let Ok(output) = output { + if output.status.success() { + tracing::info!(dx_src = ?TraceSrc::Dev, "Successfully updated wasm-bindgen to {cli_bindgen_version}"); + return Ok(()); + } else { + error_message = Some(output); + } + } -impl BuildRequest { - pub(crate) async fn run_wasm_bindgen( - &self, - input_path: &Path, - bindgen_outdir: &Path, - ) -> Result<()> { - tracing::info!("Running wasm-bindgen"); + if let Some(output) = error_message { + tracing::error!(dx_src = ?TraceSrc::Dev, "Failed to update wasm-bindgen: {:#?}", output); + } + + Err(Error::BuildFailed(format!("WASM bindgen build failed!\nThis is probably due to the Bindgen version, dioxus-cli is using `{cli_bindgen_version}` which is not compatible with your crate.\nPlease reinstall the dioxus cli to fix this issue.\nYou can reinstall the dioxus cli by running `cargo install dioxus-cli --force` and then rebuild your project"))) +} + +/// Check if the wasm32-unknown-unknown target is installed and try to install it if not +pub(crate) async fn install_web_build_tooling( + progress: &mut UnboundedSender, +) -> Result<()> { + // If the user has rustup, we can check if the wasm32-unknown-unknown target is installed + // Otherwise we can just assume it is installed - which is not great... + // Eventually we can poke at the errors and let the user know they need to install the target + if let Ok(wasm_check_command) = Command::new("rustup").args(["show"]).output().await { + let wasm_check_output = String::from_utf8(wasm_check_command.stdout).unwrap(); + if !wasm_check_output.contains("wasm32-unknown-unknown") { + _ = progress.start_send(UpdateBuildProgress { + stage: Stage::InstallingWasmTooling, + update: UpdateStage::Start, + }); + tracing::info!(dx_src = ?TraceSrc::Build, "`wasm32-unknown-unknown` target not detected, installing.."); + let _ = Command::new("rustup") + .args(["target", "add", "wasm32-unknown-unknown"]) + .output() + .await?; + } + } + Ok(()) +} + +impl BuildRequest { + async fn run_wasm_bindgen(&self, input_path: &Path, bindgen_outdir: &Path) -> Result<()> { + tracing::info!(dx_src = ?TraceSrc::Build, "Running wasm-bindgen"); let input_path = input_path.to_path_buf(); let bindgen_outdir = bindgen_outdir.to_path_buf(); - let name = self.krate.dioxus_config.application.name.clone(); - let keep_debug = self.krate.dioxus_config.web.wasm_opt.debug || (!self.build.release); - - let start = std::time::Instant::now(); - tokio::task::spawn_blocking(move || { - Bindgen::new() + let keep_debug = + self.dioxus_crate.dioxus_config.web.wasm_opt.debug || (!self.build_arguments.release); + let name = self.dioxus_crate.dioxus_config.application.name.clone(); + let run_wasm_bindgen = move || { + // [3] Bindgen the final binary for use easy linking + let mut bindgen_builder = Bindgen::new(); + + bindgen_builder .input_path(&input_path) .web(true) .unwrap() @@ -43,263 +93,101 @@ impl BuildRequest { .remove_producers_section(!keep_debug) .out_name(&name) .generate(&bindgen_outdir) - }) - .await - .context("Wasm-bindgen crashed while optimizing the wasm binary")? - .context("Failed to generate wasm-bindgen bindings")?; - - tracing::info!(dx_src = ?TraceSrc::Build, "wasm-bindgen complete in {:?}", start.elapsed()); - - Ok(()) - } - - #[allow(unused)] - pub(crate) fn run_wasm_opt(&self, bindgen_outdir: &std::path::PathBuf) -> Result<(), Error> { - if !self.build.release { - return Ok(()); + .unwrap(); }; - - #[cfg(feature = "wasm-opt")] - { - use crate::config::WasmOptLevel; - - tracing::info!(dx_src = ?TraceSrc::Build, "Running optimization with wasm-opt..."); - - let mut options = match self.dioxus_crate.dioxus_config.web.wasm_opt.level { - WasmOptLevel::Z => { - wasm_opt::OptimizationOptions::new_optimize_for_size_aggressively() - } - WasmOptLevel::S => wasm_opt::OptimizationOptions::new_optimize_for_size(), - WasmOptLevel::Zero => wasm_opt::OptimizationOptions::new_opt_level_0(), - WasmOptLevel::One => wasm_opt::OptimizationOptions::new_opt_level_1(), - WasmOptLevel::Two => wasm_opt::OptimizationOptions::new_opt_level_2(), - WasmOptLevel::Three => wasm_opt::OptimizationOptions::new_opt_level_3(), - WasmOptLevel::Four => wasm_opt::OptimizationOptions::new_opt_level_4(), - }; - let wasm_file = bindgen_outdir.join(format!( - "{}_bg.wasm", - self.dioxus_crate.dioxus_config.application.name - )); - let old_size = wasm_file.metadata()?.len(); - options - // WASM bindgen relies on reference types - .enable_feature(wasm_opt::Feature::ReferenceTypes) - .debug_info(self.dioxus_crate.dioxus_config.web.wasm_opt.debug) - .run(&wasm_file, &wasm_file) - .map_err(|err| Error::Other(anyhow::anyhow!(err)))?; - - let new_size = wasm_file.metadata()?.len(); - tracing::info!( - dx_src = ?TraceSrc::Build, - "wasm-opt reduced WASM size from {} to {} ({:2}%)", - old_size, - new_size, - (new_size as f64 - old_size as f64) / old_size as f64 * 100.0 - ); + let bindgen_result = tokio::task::spawn_blocking(run_wasm_bindgen.clone()).await; + + // WASM bindgen requires the exact version of the bindgen schema to match the version the CLI was built with + // If we get an error, we can try to recover by pinning the user's wasm-bindgen version to the version we used + if let Err(err) = bindgen_result { + tracing::error!(dx_src = ?TraceSrc::Build, "Bindgen build failed: {:?}", err); + update_wasm_bindgen_version().await?; + run_wasm_bindgen(); } Ok(()) } - pub(crate) fn prepare_html(&self) -> Result { - let mut html = { - let crate_root: &Path = &self.krate.crate_dir(); - let custom_html_file = crate_root.join("index.html"); - std::fs::read_to_string(custom_html_file).unwrap_or_else(|_| String::from(DEFAULT_HTML)) - }; - - // Inject any resources from the config into the html - self.inject_resources(&mut html)?; - - // Inject loading scripts if they are not already present - self.inject_loading_scripts(&mut html); - - // Replace any special placeholders in the HTML with resolved values - self.replace_template_placeholders(&mut html); - - let title = self.krate.dioxus_config.web.app.title.clone(); - - replace_or_insert_before("{app_title}", " bool { - !self.build.release - } - - // Inject any resources from the config into the html - fn inject_resources(&self, html: &mut String) -> Result<()> { - // Collect all resources into a list of styles and scripts - let resources = &self.krate.dioxus_config.web.resource; - let mut style_list = resources.style.clone().unwrap_or_default(); - let mut script_list = resources.script.clone().unwrap_or_default(); - - if self.is_dev_build() { - style_list.extend(resources.dev.style.iter().cloned()); - script_list.extend(resources.dev.script.iter().cloned()); - } - - let mut head_resources = String::new(); - - // Add all styles to the head - for style in &style_list { - writeln!( - &mut head_resources, - "", - &style.to_str().unwrap(), - )?; - } - - // Add all scripts to the head - for script in &script_list { - writeln!( - &mut head_resources, - "", - &script.to_str().unwrap(), - )?; - } - - if !style_list.is_empty() { - self.send_resource_deprecation_warning(style_list, ResourceType::Style); - } - if !script_list.is_empty() { - self.send_resource_deprecation_warning(script_list, ResourceType::Script); - } - - // Inject any resources from manganis into the head - // if let Some(assets) = assets { - // head_resources.push_str(&assets.head()); - // } - - replace_or_insert_before("{style_include}", " - // We can't use a module script here because we need to start the script immediately when streaming - import("/{base_path}/wasm/{app_name}.js").then( - ({ default: init }) => { - init("/{base_path}/wasm/{app_name}_bg.wasm").then((wasm) => { - if (wasm.__wbindgen_start == undefined) { - wasm.main(); - } - }); - } - ); - - {DX_TOAST_UTILITIES} - html.replace("{DX_TOAST_UTILITIES}", TOAST_HTML), - false => html.replace("{DX_TOAST_UTILITIES}", ""), - }; - - // And try to insert preload links for the wasm and js files - *html = html.replace( - " - - , + progress: &mut UnboundedSender, + ) -> Result<()> { + _ = progress.start_send(UpdateBuildProgress { + stage: Stage::OptimizingWasm, + update: UpdateStage::Start, + }); - /// Replace any special placeholders in the HTML with resolved values - fn replace_template_placeholders(&self, html: &mut String) { - let base_path = self.krate.dioxus_config.web.app.base_path(); - *html = html.replace("{base_path}", base_path); + // Find the wasm file + let output_location = build_result.executable.clone(); + let input_path = output_location.with_extension("wasm"); - let app_name = &self.krate.dioxus_config.application.name; - *html = html.replace("{app_name}", app_name); - } + // Create the directory where the bindgen output will be placed + let bindgen_outdir = self.target_out_dir().join("assets").join("dioxus"); - fn send_resource_deprecation_warning(&self, paths: Vec, variant: ResourceType) { - const RESOURCE_DEPRECATION_MESSAGE: &str = r#"The `web.resource` config has been deprecated in favor of head components and will be removed in a future release. Instead of including assets in the config, you can include assets with the `asset!` macro and add them to the head with `document::Link` and `Script` components."#; + // Run wasm-bindgen + self.run_wasm_bindgen(&input_path, &bindgen_outdir).await?; - let replacement_components = paths - .iter() - .map(|path| { - let path = if path.exists() { - path.to_path_buf() - } else { - // If the path is absolute, make it relative to the current directory before we join it - // The path is actually a web path which is relative to the root of the website - let path = path.strip_prefix("/").unwrap_or(path); - let asset_dir_path = self.krate.legacy_asset_dir().join(path); - if let Ok(absolute_path) = asset_dir_path.canonicalize() { - let absolute_crate_root = self.krate.crate_dir().canonicalize().unwrap(); - PathBuf::from("./") - .join(absolute_path.strip_prefix(absolute_crate_root).unwrap()) - } else { - path.to_path_buf() + // Only run wasm-opt if the feature is enabled + // Wasm-opt has an expensive build script that makes it annoying to keep enabled for iterative dev + #[cfg(feature = "wasm-opt")] + { + // Run wasm-opt if this is a release build + if self.build_arguments.release { + use dioxus_cli_config::WasmOptLevel; + + tracing::info!(dx_src = ?TraceSrc::Build, "Running optimization with wasm-opt..."); + let mut options = match self.dioxus_crate.dioxus_config.web.wasm_opt.level { + WasmOptLevel::Z => { + wasm_opt::OptimizationOptions::new_optimize_for_size_aggressively() } + WasmOptLevel::S => wasm_opt::OptimizationOptions::new_optimize_for_size(), + WasmOptLevel::Zero => wasm_opt::OptimizationOptions::new_opt_level_0(), + WasmOptLevel::One => wasm_opt::OptimizationOptions::new_opt_level_1(), + WasmOptLevel::Two => wasm_opt::OptimizationOptions::new_opt_level_2(), + WasmOptLevel::Three => wasm_opt::OptimizationOptions::new_opt_level_3(), + WasmOptLevel::Four => wasm_opt::OptimizationOptions::new_opt_level_4(), }; - match variant { - ResourceType::Style => { - format!(" Stylesheet {{ href: asset!(\"{}\") }}", path.display()) - } - ResourceType::Script => { - format!(" Script {{ src: asset!(\"{}\") }}", path.display()) - } - } - }) - .collect::>(); - let replacement_components = format!("rsx! {{\n{}\n}}", replacement_components.join("\n")); - let section_name = match variant { - ResourceType::Style => "web.resource.style", - ResourceType::Script => "web.resource.script", - }; - - let message = format!( - "{RESOURCE_DEPRECATION_MESSAGE}\nTo migrate to head components, remove `{section_name}` and include the following rsx in your root component:\n```rust\n{replacement_components}\n```" - ); - - // _ = self.progress.unbounded_send(BuildUpdateProgress { - // platform: self.platform(), - // stage: Stage::OptimizingWasm, - // update: UpdateStage::AddMessage(BuildMessage { - // level: Level::WARN, - // message: MessageType::Text(message), - // source: MessageSource::Build, - // }), - // }); - } - - /// Check if the build is targeting the web platform - pub(crate) fn targeting_web(&self) -> bool { - self.platform() == Platform::Web - } -} + let wasm_file = bindgen_outdir.join(format!( + "{}_bg.wasm", + self.dioxus_crate.dioxus_config.application.name + )); + let old_size = wasm_file.metadata()?.len(); + options + // WASM bindgen relies on reference types + .enable_feature(wasm_opt::Feature::ReferenceTypes) + .debug_info(self.dioxus_crate.dioxus_config.web.wasm_opt.debug) + .run(&wasm_file, &wasm_file) + .map_err(|err| Error::Other(anyhow::anyhow!(err)))?; + let new_size = wasm_file.metadata()?.len(); + tracing::info!( + dx_src = ?TraceSrc::Build, + "wasm-opt reduced WASM size from {} to {} ({:2}%)", + old_size, + new_size, + (new_size as f64 - old_size as f64) / old_size as f64 * 100.0 + ); + } + } -enum ResourceType { - Style, - Script, -} + // If pre-compressing is enabled, we can pre_compress the wasm-bindgen output + let pre_compress = self + .dioxus_crate + .should_pre_compress_web_assets(self.build_arguments.release); + tokio::task::spawn_blocking(move || pre_compress_folder(&bindgen_outdir, pre_compress)) + .await + .unwrap()?; + + // Create the index.html file + // Note that we do this last since the webserver will attempt to serve the index.html file + // If we do this too early, the wasm won't be ready but the index.html will be served, leading + // to test failures and broken pages. + let html = self.prepare_html(assets, progress)?; + let html_path = self.target_out_dir().join("index.html"); + std::fs::write(html_path, html)?; -/// Replace a string or insert the new contents before a marker -fn replace_or_insert_before( - replace: &str, - or_insert_before: &str, - with: &str, - content: &mut String, -) { - if content.contains(replace) { - *content = content.replace(replace, with); - } else if let Some(pos) = content.find(or_insert_before) { - content.insert_str(pos, with); + Ok(()) } } diff --git a/packages/cli/src/bundle_utils.rs b/packages/cli/src/bundle_utils.rs deleted file mode 100644 index e9619db664..0000000000 --- a/packages/cli/src/bundle_utils.rs +++ /dev/null @@ -1,165 +0,0 @@ -// use dioxus_cli_config::BundleConfig; - -use crate::config::BundleConfig; - -pub(crate) fn make_tauri_bundler_settings(bundle_config: BundleConfig) -> tauri_bundler::BundleSettings { - todo!() -} - -// impl From for tauri_bundler::NsisSettings { -// fn from(val: NsisSettings) -> Self { -// tauri_bundler::NsisSettings { -// header_image: val.header_image, -// sidebar_image: val.sidebar_image, -// installer_icon: val.installer_icon, -// install_mode: val.install_mode.into(), -// languages: val.languages, -// display_language_selector: val.display_language_selector, -// custom_language_files: None, -// template: None, -// compression: tauri_utils::config::NsisCompression::None, -// start_menu_folder: todo!(), -// installer_hooks: todo!(), -// } -// } -// } - -// impl From for tauri_bundler::BundleSettings { -// fn from(val: BundleConfig) -> Self { -// tauri_bundler::BundleSettings { -// identifier: val.identifier, -// publisher: val.publisher, -// icon: val.icon, -// resources: val.resources, -// copyright: val.copyright, -// category: val.category.and_then(|c| c.parse().ok()), -// short_description: val.short_description, -// long_description: val.long_description, -// external_bin: val.external_bin, -// deb: val.deb.map(Into::into).unwrap_or_default(), -// macos: val.macos.map(Into::into).unwrap_or_default(), -// windows: val.windows.map(Into::into).unwrap_or_default(), -// ..Default::default() -// } -// } -// } - -// impl From for tauri_bundler::DebianSettings { -// fn from(val: DebianSettings) -> Self { -// tauri_bundler::DebianSettings { -// depends: val.depends, -// files: val.files, -// desktop_template: None, -// provides: todo!(), -// conflicts: todo!(), -// replaces: todo!(), -// section: todo!(), -// priority: todo!(), -// changelog: todo!(), -// pre_install_script: todo!(), -// post_install_script: todo!(), -// pre_remove_script: todo!(), -// post_remove_script: todo!(), -// } -// } -// } - -// impl From for tauri_bundler::WixSettings { -// fn from(val: WixSettings) -> Self { -// tauri_bundler::WixSettings { -// language: tauri_bundler::bundle::WixLanguage({ -// let mut languages: Vec<_> = val -// .language -// .iter() -// .map(|l| { -// ( -// l.0.clone(), -// tauri_bundler::bundle::WixLanguageConfig { -// locale_path: l.1.clone(), -// }, -// ) -// }) -// .collect(); -// if languages.is_empty() { -// languages.push(("en-US".into(), Default::default())); -// } -// languages -// }), -// template: val.template, -// fragment_paths: val.fragment_paths, -// component_group_refs: val.component_group_refs, -// component_refs: val.component_refs, -// feature_group_refs: val.feature_group_refs, -// feature_refs: val.feature_refs, -// merge_refs: val.merge_refs, -// enable_elevated_update_task: val.enable_elevated_update_task, -// banner_path: val.banner_path, -// dialog_image_path: val.dialog_image_path, -// fips_compliant: val.fips_compliant, -// } -// } -// } - -// impl From for tauri_bundler::MacOsSettings { -// fn from(val: MacOsSettings) -> Self { -// tauri_bundler::MacOsSettings { -// frameworks: val.frameworks, -// minimum_system_version: val.minimum_system_version, -// exception_domain: val.exception_domain, -// signing_identity: val.signing_identity, -// provider_short_name: val.provider_short_name, -// entitlements: val.entitlements, -// info_plist_path: val.info_plist_path, -// files: todo!(), -// hardened_runtime: todo!(), -// } -// } -// } - -// impl From for tauri_bundler::WindowsSettings { -// fn from(val: WindowsSettings) -> Self { -// tauri_bundler::WindowsSettings { -// digest_algorithm: val.digest_algorithm, -// certificate_thumbprint: val.certificate_thumbprint, -// timestamp_url: val.timestamp_url, -// tsp: val.tsp, -// wix: val.wix.map(Into::into), -// icon_path: val.icon_path.unwrap_or("icons/icon.ico".into()), -// webview_install_mode: val.webview_install_mode.into(), -// webview_fixed_runtime_path: val.webview_fixed_runtime_path, -// allow_downgrades: val.allow_downgrades, -// nsis: val.nsis.map(Into::into), -// sign_command: todo!(), -// } -// } -// } - -// impl From for tauri_utils::config::NSISInstallerMode { -// fn from(val: NSISInstallerMode) -> Self { -// match val { -// NSISInstallerMode::CurrentUser => tauri_utils::config::NSISInstallerMode::CurrentUser, -// NSISInstallerMode::PerMachine => tauri_utils::config::NSISInstallerMode::PerMachine, -// NSISInstallerMode::Both => tauri_utils::config::NSISInstallerMode::Both, -// } -// } -// } - -// impl WebviewInstallMode { -// fn into(self) -> tauri_utils::config::WebviewInstallMode { -// match self { -// Self::Skip => tauri_utils::config::WebviewInstallMode::Skip, -// Self::DownloadBootstrapper { silent } => { -// tauri_utils::config::WebviewInstallMode::DownloadBootstrapper { silent } -// } -// Self::EmbedBootstrapper { silent } => { -// tauri_utils::config::WebviewInstallMode::EmbedBootstrapper { silent } -// } -// Self::OfflineInstaller { silent } => { -// tauri_utils::config::WebviewInstallMode::OfflineInstaller { silent } -// } -// Self::FixedRuntime { path } => { -// tauri_utils::config::WebviewInstallMode::FixedRuntime { path } -// } -// } -// } -// } diff --git a/packages/cli/src/bundler/android.rs b/packages/cli/src/bundler/android.rs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/packages/cli/src/bundler/app.rs b/packages/cli/src/bundler/app.rs deleted file mode 100644 index cf8f53c8eb..0000000000 --- a/packages/cli/src/bundler/app.rs +++ /dev/null @@ -1,246 +0,0 @@ -use crate::assets::AssetManifest; -use crate::builder::{BuildRequest, Platform}; -use crate::Result; -use rayon::prelude::{IntoParallelRefIterator, ParallelIterator}; -use std::path::PathBuf; -use std::sync::atomic::AtomicUsize; - -pub(crate) struct AppBundle { - pub(crate) build: BuildRequest, - pub(crate) workdir: PathBuf, - pub(crate) executable: PathBuf, - pub(crate) assets: AssetManifest, -} - -impl AppBundle { - pub(crate) async fn new( - build: BuildRequest, - assets: AssetManifest, - executable: PathBuf, - ) -> Result { - let bundle = Self { - workdir: build.krate.workdir(build.build.platform()), - build, - executable, - assets, - }; - - bundle.prepare_workdir()?; - bundle.write_main_executable().await?; - bundle.write_assets().await?; - bundle.write_metadata().await?; - bundle.optimize().await?; - - Ok(bundle) - } - - // Create the workdir and then clean its contents, in case it already exists - fn prepare_workdir(&self) -> Result<()> { - // _ = std::fs::remove_dir_all(&self.workdir); - _ = std::fs::create_dir_all(&self.workdir); - Ok(()) - } - - /// Take the output of rustc and make it into the main exe of the bundle - /// - /// For wasm, we'll want to run `wasm-bindgen` to make it a wasm binary along with some other optimizations - /// Other platforms we might do some stripping or other optimizations - async fn write_main_executable(&self) -> Result<()> { - match self.build.platform() { - // Run wasm-bindgen on the wasm binary and set its output to be in the bundle folder - // Also run wasm-opt on the wasm binary, and sets the index.html since that's also the "executable". - // - // The wasm stuff will be in a folder called "wasm" in the workdir. - // - // Final output format: - // ``` - // dist/ - // web - // index.html - // wasm/ - // app.wasm - // glue.js - // snippets/ - // ... - // assets/ - // logo.png - // ``` - Platform::Web => { - // Run wasm-bindgen and drop its output into the assets folder under "dioxus" - self.build - .run_wasm_bindgen(&self.executable.with_extension("wasm"), &self.bindgen_dir()) - .await?; - - // Only run wasm-opt if the feature is enabled - // Wasm-opt has an expensive build script that makes it annoying to keep enabled for iterative dev - // We put it behind the "wasm-opt" feature flag so that it can be disabled when iterating on the cli - self.build.run_wasm_opt(&self.bindgen_dir())?; - - // Write the index.html file - std::fs::write(self.workdir.join("index.html"), self.build.prepare_html()?)?; - } - - // Move the executable to the workdir - Platform::Desktop => { - std::fs::copy(self.executable.clone(), self.workdir.join("app"))?; - } - - Platform::Ios => {} - Platform::Server => {} - Platform::Liveview => {} - Platform::Android => todo!("android not yet supported!"), - } - - Ok(()) - } - - /// Copy the assets out of the manifest and into the target location - /// - /// Should be the same on all platforms - just copy over the assets from the manifest into the output directory - async fn write_assets(&self) -> Result<()> { - // Server doesn't need assets - web will provide them - if self.build.platform() == Platform::Server { - return Ok(()); - } - - let asset_dir = self.asset_dir(); - let assets = self.all_source_assets(); - - let asset_count = assets.len(); - let assets_finished = AtomicUsize::new(0); - let optimize = false; - let pre_compress = false; - - // Parallel Copy over the assets and keep track of progress with an atomic counter - assets.par_iter().try_for_each(|asset| { - self.build.status_copying_asset( - assets_finished.fetch_add(0, std::sync::atomic::Ordering::SeqCst), - asset_count, - asset, - ); - - let res = self - .assets - .copy_asset_to(&asset_dir, asset, optimize, pre_compress); - - if let Err(err) = res { - tracing::error!("Failed to copy asset {asset:?}: {err}"); - } - - self.build.status_finished_asset( - assets_finished.fetch_add(1, std::sync::atomic::Ordering::SeqCst), - asset_count, - asset, - ); - - Ok(()) as anyhow::Result<()> - })?; - - Ok(()) - } - - /// Take the workdir and copy it to the output location, returning the path to final bundle - /// - /// Perform any finishing steps here: - /// - Signing the bundle - pub(crate) async fn finish(&self, destination: PathBuf) -> Result { - // std::fs::create_dir_all(&destination.join(self.build.app_name()))?; - - match self.build.platform() { - // Nothing special to do - just copy the workdir to the output location - Platform::Web => { - std::fs::create_dir_all(&destination.join("web"))?; - crate::fastfs::copy_asset(&self.workdir, &destination.join("web"))?; - Ok(destination.join("web")) - } - - // Create a final .app/.exe/etc depending on the host platform, not dependent on the host - Platform::Desktop => { - // for now, until we have bundled hotreload, just copy the executable to the output location - // let output_location = destination.join(self.build.app_name()); - Ok(self.executable.clone()) - // Ok(output_location) - } - - Platform::Server => { - std::fs::copy( - self.executable.clone(), - destination.join(self.build.app_name()), - )?; - - Ok(destination.join(self.build.app_name())) - } - Platform::Liveview => Ok(self.executable.clone()), - - // Create a .ipa, only from macOS - Platform::Ios => todo!(), - - // Create a .exe, from linux/mac/windows - Platform::Android => todo!(), - } - } - - fn bindgen_dir(&self) -> PathBuf { - self.workdir.join("wasm") - } - - pub(crate) fn all_source_assets(&self) -> Vec { - // Merge the legacy asset dir assets with the assets from the manifest - // Legacy assets need to retain their name in case they're referenced in the manifest - // todo: we should only copy over assets that appear in `img { src: "assets/logo.png" }` to - // properly deprecate the legacy asset dir - self.assets - .assets - .keys() - .cloned() - .chain(self.build.krate.legacy_asset_dir_files()) - .collect::>() - } - - async fn write_metadata(&self) -> Result<()> { - Ok(()) - } - - pub(crate) fn asset_dir(&self) -> PathBuf { - let dir: PathBuf = match self.build.platform() { - Platform::Web => self.workdir.join("assets"), - Platform::Desktop => self.workdir.join("Resources"), - Platform::Ios => self.workdir.join("Resources"), - Platform::Android => self.workdir.join("assets"), - Platform::Server => self.workdir.join("assets"), - Platform::Liveview => self.workdir.join("assets"), - }; - - if !dir.exists() { - std::fs::create_dir_all(&dir).expect("Failed to create asset dir in temp dir"); - } - - dir - } - - /// Run the optimizers, obfuscators, minimizers, etc - pub(crate) async fn optimize(&self) -> Result<()> { - match self.build.platform() { - Platform::Web => { - // Compress the asset dir - // // If pre-compressing is enabled, we can pre_compress the wasm-bindgen output - // let pre_compress = self - // .krate - // .should_pre_compress_web_assets(self.build.release); - - // tokio::task::spawn_blocking(move || { - // pre_compress_folder(&bindgen_outdir, pre_compress) - // }) - // .await - // .unwrap()?; - } - Platform::Desktop => {} - Platform::Ios => {} - Platform::Android => {} - Platform::Server => {} - Platform::Liveview => {} - } - - Ok(()) - } -} diff --git a/packages/cli/src/bundler/deb.rs b/packages/cli/src/bundler/deb.rs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/packages/cli/src/bundler/format.rs b/packages/cli/src/bundler/format.rs deleted file mode 100644 index 2026edb60d..0000000000 --- a/packages/cli/src/bundler/format.rs +++ /dev/null @@ -1,23 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Debug)] -pub(crate) enum BundleFormat { - // Apple - Macos, - Ios, - - // wasm - Web, - - // Android - Android, - - // Linux - AppImage, - Deb, - Rpm, - - // Windows - Msi, - Wix, -} diff --git a/packages/cli/src/bundler/ios.rs b/packages/cli/src/bundler/ios.rs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/packages/cli/src/bundler/mac.rs b/packages/cli/src/bundler/mac.rs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/packages/cli/src/bundler/mod.rs b/packages/cli/src/bundler/mod.rs deleted file mode 100644 index 6a3f98a4dc..0000000000 --- a/packages/cli/src/bundler/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -mod android; -mod format; -mod ios; -mod mac; -mod web; -mod win; - -mod app; -pub(crate) use app::*; diff --git a/packages/cli/src/bundler/web.rs b/packages/cli/src/bundler/web.rs deleted file mode 100644 index 0012867163..0000000000 --- a/packages/cli/src/bundler/web.rs +++ /dev/null @@ -1,8 +0,0 @@ -use super::AppBundle; -use crate::Result; - -impl AppBundle { - pub(crate) fn prepare_html(&self) -> Result { - todo!() - } -} diff --git a/packages/cli/src/bundler/win.rs b/packages/cli/src/bundler/win.rs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/packages/cli/src/cli/autoformat.rs b/packages/cli/src/cli/autoformat.rs index 05549c454a..3d2f5c6827 100644 --- a/packages/cli/src/cli/autoformat.rs +++ b/packages/cli/src/cli/autoformat.rs @@ -10,35 +10,35 @@ use std::{borrow::Cow, fs, path::Path, process::exit}; /// Format some rsx #[derive(Clone, Debug, Parser)] -pub(crate) struct Autoformat { +pub struct Autoformat { /// Format rust code before the formatting the rsx macros #[clap(long)] - pub(crate) all_code: bool, + pub all_code: bool, /// Run in 'check' mode. Exits with 0 if input is formatted correctly. Exits /// with 1 and prints a diff if formatting is required. #[clap(short, long)] - pub(crate) check: bool, + pub check: bool, /// Input rsx (selection) #[clap(short, long)] - pub(crate) raw: Option, + pub raw: Option, /// Input file #[clap(short, long)] - pub(crate) file: Option, + pub file: Option, /// Split attributes in lines or not #[clap(short, long, default_value = "false")] - pub(crate) split_line_attributes: bool, + pub split_line_attributes: bool, /// The package to build #[clap(short, long)] - pub(crate) package: Option, + pub package: Option, } impl Autoformat { - pub(crate) fn autoformat(self) -> Result<()> { + pub fn autoformat(self) -> Result<()> { let Autoformat { check, raw, diff --git a/packages/cli/src/cli/build.rs b/packages/cli/src/cli/build.rs index 2dd17c3173..f8e96dc71a 100644 --- a/packages/cli/src/cli/build.rs +++ b/packages/cli/src/cli/build.rs @@ -1,155 +1,107 @@ -use super::*; -use crate::builder::{Builder, Platform}; -use crate::dioxus_crate::DioxusCrate; -use anyhow::Context; use std::str::FromStr; +use anyhow::Context; +use dioxus_cli_config::Platform; + +use crate::{ + builder::{BuildRequest, TargetPlatform}, + dioxus_crate::DioxusCrate, +}; + +use super::*; + +/// Information about the target to build +#[derive(Clone, Debug, Default, Deserialize, Parser)] +pub struct TargetArgs { + /// Build for nightly [default: false] + #[clap(long)] + pub nightly: bool, + + /// Build a example [default: ""] + #[clap(long)] + pub example: Option, + + /// Build a binary [default: ""] + #[clap(long)] + pub bin: Option, + + /// The package to build + #[clap(short, long)] + pub package: Option, + + /// Space separated list of features to activate + #[clap(long)] + pub features: Vec, + + /// The feature to use for the client in a fullstack app [default: "web"] + #[clap(long)] + pub client_feature: Option, + + /// The feature to use for the server in a fullstack app [default: "server"] + #[clap(long)] + pub server_feature: Option, + + /// Rustc platform triple + #[clap(long)] + pub target: Option, +} + /// Build the Rust Dioxus app and all of its assets. -/// -/// Produces a final output bundle designed to be run on the target platform. #[derive(Clone, Debug, Default, Deserialize, Parser)] #[clap(name = "build")] -pub(crate) struct BuildArgs { +pub struct Build { /// Build in release mode [default: false] #[clap(long, short)] #[serde(default)] - pub(crate) release: bool, + pub release: bool, /// This flag only applies to fullstack builds. By default fullstack builds will run with something in between debug and release mode. This flag will force the build to run in debug mode. [default: false] #[clap(long)] #[serde(default)] - pub(crate) force_debug: bool, + pub force_debug: bool, /// This flag only applies to fullstack builds. By default fullstack builds will run the server and client builds in parallel. This flag will force the build to run the server build first, then the client build. [default: false] #[clap(long)] #[serde(default)] - pub(crate) force_sequential: bool, - - /// Use verbose output [default: false] - #[clap(long)] - #[serde(default)] - pub(crate) verbose: bool, + pub force_sequential: bool, - /// Pass -Awarnings to the cargo build + // Use verbose output [default: false] #[clap(long)] #[serde(default)] - pub(crate) silent: bool, + pub verbose: bool, /// Build with custom profile #[clap(long)] - pub(crate) profile: Option, + pub profile: Option, /// Build platform: support Web & Desktop [default: "default_platform"] #[clap(long, value_enum)] - pub(crate) platform: Option, - - /// Build the fullstack variant of this app, using that as the fileserver and backend - /// - /// This defaults to `false` but will be overriden to true if the `fullstack` feature is enabled. - #[clap(long)] - pub(crate) fullstack: bool, - - /// Run the ssg config of the app and generate the files - #[clap(long)] - pub(crate) ssg: bool, + pub platform: Option, /// Skip collecting assets from dependencies [default: false] #[clap(long)] #[serde(default)] - pub(crate) skip_assets: bool, + pub skip_assets: bool, /// Extra arguments passed to cargo build #[clap(last = true)] - pub(crate) cargo_args: Vec, + pub cargo_args: Vec, /// Inject scripts to load the wasm and js files for your dioxus app if they are not already present [default: true] #[clap(long, default_value_t = true)] - pub(crate) inject_loading_scripts: bool, + pub inject_loading_scripts: bool, /// Information about the target to build #[clap(flatten)] - pub(crate) target_args: TargetArgs, -} - -/// Information about the target to build -#[derive(Clone, Debug, Default, Deserialize, Parser)] -pub(crate) struct TargetArgs { - /// Build for nightly [default: false] - #[clap(long)] - pub(crate) nightly: bool, - - /// Build a example [default: ""] - #[clap(long)] - pub(crate) example: Option, - - /// Build a binary [default: ""] - #[clap(long)] - pub(crate) bin: Option, - - /// The package to build - #[clap(short, long)] - pub(crate) package: Option, - - /// Space separated list of features to activate - #[clap(long)] - pub(crate) features: Vec, - - /// The feature to use for the client in a fullstack app [default: "web"] - #[clap(long)] - pub(crate) client_feature: Option, - - /// The feature to use for the server in a fullstack app [default: "server"] - #[clap(long)] - pub(crate) server_feature: Option, - - /// The architecture to build for [default: "native"] - /// - /// Can either be `arm | arm64 | x86 | x86_64 | native` - #[clap(long)] - pub(crate) arch: Option, - - /// Rustc platform triple - #[clap(long)] - pub(crate) target: Option, + pub target_args: TargetArgs, } -impl BuildArgs { - pub(crate) async fn run(mut self) -> anyhow::Result<()> { - let mut dioxus_crate = - DioxusCrate::new(&self.target_args).context("Failed to load Dioxus workspace")?; - - self.build(&mut dioxus_crate).await?; - - Ok(()) - } - - pub(crate) async fn build(&mut self, dioxus_crate: &mut DioxusCrate) -> Result<()> { - self.resolve(dioxus_crate)?; - - // todo: probably want to consume the logs from the builder here, instead of just waiting for it to finish - let bundles = Builder::start(dioxus_crate, self.clone())? - .wait_for_finish() - .await?; - - for bundle in bundles { - let destination = dioxus_crate.out_dir(); - bundle.finish(destination).await?; - } - - Ok(()) - } - - /// Update the arguments of the CLI by inspecting the DioxusCrate itself and learning about how - /// the user has configured their app. - /// - /// IE if they've specified "fullstack" as a feature on `dioxus`, then we want to build the - /// fullstack variant even if they omitted the `--fullstack` flag. - pub(crate) fn resolve(&mut self, dioxus_crate: &mut DioxusCrate) -> Result<()> { +impl Build { + pub fn resolve(&mut self, dioxus_crate: &mut DioxusCrate) -> Result<()> { // Inherit the platform from the defaults let platform = self .platform .unwrap_or_else(|| self.auto_detect_platform(dioxus_crate)); - self.platform = Some(platform); // Add any features required to turn on the platform we are building for @@ -160,20 +112,36 @@ impl BuildArgs { Ok(()) } + pub async fn build(&mut self, dioxus_crate: &mut DioxusCrate) -> Result<()> { + self.resolve(dioxus_crate)?; + let build_requests = BuildRequest::create(false, dioxus_crate, self.clone())?; + BuildRequest::build_all_parallel(build_requests).await?; + Ok(()) + } + + pub async fn run(&mut self) -> anyhow::Result<()> { + let mut dioxus_crate = + DioxusCrate::new(&self.target_args).context("Failed to load Dioxus workspace")?; + self.build(&mut dioxus_crate).await?; + Ok(()) + } + pub(crate) fn auto_detect_client_platform( &self, resolved: &DioxusCrate, - ) -> (Option, Platform) { + ) -> (Option, TargetPlatform) { self.find_dioxus_feature(resolved, |platform| { - matches!(platform, Platform::Web | Platform::Desktop) + matches!(platform, TargetPlatform::Web | TargetPlatform::Desktop) }) - .unwrap_or_else(|| (Some("web".to_string()), Platform::Web)) + .unwrap_or_else(|| (Some("web".to_string()), TargetPlatform::Web)) } pub(crate) fn auto_detect_server_feature(&self, resolved: &DioxusCrate) -> Option { - self.find_dioxus_feature(resolved, |platform| matches!(platform, Platform::Server)) - .map(|(feature, _)| feature) - .unwrap_or_else(|| Some("server".to_string())) + self.find_dioxus_feature(resolved, |platform| { + matches!(platform, TargetPlatform::Server) + }) + .map(|(feature, _)| feature) + .unwrap_or_else(|| Some("server".to_string())) } fn auto_detect_platform(&self, resolved: &DioxusCrate) -> Platform { @@ -237,7 +205,7 @@ impl BuildArgs { } /// Get the platform from the build arguments - pub(crate) fn platform(&self) -> Platform { + pub fn platform(&self) -> Platform { self.platform.unwrap_or_default() } } diff --git a/packages/cli/src/cli/bundle.rs b/packages/cli/src/cli/bundle.rs index 59f0fd10c2..c0470290d2 100644 --- a/packages/cli/src/cli/bundle.rs +++ b/packages/cli/src/cli/bundle.rs @@ -1,10 +1,11 @@ +use crate::build::Build; use crate::DioxusCrate; -use crate::{build::BuildArgs, bundle_utils::make_tauri_bundler_settings}; use anyhow::Context; use std::env::current_dir; use std::fs::create_dir_all; +use std::ops::Deref; use std::str::FromStr; -use tauri_bundler::{PackageSettings, SettingsBuilder}; +use tauri_bundler::{BundleSettings, PackageSettings, SettingsBuilder}; use super::*; @@ -15,14 +16,21 @@ pub struct Bundle { /// The package types to bundle #[clap(long)] pub packages: Option>, - /// The arguments for the dioxus build #[clap(flatten)] - pub(crate) build_arguments: BuildArgs, + pub build_arguments: Build, +} + +impl Deref for Bundle { + type Target = Build; + + fn deref(&self) -> &Self::Target { + &self.build_arguments + } } #[derive(Clone, Copy, Debug)] -pub(crate) enum PackageType { +pub enum PackageType { MacOsBundle, IosBundle, WindowsMsi, @@ -45,7 +53,6 @@ impl FromStr for PackageType { "rpm" => Ok(PackageType::Rpm), "appimage" => Ok(PackageType::AppImage), "dmg" => Ok(PackageType::Dmg), - "updater" => Ok(PackageType::Updater), _ => Err(format!("{} is not a valid package type", s)), } } @@ -67,7 +74,7 @@ impl From for tauri_bundler::PackageType { } impl Bundle { - pub(crate) async fn bundle(mut self) -> anyhow::Result<()> { + pub async fn bundle(mut self) -> anyhow::Result<()> { let mut dioxus_crate = DioxusCrate::new(&self.build_arguments.target_args) .context("Failed to load Dioxus workspace")?; @@ -90,9 +97,7 @@ impl Bundle { .set_src_path(Some(dioxus_crate.workspace_dir().display().to_string())), ]; - let bundle_config = dioxus_crate.dioxus_config.bundle.clone(); - let mut bundle_settings = make_tauri_bundler_settings(bundle_config); - + let mut bundle_settings: BundleSettings = dioxus_crate.dioxus_config.bundle.clone().into(); if cfg!(windows) { let windows_icon_override = dioxus_crate .dioxus_config @@ -124,7 +129,6 @@ impl Bundle { // Copy the assets in the dist directory to the bundle let static_asset_output_dir = &dioxus_crate.dioxus_config.application.out_dir; - // Make sure the dist directory is relative to the crate directory let static_asset_output_dir = static_asset_output_dir .strip_prefix(dioxus_crate.workspace_dir()) @@ -184,7 +188,7 @@ impl Bundle { settings = settings.package_types(packages.iter().map(|p| (*p).into()).collect()); } - if let Some(target) = &self.build_arguments.target_args.target { + if let Some(target) = &self.target_args.target { settings = settings.target(target.to_string()); } @@ -194,7 +198,7 @@ impl Bundle { #[cfg(target_os = "macos")] std::env::set_var("CI", "true"); - tauri_bundler::bundle::bundle_project(&settings.unwrap()).unwrap_or_else(|err|{ + tauri_bundler::bundle::bundle_project(settings.unwrap()).unwrap_or_else(|err|{ #[cfg(target_os = "macos")] panic!("Failed to bundle project: {:#?}\nMake sure you have automation enabled in your terminal (https://github.com/tauri-apps/tauri/issues/3055#issuecomment-1624389208) and full disk access enabled for your terminal (https://github.com/tauri-apps/tauri/issues/3055#issuecomment-1624389208)", err); #[cfg(not(target_os = "macos"))] diff --git a/packages/cli/src/cli/check.rs b/packages/cli/src/cli/check.rs index 8901abe956..84374184df 100644 --- a/packages/cli/src/cli/check.rs +++ b/packages/cli/src/cli/check.rs @@ -11,19 +11,19 @@ use super::*; /// Check the Rust files in the project for issues. #[derive(Clone, Debug, Parser)] -pub(crate) struct Check { +pub struct Check { /// Input file #[clap(short, long)] - pub(crate) file: Option, + pub file: Option, /// Information about the target to check #[clap(flatten)] - pub(crate) target_args: TargetArgs, + pub target_args: TargetArgs, } impl Check { // Todo: check the entire crate - pub(crate) async fn check(self) -> Result<()> { + pub async fn check(self) -> Result<()> { match self.file { // Default to checking the project None => { diff --git a/packages/cli/src/cli/clean.rs b/packages/cli/src/cli/clean.rs index 25ebfb6d68..2ea627b6c7 100644 --- a/packages/cli/src/cli/clean.rs +++ b/packages/cli/src/cli/clean.rs @@ -7,10 +7,10 @@ use super::*; /// Clean build artifacts. #[derive(Clone, Debug, Parser)] #[clap(name = "clean")] -pub(crate) struct Clean {} +pub struct Clean {} impl Clean { - pub(crate) fn clean(self) -> anyhow::Result<()> { + pub fn clean(self) -> anyhow::Result<()> { let dioxus_crate = DioxusCrate::new(&TargetArgs::default()).context("Failed to load Dioxus workspace")?; diff --git a/packages/cli/src/cli/config.rs b/packages/cli/src/cli/config.rs index 6292f16388..a1d9916e69 100644 --- a/packages/cli/src/cli/config.rs +++ b/packages/cli/src/cli/config.rs @@ -7,7 +7,7 @@ use super::*; /// Dioxus config file controls #[derive(Clone, Debug, Deserialize, Subcommand)] #[clap(name = "config")] -pub(crate) enum Config { +pub enum Config { /// Init `Dioxus.toml` for project/folder. Init { /// Init project name @@ -22,10 +22,8 @@ pub(crate) enum Config { #[clap(long, default_value = "web")] platform: String, }, - /// Format print Dioxus config. FormatPrint {}, - /// Create a custom html file. CustomHtml {}, @@ -38,7 +36,7 @@ pub(crate) enum Config { } #[derive(Debug, Clone, Copy, Deserialize, Subcommand)] -pub(crate) enum Setting { +pub enum Setting { /// Set the value of the always-hot-reload setting. AlwaysHotReload { value: BoolValue }, /// Set the value of the always-open-browser setting. @@ -63,7 +61,7 @@ impl Display for Setting { // Clap complains if we use a bool directly and I can't find much info about it. // "Argument 'value` is positional and it must take a value but action is SetTrue" #[derive(Debug, Clone, Copy, Deserialize, clap::ValueEnum)] -pub(crate) enum BoolValue { +pub enum BoolValue { True, False, } @@ -78,7 +76,7 @@ impl From for bool { } impl Config { - pub(crate) fn config(self) -> Result<()> { + pub fn config(self) -> Result<()> { let crate_root = crate_root()?; match self { Config::Init { diff --git a/packages/cli/src/cli/create.rs b/packages/cli/src/cli/create.rs index e0cce2af47..eaac213952 100644 --- a/packages/cli/src/cli/create.rs +++ b/packages/cli/src/cli/create.rs @@ -8,7 +8,7 @@ pub(crate) static DEFAULT_TEMPLATE: &str = "gh:dioxuslabs/dioxus-template"; #[derive(Clone, Debug, Default, Deserialize, Parser)] #[clap(name = "new")] -pub(crate) struct Create { +pub struct Create { /// Project name (required when `--yes` is used) name: Option, @@ -39,7 +39,7 @@ pub(crate) struct Create { } impl Create { - pub(crate) fn create(mut self) -> Result<()> { + pub fn create(mut self) -> Result<()> { let metadata = cargo_metadata::MetadataCommand::new().exec().ok(); // If we're getting pass a `.` name, that's actually a path @@ -112,7 +112,7 @@ impl Create { /// Post-creation actions for newly setup crates. // Also used by `init`. -pub(crate) fn post_create(path: &Path, metadata: Option) -> Result<()> { +pub fn post_create(path: &Path, metadata: Option) -> Result<()> { // 1. Add the new project to the workspace, if it exists. // This must be executed first in order to run `cargo fmt` on the new project. metadata.and_then(|metadata| { diff --git a/packages/cli/src/cli/doctor.rs b/packages/cli/src/cli/doctor.rs deleted file mode 100644 index 7c89c076d4..0000000000 --- a/packages/cli/src/cli/doctor.rs +++ /dev/null @@ -1,10 +0,0 @@ -use clap::Parser; - -#[derive(Clone, Debug, Parser)] -pub struct Doctor {} - -impl Doctor { - pub async fn run(self) -> anyhow::Result<()> { - Ok(()) - } -} diff --git a/packages/cli/src/cli/httpserver.rs b/packages/cli/src/cli/httpserver.rs deleted file mode 100644 index a488e851f1..0000000000 --- a/packages/cli/src/cli/httpserver.rs +++ /dev/null @@ -1,12 +0,0 @@ -use super::*; - -/// Translate some source file into Dioxus code -#[derive(Clone, Debug, Parser)] -#[clap(name = "http-server")] -pub(crate) struct Httpserver {} - -impl Httpserver { - pub(crate) async fn serve(self) -> Result<()> { - todo!() - } -} diff --git a/packages/cli/src/cli/init.rs b/packages/cli/src/cli/init.rs index f57d1eeaf5..01e49b69c7 100644 --- a/packages/cli/src/cli/init.rs +++ b/packages/cli/src/cli/init.rs @@ -4,7 +4,7 @@ use cargo_generate::{GenerateArgs, TemplatePath}; #[derive(Clone, Debug, Default, Deserialize, Parser)] #[clap(name = "init")] -pub(crate) struct Init { +pub struct Init { /// Template path #[clap(default_value = DEFAULT_TEMPLATE, short, long)] template: String, @@ -24,21 +24,19 @@ pub(crate) struct Init { } impl Init { - pub(crate) fn init(self) -> Result<()> { + pub fn init(self) -> Result<()> { let metadata = cargo_metadata::MetadataCommand::new().exec().ok(); // Get directory name. let name = std::env::current_dir()? .file_name() .map(|f| f.to_str().unwrap().to_string()); - // https://github.com/console-rs/dialoguer/issues/294 ctrlc::set_handler(move || { let _ = console::Term::stdout().show_cursor(); std::process::exit(0); }) .expect("ctrlc::set_handler"); - let args = GenerateArgs { define: self.option, init: true, diff --git a/packages/cli/src/cli/link.rs b/packages/cli/src/cli/link.rs index 53501d9b96..730c5639e4 100644 --- a/packages/cli/src/cli/link.rs +++ b/packages/cli/src/cli/link.rs @@ -1,45 +1,39 @@ -use std::{env::current_dir, path::PathBuf}; - -use serde::{Deserialize, Serialize}; - -/// The env var that will be set by the linker intercept cmd to indicate that we should act as a linker -pub(crate) const LINK_OUTPUT_ENV_VAR: &str = "dx-magic-link-file"; - -/// Should we act as a linker? -/// -/// Just check if the magic env var is set -pub(crate) fn should_link() -> bool { - std::env::var(LINK_OUTPUT_ENV_VAR).is_ok() +use crate::{assets, error::Result}; +use clap::Parser; +use std::{fs, path::PathBuf}; + +#[derive(Clone, Debug, Parser)] +#[clap(name = "link", hide = true)] +pub struct LinkCommand { + // Allow us to accept any argument after `dx link` + #[clap(trailing_var_arg = true, allow_hyphen_values = true)] + pub args: Vec, } -#[derive(Serialize, Deserialize)] -pub(crate) struct InterceptedArgs { - pub(crate) work_dir: PathBuf, - pub(crate) args: Vec, -} +impl LinkCommand { + pub fn link(self) -> Result<()> { + let Some((link_args, object_files)) = manganis_cli_support::linker_intercept(self.args) + else { + tracing::warn!("Invalid linker arguments."); + return Ok(()); + }; + + // Parse object files, deserialize JSON, & create a file to propagate JSON. + let json = manganis_cli_support::get_json_from_object_files(object_files); + let parsed = serde_json::to_string(&json).unwrap(); -/// Write the incoming linker args to a file -/// -/// The file will be given by the dx-magic-link-arg env var itself, so we use -/// it both for determining if we should act as a linker and the for the file name itself. -/// -/// This will panic if it fails -/// -/// hmmmmmmmm tbh I'd rather just pass the object files back and do the parsing here, but the interface -/// is nicer to just bounce back the args and let the host do the parsing/canonicalization -pub(crate) fn dump_link_args() -> anyhow::Result<()> { - let output = std::env::var(LINK_OUTPUT_ENV_VAR).expect("Missing env var with target file"); + let out_dir = PathBuf::from(link_args.first().unwrap()); + fs::create_dir_all(&out_dir).unwrap(); - // get the args and then dump them to the file - let args: Vec<_> = std::env::args().collect(); - let escaped = serde_json::to_string(&InterceptedArgs { - args, - work_dir: current_dir().unwrap(), - }) - .expect("Failed to escape env args"); + let path = out_dir.join(assets::MG_JSON_OUT); + fs::write(path, parsed).unwrap(); - // write the file - std::fs::write(output, escaped).expect("Failed to write output file"); + Ok(()) + } - Ok(()) + /// We need to pass the subcommand name to Manganis so this + /// helps centralize where we set the subcommand "name". + pub fn command_name() -> String { + "link".to_string() + } } diff --git a/packages/cli/src/cli/mod.rs b/packages/cli/src/cli/mod.rs index 6477528825..4ce83e12a1 100644 --- a/packages/cli/src/cli/mod.rs +++ b/packages/cli/src/cli/mod.rs @@ -1,17 +1,14 @@ -pub(crate) mod autoformat; -pub(crate) mod build; -pub(crate) mod bundle; -pub(crate) mod check; -pub(crate) mod clean; -pub(crate) mod config; -pub(crate) mod create; -pub(crate) mod doctor; -pub(crate) mod httpserver; -pub(crate) mod init; -pub(crate) mod link; -pub(crate) mod run; -pub(crate) mod serve; -pub(crate) mod translate; +pub mod autoformat; +pub mod build; +pub mod bundle; +pub mod check; +pub mod clean; +pub mod config; +pub mod create; +pub mod init; +pub mod link; +pub mod serve; +pub mod translate; use crate::{custom_error, error::Result, Error}; use clap::{Parser, Subcommand}; @@ -26,32 +23,40 @@ use std::{ process::{Command, Stdio}, }; +pub static VERSION: Lazy = Lazy::new(|| { + format!( + "{} ({})", + crate::dx_build_info::PKG_VERSION, + crate::dx_build_info::GIT_COMMIT_HASH_SHORT.unwrap_or("was built without git repository") + ) +}); + /// Build, Bundle & Ship Dioxus Apps. #[derive(Parser)] #[clap(name = "dioxus", version = VERSION.as_str())] -pub(crate) struct Cli { +pub struct Cli { #[clap(subcommand)] - pub(crate) action: Commands, + pub action: Commands, /// Enable verbose logging. #[clap(short)] - pub(crate) v: bool, + pub v: bool, /// Specify a binary target. #[clap(global = true, long)] - pub(crate) bin: Option, + pub bin: Option, } #[derive(Parser)] -pub(crate) enum Commands { +pub enum Commands { /// Build the Dioxus project and all of its assets. - Build(build::BuildArgs), + Build(build::Build), /// Translate a source file into Dioxus code. Translate(translate::Translate), /// Build, watch & serve the Dioxus project and all of its assets. - Serve(serve::ServeArgs), + Serve(serve::Serve), /// Create a new project for Dioxus. New(create::Create), @@ -74,21 +79,13 @@ pub(crate) enum Commands { #[clap(name = "check")] Check(check::Check), - /// Start a local http server, akin to a default fullstack app - #[clap(name = "http-server")] - HttpServer(httpserver::Httpserver), - - /// Run the project without any hotreloading - #[clap(name = "run")] - Run(run::RunArgs), - - /// Ensure all the tooling is installed and configured correctly - #[clap(name = "doctor")] - Doctor(doctor::Doctor), - /// Dioxus config file controls. #[clap(subcommand)] Config(config::Config), + + /// Handles parsing of linker arguments for linker-based systems + /// such as Manganis and binary patching. + Link(link::LinkCommand), } impl Display for Commands { @@ -104,17 +101,7 @@ impl Display for Commands { Commands::Autoformat(_) => write!(f, "fmt"), Commands::Check(_) => write!(f, "check"), Commands::Bundle(_) => write!(f, "bundle"), - Commands::HttpServer(_) => write!(f, "http-server"), - Commands::Run(_) => write!(f, "run"), - Commands::Doctor(_) => write!(f, "doctor"), + Commands::Link(_) => write!(f, "link"), } } } - -pub(crate) static VERSION: Lazy = Lazy::new(|| { - format!( - "{} ({})", - crate::build_info::PKG_VERSION, - crate::build_info::GIT_COMMIT_HASH_SHORT.unwrap_or("was built without git repository") - ) -}); diff --git a/packages/cli/src/cli/run.rs b/packages/cli/src/cli/run.rs deleted file mode 100644 index d3f678f7e5..0000000000 --- a/packages/cli/src/cli/run.rs +++ /dev/null @@ -1,60 +0,0 @@ -use crate::DioxusCrate; -use crate::{builder::Builder, serve::ServeUpdate}; -use anyhow::Context; -use build::BuildArgs; - -use super::*; - -/// Check the Rust files in the project for issues. -#[derive(Clone, Debug, Parser)] -pub(crate) struct RunArgs { - /// Information about the target to check - #[clap(flatten)] - pub(crate) build_args: BuildArgs, -} - -impl RunArgs { - pub(crate) async fn run(mut self) -> anyhow::Result<()> { - let mut dioxus_crate = DioxusCrate::new(&self.build_args.target_args) - .context("Failed to load Dioxus workspace")?; - - self.build_args.resolve(&mut dioxus_crate)?; - - let bundles = Builder::start(&mut dioxus_crate, self.build_args.clone())? - .wait_for_finish() - .await?; - - let mut runner = crate::serve::AppRunner::start(); - - let devserver_ip = "127.0.0.1:8080".parse().unwrap(); - let fullstack_ip = "127.0.0.1:6955".parse().unwrap(); - - for bundle in bundles { - runner - .open(bundle, devserver_ip, Some(fullstack_ip)) - .await?; - } - - loop { - let msg = runner.wait().await; - - match msg { - ServeUpdate::StderrReceived { platform, msg } => println!("[{platform}]: {msg}"), - ServeUpdate::StdoutReceived { platform, msg } => println!("[{platform}]: {msg}"), - ServeUpdate::ProcessExited { platform, status } => { - runner.kill(platform).await; - eprintln!("[{platform}]: process exited with status: {status:?}") - } - - ServeUpdate::TracingLog { log } => todo!(), - ServeUpdate::NewConnection => todo!(), - ServeUpdate::WsMessage(_) => todo!(), - ServeUpdate::BuildUpdate(_) => todo!(), - ServeUpdate::FilesChanged { files } => todo!(), - ServeUpdate::TuiInput { event } => todo!(), - } - } - - Ok(()) - } -} diff --git a/packages/cli/src/cli/serve.rs b/packages/cli/src/cli/serve.rs index 7d373ea960..a55e19414a 100644 --- a/packages/cli/src/cli/serve.rs +++ b/packages/cli/src/cli/serve.rs @@ -1,97 +1,96 @@ -use super::*; -use crate::settings; -use crate::DioxusCrate; -use crate::{builder::Platform, config::AddressArguments}; +use crate::{settings, tracer::CLILogControl, DioxusCrate}; use anyhow::Context; -use build::BuildArgs; -use crossterm::tty::IsTty; +use build::Build; +use dioxus_cli_config::AddressArguments; +use std::ops::Deref; -/// Run the WASM project on dev-server -#[derive(Clone, Debug, Default, Parser)] -#[command(group = clap::ArgGroup::new("release-incompatible").multiple(true).conflicts_with("release"))] -#[clap(name = "serve")] -pub(crate) struct ServeArgs { +use super::*; + +/// Arguments for the serve command +#[derive(Clone, Debug, Parser, Default)] +pub struct ServeArguments { /// The arguments for the address the server will run on #[clap(flatten)] - pub(crate) address: AddressArguments, + pub address: AddressArguments, /// Open the app in the default browser [default: true - unless cli settings are set] #[arg(long, default_missing_value="true", num_args=0..=1)] - pub(crate) open: Option, + pub open: Option, /// Enable full hot reloading for the app [default: true - unless cli settings are set] #[clap(long, group = "release-incompatible")] - pub(crate) hot_reload: Option, + pub hot_reload: Option, /// Configure always-on-top for desktop apps [default: true - unless cli settings are set] #[clap(long, default_missing_value = "true")] - pub(crate) always_on_top: Option, + pub always_on_top: Option, /// Set cross-origin-policy to same-origin [default: false] #[clap(name = "cross-origin-policy")] #[clap(long)] - pub(crate) cross_origin_policy: bool, + pub cross_origin_policy: bool, /// Additional arguments to pass to the executable #[clap(long)] - pub(crate) args: Vec, + pub args: Vec, /// Sets the interval in seconds that the CLI will poll for file changes on WSL. #[clap(long, default_missing_value = "2")] - pub(crate) wsl_file_poll_interval: Option, - - /// Run the server in interactive mode - #[arg(long, default_missing_value="true", num_args=0..=1, short = 'i')] - pub(crate) interactive: Option, - - /// Arguments for the build itself - #[clap(flatten)] - pub(crate) build_arguments: BuildArgs, + pub wsl_file_poll_interval: Option, } -impl ServeArgs { - /// Start the tui, builder, etc by resolving the arguments and then running the actual top-level serve function - pub(crate) async fn serve(mut self) -> Result<()> { - let mut krate = DioxusCrate::new(&self.build_arguments.target_args) - .context("Failed to load Dioxus workspace")?; +/// Run the WASM project on dev-server +#[derive(Clone, Debug, Default, Parser)] +#[command(group = clap::ArgGroup::new("release-incompatible").multiple(true).conflicts_with("release"))] +#[clap(name = "serve")] +pub struct Serve { + /// Arguments for the serve command + #[clap(flatten)] + pub(crate) server_arguments: ServeArguments, - self.resolve(&mut krate)?; + /// Arguments for the dioxus build + #[clap(flatten)] + pub(crate) build_arguments: Build, - crate::serve::serve_all(self, krate).await - } + /// Run the server in interactive mode + #[arg(long, default_missing_value="true", num_args=0..=1, short = 'i')] + pub interactive: Option, +} +impl Serve { /// Resolve the serve arguments from the arguments or the config fn resolve(&mut self, crate_config: &mut DioxusCrate) -> Result<()> { // Set config settings. let settings = settings::CliSettings::load(); // Enable hot reload. - if self.hot_reload.is_none() { - self.hot_reload = Some(settings.always_hot_reload.unwrap_or(true)); + if self.server_arguments.hot_reload.is_none() { + self.server_arguments.hot_reload = Some(settings.always_hot_reload.unwrap_or(true)); } // Open browser. - if self.open.is_none() { - self.open = Some(settings.always_open_browser.unwrap_or_default()); + if self.server_arguments.open.is_none() { + self.server_arguments.open = Some(settings.always_open_browser.unwrap_or_default()); } // Set WSL file poll interval. - if self.wsl_file_poll_interval.is_none() { - self.wsl_file_poll_interval = Some(settings.wsl_file_poll_interval.unwrap_or(2)); + if self.server_arguments.wsl_file_poll_interval.is_none() { + self.server_arguments.wsl_file_poll_interval = + Some(settings.wsl_file_poll_interval.unwrap_or(2)); } // Set always-on-top for desktop. - if self.always_on_top.is_none() { - self.always_on_top = Some(settings.always_on_top.unwrap_or(true)) + if self.server_arguments.always_on_top.is_none() { + self.server_arguments.always_on_top = Some(settings.always_on_top.unwrap_or(true)) } - - crate_config.dioxus_config.desktop.always_on_top = self.always_on_top.unwrap_or(true); + crate_config.dioxus_config.desktop.always_on_top = + self.server_arguments.always_on_top.unwrap_or(true); // Resolve the build arguments self.build_arguments.resolve(crate_config)?; // Since this is a serve, adjust the outdir to be target/dx-dist/ - let mut dist_dir = crate_config.out_dir(); + let mut dist_dir = crate_config.workspace_dir().join("target").join("dx-dist"); if crate_config.target.is_example() { dist_dir = dist_dir.join("examples"); @@ -103,31 +102,19 @@ impl ServeArgs { Ok(()) } - pub(crate) fn should_hotreload(&self) -> bool { - self.hot_reload.unwrap_or(true) - } + pub async fn serve(mut self, log_control: CLILogControl) -> anyhow::Result<()> { + let mut dioxus_crate = DioxusCrate::new(&self.build_arguments.target_args) + .context("Failed to load Dioxus workspace")?; - pub(crate) fn build_args(&self) -> BuildArgs { - self.build_arguments.clone() - } + self.resolve(&mut dioxus_crate)?; - pub(crate) fn interactive_tty(&self) -> bool { - std::io::stdout().is_tty() && self.interactive.unwrap_or(true) - } - - pub(crate) fn should_proxy_build(&self) -> bool { - match self.build_arguments.platform() { - Platform::Server => true, - Platform::Liveview => true, - Platform::Web | Platform::Desktop | Platform::Ios | Platform::Android => { - self.build_arguments.fullstack - } - } + crate::serve::serve_all(self, dioxus_crate, log_control).await?; + Ok(()) } } -impl std::ops::Deref for ServeArgs { - type Target = BuildArgs; +impl Deref for Serve { + type Target = Build; fn deref(&self) -> &Self::Target { &self.build_arguments diff --git a/packages/cli/src/cli/translate.rs b/packages/cli/src/cli/translate.rs index cf87775496..9676f18a53 100644 --- a/packages/cli/src/cli/translate.rs +++ b/packages/cli/src/cli/translate.rs @@ -9,27 +9,27 @@ use super::*; /// Translate some source file into Dioxus code #[derive(Clone, Debug, Parser)] #[clap(name = "translate")] -pub(crate) struct Translate { +pub struct Translate { /// Activate debug mode // short and long flags (-d, --debug) will be deduced from the field's name #[clap(short, long)] - pub(crate) component: bool, + pub component: bool, /// Input file #[clap(short, long)] - pub(crate) file: Option, + pub file: Option, /// Input file #[clap(short, long)] - pub(crate) raw: Option, + pub raw: Option, /// Output file, stdout if not present #[arg(short, long)] - pub(crate) output: Option, + pub output: Option, } impl Translate { - pub(crate) fn translate(self) -> Result<()> { + pub fn translate(self) -> Result<()> { // Get the right input for the translation let contents = determine_input(self.file, self.raw)?; @@ -85,7 +85,7 @@ fn write_svg_section(out: &mut String, svgs: Vec) { for (idx, icon) in svgs.into_iter().enumerate() { let raw = dioxus_autofmt::write_block_out(&CallBody::new(TemplateBody::new(vec![icon]))).unwrap(); - out.push_str("\n\n pub(crate) fn icon_"); + out.push_str("\n\n pub fn icon_"); out.push_str(&idx.to_string()); out.push_str("() -> Element {\n rsx! {"); indent_and_write(&raw, 2, out); diff --git a/packages/cli/src/config.rs b/packages/cli/src/config.rs deleted file mode 100644 index 3a80412965..0000000000 --- a/packages/cli/src/config.rs +++ /dev/null @@ -1,18 +0,0 @@ -mod app; -mod bundle; -mod desktop; -mod dioxus_config; -mod platform; -mod serve; -mod web; - -pub(crate) use app::*; -pub(crate) use bundle::*; -pub(crate) use desktop::*; -pub(crate) use dioxus_config::*; -pub(crate) use serve::*; -pub(crate) use web::*; - -// mod fullstack; -// mod liveview; -// mod static_generation; diff --git a/packages/cli/src/config/app.rs b/packages/cli/src/config/app.rs deleted file mode 100644 index ba52f3ce3f..0000000000 --- a/packages/cli/src/config/app.rs +++ /dev/null @@ -1,37 +0,0 @@ -use crate::builder::Platform; -use serde::{Deserialize, Serialize}; -use std::path::PathBuf; - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub(crate) struct ApplicationConfig { - #[serde(default = "default_name")] - pub(crate) name: String, - - #[serde(default = "default_platform")] - pub(crate) default_platform: Platform, - - #[serde(default = "out_dir_default")] - pub(crate) out_dir: PathBuf, - - #[serde(default = "asset_dir_default")] - pub(crate) asset_dir: PathBuf, - - #[serde(default)] - pub(crate) sub_package: Option, -} - -pub(crate) fn default_name() -> String { - "dioxus-app".into() -} - -pub(crate) fn default_platform() -> Platform { - Platform::Web -} - -pub(crate) fn asset_dir_default() -> PathBuf { - PathBuf::from("assets") -} - -pub(crate) fn out_dir_default() -> PathBuf { - PathBuf::from("dist") -} diff --git a/packages/cli/src/config/bundle.rs b/packages/cli/src/config/bundle.rs deleted file mode 100644 index 2ac73dac7e..0000000000 --- a/packages/cli/src/config/bundle.rs +++ /dev/null @@ -1,105 +0,0 @@ -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; -use std::path::PathBuf; - -#[derive(Debug, Clone, Serialize, Deserialize, Default)] -pub(crate) struct BundleConfig { - pub(crate) identifier: Option, - pub(crate) publisher: Option, - pub(crate) icon: Option>, - pub(crate) resources: Option>, - pub(crate) copyright: Option, - pub(crate) category: Option, - pub(crate) short_description: Option, - pub(crate) long_description: Option, - pub(crate) external_bin: Option>, - pub(crate) deb: Option, - pub(crate) macos: Option, - pub(crate) windows: Option, -} - -#[derive(Debug, Clone, Serialize, Deserialize, Default)] -pub(crate) struct DebianSettings { - pub(crate) depends: Option>, - pub(crate) files: HashMap, - pub(crate) nsis: Option, -} - -#[derive(Debug, Clone, Serialize, Deserialize, Default)] -pub(crate) struct WixSettings { - pub(crate) language: Vec<(String, Option)>, - pub(crate) template: Option, - pub(crate) fragment_paths: Vec, - pub(crate) component_group_refs: Vec, - pub(crate) component_refs: Vec, - pub(crate) feature_group_refs: Vec, - pub(crate) feature_refs: Vec, - pub(crate) merge_refs: Vec, - pub(crate) skip_webview_install: bool, - pub(crate) license: Option, - pub(crate) enable_elevated_update_task: bool, - pub(crate) banner_path: Option, - pub(crate) dialog_image_path: Option, - pub(crate) fips_compliant: bool, -} - -#[derive(Debug, Clone, Serialize, Deserialize, Default)] -pub(crate) struct MacOsSettings { - pub(crate) frameworks: Option>, - pub(crate) minimum_system_version: Option, - pub(crate) license: Option, - pub(crate) exception_domain: Option, - pub(crate) signing_identity: Option, - pub(crate) provider_short_name: Option, - pub(crate) entitlements: Option, - pub(crate) info_plist_path: Option, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub(crate) struct WindowsSettings { - pub(crate) digest_algorithm: Option, - pub(crate) certificate_thumbprint: Option, - pub(crate) timestamp_url: Option, - pub(crate) tsp: bool, - pub(crate) wix: Option, - pub(crate) icon_path: Option, - pub(crate) webview_install_mode: WebviewInstallMode, - pub(crate) webview_fixed_runtime_path: Option, - pub(crate) allow_downgrades: bool, - pub(crate) nsis: Option, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub(crate) struct NsisSettings { - pub(crate) template: Option, - pub(crate) license: Option, - pub(crate) header_image: Option, - pub(crate) sidebar_image: Option, - pub(crate) installer_icon: Option, - pub(crate) install_mode: NSISInstallerMode, - pub(crate) languages: Option>, - pub(crate) custom_language_files: Option>, - pub(crate) display_language_selector: bool, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub(crate) enum NSISInstallerMode { - CurrentUser, - PerMachine, - Both, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub(crate) enum WebviewInstallMode { - Skip, - DownloadBootstrapper { silent: bool }, - EmbedBootstrapper { silent: bool }, - OfflineInstaller { silent: bool }, - FixedRuntime { path: PathBuf }, -} - -impl Default for WebviewInstallMode { - fn default() -> Self { - Self::OfflineInstaller { silent: false } - } -} diff --git a/packages/cli/src/config/desktop.rs b/packages/cli/src/config/desktop.rs deleted file mode 100644 index 13fb0918b9..0000000000 --- a/packages/cli/src/config/desktop.rs +++ /dev/null @@ -1,17 +0,0 @@ -use serde::{Deserialize, Serialize}; - -/// Represents configuration items for the desktop platform. -#[derive(Debug, Clone, Serialize, Deserialize)] -pub(crate) struct DesktopConfig { - /// Describes whether a debug-mode desktop app should be always-on-top. - #[serde(default)] - pub(crate) always_on_top: bool, -} - -impl Default for DesktopConfig { - fn default() -> Self { - Self { - always_on_top: true, - } - } -} diff --git a/packages/cli/src/config/dioxus_config.rs b/packages/cli/src/config/dioxus_config.rs deleted file mode 100644 index ec1bcbc66e..0000000000 --- a/packages/cli/src/config/dioxus_config.rs +++ /dev/null @@ -1,62 +0,0 @@ -use super::*; -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub(crate) struct DioxusConfig { - pub(crate) application: ApplicationConfig, - - #[serde(default)] - pub(crate) web: WebConfig, - - #[serde(default)] - pub(crate) desktop: DesktopConfig, - - #[serde(default)] - pub(crate) bundle: BundleConfig, -} - -impl Default for DioxusConfig { - fn default() -> Self { - let name = default_name(); - Self { - application: ApplicationConfig { - name: name.clone(), - default_platform: default_platform(), - out_dir: out_dir_default(), - asset_dir: asset_dir_default(), - - sub_package: None, - }, - web: WebConfig { - app: WebAppConfig { - title: default_title(), - base_path: None, - }, - proxy: vec![], - watcher: Default::default(), - resource: WebResourceConfig { - dev: WebDevResourceConfig { - style: vec![], - script: vec![], - }, - style: Some(vec![]), - script: Some(vec![]), - }, - https: WebHttpsConfig { - enabled: None, - mkcert: None, - key_path: None, - cert_path: None, - }, - pre_compress: true, - wasm_opt: Default::default(), - }, - desktop: DesktopConfig::default(), - bundle: BundleConfig { - identifier: Some(format!("io.github.{name}")), - publisher: Some(name), - ..Default::default() - }, - } - } -} diff --git a/packages/cli/src/config/platform.rs b/packages/cli/src/config/platform.rs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/packages/cli/src/config/serve.rs b/packages/cli/src/config/serve.rs deleted file mode 100644 index 8edc6e6c35..0000000000 --- a/packages/cli/src/config/serve.rs +++ /dev/null @@ -1,41 +0,0 @@ -#![allow(unused)] // lots of configs... - -use clap::Parser; -use std::net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4}; - -/// The arguments for the address the server will run on -#[derive(Clone, Debug, Parser)] -pub(crate) struct AddressArguments { - /// The port the server will run on - #[clap(long)] - #[clap(default_value_t = default_port())] - pub(crate) port: u16, - - /// The address the server will run on - #[clap(long, default_value_t = default_address())] - pub(crate) addr: std::net::IpAddr, -} - -impl Default for AddressArguments { - fn default() -> Self { - Self { - port: default_port(), - addr: default_address(), - } - } -} - -impl AddressArguments { - /// Get the address the server should run on - pub(crate) fn address(&self) -> SocketAddr { - SocketAddr::new(self.addr, self.port) - } -} - -fn default_port() -> u16 { - 8080 -} - -fn default_address() -> IpAddr { - IpAddr::V4(std::net::Ipv4Addr::new(127, 0, 0, 1)) -} diff --git a/packages/cli/src/config/web.rs b/packages/cli/src/config/web.rs deleted file mode 100644 index 489bfa07c8..0000000000 --- a/packages/cli/src/config/web.rs +++ /dev/null @@ -1,180 +0,0 @@ -use serde::{Deserialize, Serialize}; -use std::path::PathBuf; - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub(crate) struct WebConfig { - #[serde(default)] - pub(crate) app: WebAppConfig, - - #[serde(default)] - pub(crate) proxy: Vec, - - #[serde(default)] - pub(crate) watcher: WebWatcherConfig, - - #[serde(default)] - pub(crate) resource: WebResourceConfig, - - #[serde(default)] - pub(crate) https: WebHttpsConfig, - - /// Whether to enable pre-compression of assets and wasm during a web build in release mode - #[serde(default = "true_bool")] - pub(crate) pre_compress: bool, - - /// The wasm-opt configuration - #[serde(default)] - pub(crate) wasm_opt: WasmOptConfig, -} - -impl Default for WebConfig { - fn default() -> Self { - Self { - pre_compress: true_bool(), - app: Default::default(), - https: Default::default(), - wasm_opt: Default::default(), - proxy: Default::default(), - watcher: Default::default(), - resource: Default::default(), - } - } -} - -/// The wasm-opt configuration -#[derive(Debug, Clone, Serialize, Deserialize, Default)] -pub(crate) struct WasmOptConfig { - /// The wasm-opt level to use for release builds [default: s] - /// Options: - /// - z: optimize aggressively for size - /// - s: optimize for size - /// - 1: optimize for speed - /// - 2: optimize for more for speed - /// - 3: optimize for even more for speed - /// - 4: optimize aggressively for speed - #[serde(default)] - pub(crate) level: WasmOptLevel, - - /// Keep debug symbols in the wasm file - #[serde(default = "false_bool")] - pub(crate) debug: bool, -} - -/// The wasm-opt level to use for release web builds [default: 4] -#[derive(Default, Debug, Copy, Clone, Serialize, Deserialize)] -pub(crate) enum WasmOptLevel { - /// Optimize aggressively for size - #[serde(rename = "z")] - Z, - /// Optimize for size - #[serde(rename = "s")] - S, - /// Don't optimize - #[serde(rename = "0")] - Zero, - /// Optimize for speed - #[serde(rename = "1")] - One, - /// Optimize for more for speed - #[serde(rename = "2")] - Two, - /// Optimize for even more for speed - #[serde(rename = "3")] - Three, - /// Optimize aggressively for speed - #[serde(rename = "4")] - #[default] - Four, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub(crate) struct WebAppConfig { - #[serde(default = "default_title")] - pub(crate) title: String, - pub(crate) base_path: Option, -} - -impl WebAppConfig { - /// Get the normalized base path for the application with `/` trimmed from both ends. If the base path is not set, this will return `.`. - pub(crate) fn base_path(&self) -> &str { - match &self.base_path { - Some(path) => path.trim_matches('/'), - None => ".", - } - } -} - -impl Default for WebAppConfig { - fn default() -> Self { - Self { - title: default_title(), - base_path: None, - } - } -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub(crate) struct WebProxyConfig { - pub(crate) backend: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub(crate) struct WebWatcherConfig { - #[serde(default = "watch_path_default")] - pub(crate) watch_path: Vec, - - #[serde(default)] - pub(crate) reload_html: bool, - - #[serde(default = "true_bool")] - pub(crate) index_on_404: bool, -} - -impl Default for WebWatcherConfig { - fn default() -> Self { - Self { - watch_path: watch_path_default(), - reload_html: false, - index_on_404: true, - } - } -} - -fn watch_path_default() -> Vec { - vec![PathBuf::from("src"), PathBuf::from("examples")] -} - -#[derive(Default, Debug, Clone, Serialize, Deserialize)] -pub(crate) struct WebResourceConfig { - pub(crate) dev: WebDevResourceConfig, - pub(crate) style: Option>, - pub(crate) script: Option>, -} - -#[derive(Default, Debug, Clone, Serialize, Deserialize)] -pub(crate) struct WebDevResourceConfig { - #[serde(default)] - pub(crate) style: Vec, - #[serde(default)] - pub(crate) script: Vec, -} - -#[derive(Debug, Default, Clone, Serialize, Deserialize)] -pub(crate) struct WebHttpsConfig { - pub(crate) enabled: Option, - pub(crate) mkcert: Option, - pub(crate) key_path: Option, - pub(crate) cert_path: Option, -} - -fn true_bool() -> bool { - true -} - -fn false_bool() -> bool { - false -} - -pub(crate) fn default_title() -> String { - "dioxus | ⛺".into() -} diff --git a/packages/cli/src/dioxus_crate.rs b/packages/cli/src/dioxus_crate.rs index af4fcc4a2b..3ecadd2240 100644 --- a/packages/cli/src/dioxus_crate.rs +++ b/packages/cli/src/dioxus_crate.rs @@ -1,5 +1,5 @@ -use crate::builder::Platform; -use crate::{build::TargetArgs, config::DioxusConfig}; +use crate::build::TargetArgs; +use dioxus_cli_config::{DioxusConfig, Platform}; use krates::cm::Target; use krates::{cm::TargetKind, Cmd, Krates, NodeId}; use serde::{Deserialize, Serialize}; @@ -11,17 +11,135 @@ use std::{ use crate::metadata::CargoError; +/// Load the dioxus config from a path +fn load_dioxus_config( + krates: &Krates, + package: NodeId, +) -> Result, CrateConfigError> { + fn acquire_dioxus_toml(dir: &std::path::Path) -> Option { + ["Dioxus.toml", "dioxus.toml"] + .into_iter() + .map(|file| dir.join(file)) + .find(|path| path.is_file()) + } + + // Walk up from the cargo.toml to the root of the workspace looking for Dioxus.toml + let mut current_dir = krates[package] + .manifest_path + .parent() + .unwrap() + .as_std_path() + .to_path_buf() + .canonicalize()?; + let workspace_path = krates + .workspace_root() + .as_std_path() + .to_path_buf() + .canonicalize()?; + + let mut dioxus_conf_file = None; + while current_dir.starts_with(&workspace_path) { + // Try to find Dioxus.toml in the current directory + if let Some(new_config) = acquire_dioxus_toml(¤t_dir) { + dioxus_conf_file = Some(new_config.as_path().to_path_buf()); + break; + } + // If we can't find it, go up a directory + current_dir = current_dir + .parent() + .ok_or(CrateConfigError::CurrentPackageNotFound)? + .to_path_buf(); + } + + let Some(dioxus_conf_file) = dioxus_conf_file else { + return Ok(None); + }; + + let cfg = toml::from_str::(&std::fs::read_to_string(&dioxus_conf_file)?) + .map_err(|err| { + CrateConfigError::LoadDioxusConfig(LoadDioxusConfigError { + location: dioxus_conf_file.display().to_string(), + error: err.to_string(), + }) + }) + .map(Some); + match cfg { + Ok(Some(mut cfg)) => { + let name = cfg.application.name.clone(); + if cfg.bundle.identifier.is_none() { + cfg.bundle.identifier = Some(format!("io.github.{name}")); + } + if cfg.bundle.publisher.is_none() { + cfg.bundle.publisher = Some(name); + } + + Ok(Some(cfg)) + } + cfg => cfg, + } +} + +// Find the main package in the workspace +fn find_main_package(package: Option, krates: &Krates) -> Result { + let kid = match package { + Some(package) => { + let mut workspace_members = krates.workspace_members(); + workspace_members + .find_map(|node| { + if let krates::Node::Krate { id, krate, .. } = node { + if krate.name == package { + return Some(id); + } + } + None + }) + .ok_or_else(|| CrateConfigError::PackageNotFound(package.clone()))? + } + None => { + // Otherwise find the package that is the closest parent of the current directory + let current_dir = std::env::current_dir()?; + let current_dir = current_dir.as_path(); + // Go through each member and find the path that is a parent of the current directory + let mut closest_parent = None; + for member in krates.workspace_members() { + if let krates::Node::Krate { id, krate, .. } = member { + let member_path = krate.manifest_path.parent().unwrap(); + if let Ok(path) = current_dir.strip_prefix(member_path.as_std_path()) { + let len = path.components().count(); + match closest_parent { + Some((_, closest_parent_len)) => { + if len < closest_parent_len { + closest_parent = Some((id, len)); + } + } + None => { + closest_parent = Some((id, len)); + } + } + } + } + } + closest_parent + .map(|(id, _)| id) + .ok_or(CrateConfigError::CurrentPackageNotFound)? + } + }; + + let package = krates.nid_for_kid(kid).unwrap(); + Ok(package) +} + // Contains information about the crate we are currently in and the dioxus config for that crate #[derive(Clone)] -pub(crate) struct DioxusCrate { - pub(crate) krates: Arc, - pub(crate) package: NodeId, - pub(crate) dioxus_config: DioxusConfig, - pub(crate) target: Target, +pub struct DioxusCrate { + pub krates: Arc, + pub package: NodeId, + pub dioxus_config: DioxusConfig, + pub target: Target, } impl DioxusCrate { - pub(crate) fn new(target: &TargetArgs) -> Result { + pub fn new(target: &TargetArgs) -> Result { let mut cmd = Cmd::new(); cmd.features(target.features.clone()); let builder = krates::Builder::new(); @@ -61,70 +179,26 @@ impl DioxusCrate { /// Compose an asset directory. Represents the typical "public" directory /// with publicly available resources (configurable in the `Dioxus.toml`). - pub(crate) fn legacy_asset_dir(&self) -> PathBuf { + pub fn asset_dir(&self) -> PathBuf { self.crate_dir() .join(&self.dioxus_config.application.asset_dir) } - /// Get the list of files in the "legacy" asset directory - pub(crate) fn legacy_asset_dir_files(&self) -> Vec { - let mut files = vec![]; - - let Ok(read_dir) = self.legacy_asset_dir().read_dir() else { - return files; - }; - - for entry in read_dir { - if let Ok(entry) = entry { - files.push(entry.path()); - } - } - - files - } - - pub(crate) fn bundle_out_dir(&self) -> PathBuf { - todo!("bundle out dir") - } - /// Compose an out directory. Represents the typical "dist" directory that /// is "distributed" after building an application (configurable in the /// `Dioxus.toml`). - pub(crate) fn out_dir(&self) -> PathBuf { - let dir = self.workspace_dir().join("target").join("dx-dist"); - std::fs::create_dir_all(&dir).unwrap(); - dir - } - - /// Create a workdir for the given platform - /// This can be used as a temporary directory for the build, but in an observable way such that - /// you can see the files in the directory via `target` - pub(crate) fn workdir(&self, platform: Platform) -> PathBuf { - let plat_name = match platform { - Platform::Web => "web", - Platform::Desktop => "desktop", - Platform::Ios => "ios", - Platform::Android => "android", - Platform::Server => "server", - Platform::Liveview => "liveview", - }; - let dir = self - .workspace_dir() - .join("target") - .join("dx-workdir") - .join(self.dioxus_config.application.name.clone()) - .join(plat_name); - std::fs::create_dir_all(&dir).unwrap(); - dir + pub fn out_dir(&self) -> PathBuf { + self.workspace_dir() + .join(&self.dioxus_config.application.out_dir) } /// Get the workspace directory for the crate - pub(crate) fn workspace_dir(&self) -> PathBuf { + pub fn workspace_dir(&self) -> PathBuf { self.krates.workspace_root().as_std_path().to_path_buf() } /// Get the directory of the crate - pub(crate) fn crate_dir(&self) -> PathBuf { + pub fn crate_dir(&self) -> PathBuf { self.package() .manifest_path .parent() @@ -134,26 +208,26 @@ impl DioxusCrate { } /// Get the main source file of the target - pub(crate) fn main_source_file(&self) -> PathBuf { + pub fn main_source_file(&self) -> PathBuf { self.target.src_path.as_std_path().to_path_buf() } /// Get the package we are currently in - pub(crate) fn package(&self) -> &krates::cm::Package { + pub fn package(&self) -> &krates::cm::Package { &self.krates[self.package] } /// Get the name of the package we are compiling - pub(crate) fn executable_name(&self) -> &str { + pub fn executable_name(&self) -> &str { &self.target.name } /// Get the type of executable we are compiling - pub(crate) fn executable_type(&self) -> krates::cm::TargetKind { + pub fn executable_type(&self) -> krates::cm::TargetKind { self.target.kind[0].clone() } - pub(crate) fn features_for_platform(&mut self, platform: Platform) -> Vec { + pub fn features_for_platform(&mut self, platform: Platform) -> Vec { let package = self.package(); // Try to find the feature that activates the dioxus feature for the given platform let dioxus_feature = platform.feature_name(); @@ -177,21 +251,20 @@ impl DioxusCrate { feature.into_iter().collect() } - /// Check if assets should be pre_compressed. This will only be true in release mode if the user - /// has enabled pre_compress in the web config. - pub(crate) fn should_pre_compress_web_assets(&self, release: bool) -> bool { + /// Check if assets should be pre_compressed. This will only be true in release mode if the user has enabled pre_compress in the web config. + pub fn should_pre_compress_web_assets(&self, release: bool) -> bool { self.dioxus_config.web.pre_compress && release } } #[derive(Debug, Clone, Serialize, Deserialize)] -pub(crate) struct Executable { - pub(crate) name: String, - pub(crate) ty: ExecutableType, +pub struct Executable { + pub name: String, + pub ty: ExecutableType, } #[derive(Debug, Copy, Clone, Serialize, Deserialize)] -pub(crate) enum ExecutableType { +pub enum ExecutableType { Binary, Lib, Example, @@ -199,13 +272,13 @@ pub(crate) enum ExecutableType { impl ExecutableType { /// Get the name of the executable if it is a binary or an example. - pub(crate) fn executable(&self) -> bool { + pub fn executable(&self) -> bool { matches!(self, Self::Binary | Self::Example) } } #[derive(Debug, Clone, Serialize, Deserialize)] -pub(crate) struct LoadDioxusConfigError { +pub struct LoadDioxusConfigError { location: String, error: String, } @@ -220,7 +293,7 @@ impl std::error::Error for LoadDioxusConfigError {} #[derive(Debug)] #[non_exhaustive] -pub(crate) enum CrateConfigError { +pub enum CrateConfigError { Cargo(CargoError), Io(std::io::Error), Toml(toml::de::Error), @@ -279,122 +352,3 @@ impl Display for CrateConfigError { } impl std::error::Error for CrateConfigError {} - -/// Load the dioxus config from a path -fn load_dioxus_config( - krates: &Krates, - package: NodeId, -) -> Result, CrateConfigError> { - fn acquire_dioxus_toml(dir: &std::path::Path) -> Option { - ["Dioxus.toml", "dioxus.toml"] - .into_iter() - .map(|file| dir.join(file)) - .find(|path| path.is_file()) - } - - // Walk up from the cargo.toml to the root of the workspace looking for Dioxus.toml - let mut current_dir = krates[package] - .manifest_path - .parent() - .unwrap() - .as_std_path() - .to_path_buf() - .canonicalize()?; - - let workspace_path = krates - .workspace_root() - .as_std_path() - .to_path_buf() - .canonicalize()?; - - let mut dioxus_conf_file = None; - while current_dir.starts_with(&workspace_path) { - // Try to find Dioxus.toml in the current directory - if let Some(new_config) = acquire_dioxus_toml(¤t_dir) { - dioxus_conf_file = Some(new_config.as_path().to_path_buf()); - break; - } - // If we can't find it, go up a directory - current_dir = current_dir - .parent() - .ok_or(CrateConfigError::CurrentPackageNotFound)? - .to_path_buf(); - } - - let Some(dioxus_conf_file) = dioxus_conf_file else { - return Ok(None); - }; - - let cfg = toml::from_str::(&std::fs::read_to_string(&dioxus_conf_file)?) - .map_err(|err| { - CrateConfigError::LoadDioxusConfig(LoadDioxusConfigError { - location: dioxus_conf_file.display().to_string(), - error: err.to_string(), - }) - }) - .map(Some); - match cfg { - Ok(Some(mut cfg)) => { - let name = cfg.application.name.clone(); - if cfg.bundle.identifier.is_none() { - cfg.bundle.identifier = Some(format!("io.github.{name}")); - } - if cfg.bundle.publisher.is_none() { - cfg.bundle.publisher = Some(name); - } - - Ok(Some(cfg)) - } - cfg => cfg, - } -} - -// Find the main package in the workspace -fn find_main_package(package: Option, krates: &Krates) -> Result { - let kid = match package { - Some(package) => { - let mut workspace_members = krates.workspace_members(); - workspace_members - .find_map(|node| { - if let krates::Node::Krate { id, krate, .. } = node { - if krate.name == package { - return Some(id); - } - } - None - }) - .ok_or_else(|| CrateConfigError::PackageNotFound(package.clone()))? - } - None => { - // Otherwise find the package that is the closest parent of the current directory - let current_dir = std::env::current_dir()?; - let current_dir = current_dir.as_path(); - // Go through each member and find the path that is a parent of the current directory - let mut closest_parent = None; - for member in krates.workspace_members() { - if let krates::Node::Krate { id, krate, .. } = member { - let member_path = krate.manifest_path.parent().unwrap(); - if let Ok(path) = current_dir.strip_prefix(member_path.as_std_path()) { - let len = path.components().count(); - match closest_parent { - Some((_, closest_parent_len)) => { - if len < closest_parent_len { - closest_parent = Some((id, len)); - } - } - None => { - closest_parent = Some((id, len)); - } - } - } - } - } - closest_parent - .map(|(id, _)| id) - .ok_or(CrateConfigError::CurrentPackageNotFound)? - } - }; - - let package = krates.nid_for_kid(kid).unwrap(); - Ok(package) -} diff --git a/packages/cli/src/build_info.rs b/packages/cli/src/dx_build_info.rs similarity index 100% rename from packages/cli/src/build_info.rs rename to packages/cli/src/dx_build_info.rs diff --git a/packages/cli/src/error.rs b/packages/cli/src/error.rs index 04d1e9e2c7..6d37636eda 100644 --- a/packages/cli/src/error.rs +++ b/packages/cli/src/error.rs @@ -2,10 +2,10 @@ use thiserror::Error as ThisError; use crate::{metadata::CargoError, CrateConfigError, LoadDioxusConfigError}; -pub(crate) type Result = std::result::Result; +pub type Result = std::result::Result; #[derive(ThisError, Debug)] -pub(crate) enum Error { +pub enum Error { /// Used when errors need to propagate but are too unique to be typed #[error("{0}")] Unique(String), diff --git a/packages/cli/src/fastfs.rs b/packages/cli/src/fastfs.rs deleted file mode 100644 index a20a2981aa..0000000000 --- a/packages/cli/src/fastfs.rs +++ /dev/null @@ -1,126 +0,0 @@ -//! Methods for working with the filesystem that are faster than the std fs methods -//! Uses stuff like rayon, caching, and other optimizations -//! -//! Allows configuration in case you want to do some work while copying and allows you to track progress - -use std::{ - ffi::OsString, - path::{Path, PathBuf}, -}; - -use brotli::enc::BrotliEncoderParams; -use walkdir::WalkDir; - -pub fn copy_asset(src: &Path, dest: &Path) -> std::io::Result<()> { - if src.is_dir() { - copy_dir_to(src, dest, false)?; - } else { - std::fs::copy(src, dest)?; - } - - Ok(()) -} - -pub(crate) fn copy_dir_to( - src_dir: &Path, - dest_dir: &Path, - pre_compress: bool, -) -> std::io::Result<()> { - let entries = std::fs::read_dir(&src_dir)?; - let mut children: Vec>> = Vec::new(); - - for entry in entries.flatten() { - let entry_path = entry.path(); - let path_relative_to_src = entry_path.strip_prefix(&src_dir).unwrap(); - let output_file_location = dest_dir.join(path_relative_to_src); - children.push(std::thread::spawn(move || { - if entry.file_type()?.is_dir() { - // If the file is a directory, recursively copy it into the output directory - if let Err(err) = copy_dir_to(&entry_path, &output_file_location, pre_compress) { - tracing::error!( - "Failed to pre-compress directory {}: {}", - entry_path.display(), - err - ); - } - } else { - // Make sure the directory exists - std::fs::create_dir_all(output_file_location.parent().unwrap())?; - // Copy the file to the output directory - std::fs::copy(&entry_path, &output_file_location)?; - - // Then pre-compress the file if needed - if pre_compress { - if let Err(err) = pre_compress_file(&output_file_location) { - tracing::error!( - "Failed to pre-compress static assets {}: {}", - output_file_location.display(), - err - ); - } - // If pre-compression isn't enabled, we should remove the old compressed file if it exists - } else if let Some(compressed_path) = compressed_path(&output_file_location) { - _ = std::fs::remove_file(compressed_path); - } - } - - Ok(()) - })); - } - for child in children { - child.join().unwrap()?; - } - Ok(()) -} - -/// Get the path to the compressed version of a file -fn compressed_path(path: &Path) -> Option { - let new_extension = match path.extension() { - Some(ext) => { - if ext.to_string_lossy().to_lowercase().ends_with("br") { - return None; - } - let mut ext = ext.to_os_string(); - ext.push(".br"); - ext - } - None => OsString::from("br"), - }; - - Some(path.with_extension(new_extension)) -} - -/// pre-compress a file with brotli -pub(crate) fn pre_compress_file(path: &Path) -> std::io::Result<()> { - let Some(compressed_path) = compressed_path(path) else { - return Ok(()); - }; - - let file = std::fs::File::open(path)?; - let mut stream = std::io::BufReader::new(file); - let mut buffer = std::fs::File::create(compressed_path)?; - let params = BrotliEncoderParams::default(); - brotli::BrotliCompress(&mut stream, &mut buffer, ¶ms)?; - - Ok(()) -} - -/// pre-compress all files in a folder -pub(crate) fn pre_compress_folder(path: &Path, pre_compress: bool) -> std::io::Result<()> { - let walk_dir = WalkDir::new(path); - for entry in walk_dir.into_iter().filter_map(|e| e.ok()) { - let entry_path = entry.path(); - if entry_path.is_file() { - if pre_compress { - if let Err(err) = pre_compress_file(entry_path) { - tracing::error!("Failed to pre-compress file {entry_path:?}: {err}"); - } - } - // If pre-compression isn't enabled, we should remove the old compressed file if it exists - else if let Some(compressed_path) = compressed_path(entry_path) { - _ = std::fs::remove_file(compressed_path); - } - } - } - Ok(()) -} diff --git a/packages/cli/src/main.rs b/packages/cli/src/main.rs index b2602779f0..e2146a77d4 100644 --- a/packages/cli/src/main.rs +++ b/packages/cli/src/main.rs @@ -2,23 +2,17 @@ #![doc(html_logo_url = "https://avatars.githubusercontent.com/u/79236386")] #![doc(html_favicon_url = "https://avatars.githubusercontent.com/u/79236386")] -pub(crate) mod assets; -pub(crate) mod build_info; -pub(crate) mod builder; -pub(crate) mod bundle_utils; -pub(crate) mod bundler; -pub(crate) mod cli; -pub(crate) mod config; -pub(crate) mod dioxus_crate; -pub(crate) mod error; -pub(crate) mod fastfs; -pub(crate) mod metadata; -pub(crate) mod serve; -pub(crate) mod settings; -pub(crate) mod tooling; -pub(crate) mod tracer; - -pub(crate) use builder::Platform; +pub mod assets; +pub mod builder; +pub mod cli; +pub mod dioxus_crate; +pub mod dx_build_info; +pub mod error; +pub mod metadata; +pub mod serve; +pub mod settings; +pub mod tracer; + pub(crate) use cli::*; pub(crate) use dioxus_crate::*; pub(crate) use error::*; @@ -27,45 +21,67 @@ pub(crate) use tracer::{TraceMsg, TraceSrc}; use anyhow::Context; use clap::Parser; + use Commands::*; #[tokio::main] async fn main() -> anyhow::Result<()> { - // If we have a magic env var set, we want to operate as a linker instead. - if link::should_link() { - return link::dump_link_args(); - } + let args = Cli::parse(); - // Start the tracer so it captures logs from the build engine before we start the builder - crate::serve::TraceController::initialize(); + let log_control = tracer::build_tracing(); - match Cli::parse().action { + match args.action { Translate(opts) => opts .translate() - .context("⛔️ Translation of HTML into RSX failed:"), - - New(opts) => opts.create().context("🚫 Creating new project failed:"), - - Init(opts) => opts.init().context("🚫 Initializing a new project failed:"), - - Config(opts) => opts.config().context("🚫 Configuring new project failed:"), - - Autoformat(opts) => opts.autoformat().context("🚫 Error autoformatting RSX:"), - - Check(opts) => opts.check().await.context("🚫 Error checking RSX:"), - - Clean(opts) => opts.clean().context("🚫 Cleaning project failed:"), - - Build(opts) => opts.run().await.context("🚫 Building project failed:"), - - Serve(opts) => opts.serve().await.context("🚫 Serving project failed:"), - - Bundle(opts) => opts.bundle().await.context("🚫 Bundling project failed:"), - - Run(opts) => opts.run().await.context("🚫 Running project failed:"), - - HttpServer(opts) => opts.serve().await.context("🚫 Serving project failed:"), - - Doctor(opts) => opts.run().await.context("🚫 Checking project failed:"), + .context(error_wrapper("Translation of HTML into RSX failed")), + + New(opts) => opts + .create() + .context(error_wrapper("Creating new project failed")), + + Init(opts) => opts + .init() + .context(error_wrapper("Initializing a new project failed")), + + Config(opts) => opts + .config() + .context(error_wrapper("Configuring new project failed")), + + Autoformat(opts) => opts + .autoformat() + .context(error_wrapper("Error autoformatting RSX")), + + Check(opts) => opts + .check() + .await + .context(error_wrapper("Error checking RSX")), + + Link(opts) => opts + .link() + .context(error_wrapper("Error with linker passthrough")), + + Build(mut opts) => opts + .run() + .await + .context(error_wrapper("Building project failed")), + + Clean(opts) => opts + .clean() + .context(error_wrapper("Cleaning project failed")), + + Serve(opts) => opts + .serve(log_control) + .await + .context(error_wrapper("Serving project failed")), + + Bundle(opts) => opts + .bundle() + .await + .context(error_wrapper("Bundling project failed")), } } + +/// Simplifies error messages that use the same pattern. +fn error_wrapper(message: &str) -> String { + format!("🚫 {message}:") +} diff --git a/packages/cli/src/metadata.rs b/packages/cli/src/metadata.rs index f9e0726836..b9acbdf751 100644 --- a/packages/cli/src/metadata.rs +++ b/packages/cli/src/metadata.rs @@ -8,12 +8,12 @@ use std::{ }; #[derive(Debug, Clone)] -pub(crate) struct CargoError { +pub struct CargoError { msg: String, } impl CargoError { - pub(crate) fn new(msg: String) -> Self { + pub fn new(msg: String) -> Self { Self { msg } } } diff --git a/packages/cli/src/serve/builder.rs b/packages/cli/src/serve/builder.rs index 8b13789179..79028b67d6 100644 --- a/packages/cli/src/serve/builder.rs +++ b/packages/cli/src/serve/builder.rs @@ -1 +1,189 @@ +use crate::builder::BuildRequest; +use crate::builder::BuildResult; +use crate::builder::TargetPlatform; +use crate::builder::UpdateBuildProgress; +use crate::dioxus_crate::DioxusCrate; +use crate::serve::next_or_pending; +use crate::serve::Serve; +use crate::Result; +use futures_channel::mpsc::UnboundedReceiver; +use futures_util::future::OptionFuture; +use futures_util::stream::select_all; +use futures_util::StreamExt; +use std::process::Stdio; +use tokio::{ + process::{Child, Command}, + task::JoinHandle, +}; +/// A handle to ongoing builds and then the spawned tasks themselves +pub struct Builder { + /// The results of the build + build_results: Option>>>, + + /// The progress of the builds + build_progress: Vec<(TargetPlatform, UnboundedReceiver)>, + + /// The application we are building + config: DioxusCrate, + + /// The arguments for the build + serve: Serve, + + /// The children of the build process + pub children: Vec<(TargetPlatform, Child)>, +} + +impl Builder { + /// Create a new builder + pub fn new(config: &DioxusCrate, serve: &Serve) -> Self { + let serve = serve.clone(); + let config = config.clone(); + Self { + build_results: None, + build_progress: Vec::new(), + config: config.clone(), + serve, + children: Vec::new(), + } + } + + /// Start a new build - killing the current one if it exists + pub fn build(&mut self) -> Result<()> { + self.shutdown(); + let build_requests = + BuildRequest::create(true, &self.config, self.serve.build_arguments.clone())?; + + let mut set = tokio::task::JoinSet::new(); + + for build_request in build_requests { + let (mut tx, rx) = futures_channel::mpsc::unbounded(); + self.build_progress + .push((build_request.target_platform, rx)); + set.spawn(async move { + let res = build_request.build(tx.clone()).await; + + if let Err(err) = &res { + let _ = tx.start_send(UpdateBuildProgress { + stage: crate::builder::Stage::Finished, + update: crate::builder::UpdateStage::Failed(format!("{err}")), + }); + } + + res + }); + } + + self.build_results = Some(tokio::spawn(async move { + let mut all_results = Vec::new(); + while let Some(result) = set.join_next().await { + let res = result.map_err(|err| { + crate::Error::Unique(format!("Panic while building project: {err:?}")) + })??; + + all_results.push(res); + } + Ok(all_results) + })); + + Ok(()) + } + + /// Wait for any new updates to the builder - either it completed or gave us a message etc + pub async fn wait(&mut self) -> Result { + // Wait for build progress + let mut next = select_all( + self.build_progress + .iter_mut() + .map(|(platform, rx)| rx.map(move |update| (*platform, update))), + ); + let next = next_or_pending(next.next()); + + // The ongoing builds directly + let results: OptionFuture<_> = self.build_results.as_mut().into(); + let results = next_or_pending(results); + + // The process exits + let children_empty = self.children.is_empty(); + let process_exited = self + .children + .iter_mut() + .map(|(target, child)| Box::pin(async move { (*target, child.wait().await) })); + let process_exited = async move { + if children_empty { + return futures_util::future::pending().await; + } + futures_util::future::select_all(process_exited).await + }; + + // Wait for the next build result + tokio::select! { + build_results = results => { + self.build_results = None; + + // If we have a build result, bubble it up to the main loop + let build_results = build_results.map_err(|_| crate::Error::Unique("Build join failed".to_string()))??; + + Ok(BuilderUpdate::Ready { results: build_results }) + } + (platform, update) = next => { + // If we have a build progress, send it to the screen + Ok(BuilderUpdate::Progress { platform, update }) + } + ((target, exit_status), _, _) = process_exited => { + Ok(BuilderUpdate::ProcessExited { status: exit_status, target_platform: target }) + } + } + } + + /// Shutdown the current build process + pub(crate) fn shutdown(&mut self) { + for (_, mut child) in self.children.drain(..) { + // Gracefully shtudown the desktop app + // It might have a receiver to do some cleanup stuff + if let Some(pid) = child.id() { + // on unix, we can send a signal to the process to shut down + #[cfg(unix)] + { + _ = Command::new("kill") + .args(["-s", "TERM", &pid.to_string()]) + .stderr(Stdio::null()) + .stdout(Stdio::null()) + .spawn(); + } + + // on windows, use the `taskkill` command + #[cfg(windows)] + { + _ = Command::new("taskkill") + .args(["/F", "/PID", &pid.to_string()]) + .stderr(Stdio::null()) + .stdout(Stdio::null()) + .spawn(); + } + } + + // Todo: add a timeout here to kill the process if it doesn't shut down within a reasonable time + _ = child.start_kill(); + } + + if let Some(tasks) = self.build_results.take() { + tasks.abort(); + } + self.build_progress.clear(); + } +} + +pub enum BuilderUpdate { + Progress { + platform: TargetPlatform, + update: UpdateBuildProgress, + }, + Ready { + results: Vec, + }, + ProcessExited { + target_platform: TargetPlatform, + status: Result, + }, +} diff --git a/packages/cli/src/serve/console_widget.rs b/packages/cli/src/serve/console_widget.rs deleted file mode 100644 index 69766c4c87..0000000000 --- a/packages/cli/src/serve/console_widget.rs +++ /dev/null @@ -1,2 +0,0 @@ -/// Scrollable console widget -pub struct ConsoleWidget {} diff --git a/packages/cli/src/serve/detect.rs b/packages/cli/src/serve/detect.rs deleted file mode 100644 index 504731da14..0000000000 --- a/packages/cli/src/serve/detect.rs +++ /dev/null @@ -1,32 +0,0 @@ -/// Detects if `dx` is being ran in a WSL environment. -/// -/// We determine this based on whether the keyword `microsoft` or `wsl` is contained within the [`WSL_1`] or [`WSL_2`] files. -/// This may fail in the future as it isn't guaranteed by Microsoft. -/// See https://github.com/microsoft/WSL/issues/423#issuecomment-221627364 -pub(crate) fn is_wsl() -> bool { - const WSL_1: &str = "/proc/sys/kernel/osrelease"; - const WSL_2: &str = "/proc/version"; - const WSL_KEYWORDS: [&str; 2] = ["microsoft", "wsl"]; - - // Test 1st File - if let Ok(content) = std::fs::read_to_string(WSL_1) { - let lowercase = content.to_lowercase(); - for keyword in WSL_KEYWORDS { - if lowercase.contains(keyword) { - return true; - } - } - } - - // Test 2nd File - if let Ok(content) = std::fs::read_to_string(WSL_2) { - let lowercase = content.to_lowercase(); - for keyword in WSL_KEYWORDS { - if lowercase.contains(keyword) { - return true; - } - } - } - - false -} diff --git a/packages/cli/src/serve/handle.rs b/packages/cli/src/serve/handle.rs deleted file mode 100644 index 1703c127b5..0000000000 --- a/packages/cli/src/serve/handle.rs +++ /dev/null @@ -1,189 +0,0 @@ -use crate::{builder::Platform, bundler::AppBundle}; -use crate::{Result, TraceSrc}; -use std::{net::SocketAddr, path::PathBuf, process::Stdio}; -use tokio::{ - io::AsyncBufReadExt, - process::{Child, Command}, -}; -use tokio::{ - io::{BufReader, Lines}, - process::{ChildStderr, ChildStdout}, -}; -use uuid::Uuid; - -/// A handle to a running app -pub(crate) struct AppHandle { - pub(crate) _id: Uuid, - pub(crate) app: AppBundle, - pub(crate) executable: PathBuf, - pub(crate) child: Option, - pub(crate) stdout: Option>>, - pub(crate) stderr: Option>>, -} - -impl AppHandle { - pub async fn start( - app: AppBundle, - devserver_ip: SocketAddr, - fullstack_address: Option, - ) -> Result { - let platform = app.build.platform(); - let ip = devserver_ip.to_string(); - - if platform == Platform::Server || app.build.build.fullstack { - tracing::info!( - "Proxying fullstack server from port {:?}", - fullstack_address - ); - } - - // let work_dir = std::env::temp_dir(); - let work_dir = app.build.krate.out_dir().join("launch"); - std::fs::create_dir_all(&work_dir)?; - let executable = app.finish(work_dir).await?; - - let mut handle = AppHandle { - app, - executable, - _id: Uuid::new_v4(), - child: None, - stderr: None, - stdout: None, - }; - - match platform { - Platform::Web => { - tracing::info!(dx_src = ?TraceSrc::Dev, "Serving web app on http://{} 🎉", ip); - } - Platform::Desktop => { - tracing::info!(dx_src = ?TraceSrc::Dev, "Launching desktop app at {} 🎉", handle.executable.display()); - } - Platform::Server => { - if let Some(fullstack_address) = fullstack_address { - tracing::info!( - dx_src = ?TraceSrc::Dev, - "Launching fullstack server on http://{:?} 🎉", - fullstack_address - ); - } - } - Platform::Ios => {} - Platform::Android => {} - Platform::Liveview => { - if let Some(fullstack_address) = fullstack_address { - tracing::info!( - dx_src = ?TraceSrc::Dev, - "Launching liveview server on http://{:?} 🎉", - fullstack_address - ); - } - } - } - - // open the exe with some arguments/envvars/etc - // we're going to try and configure this binary from the environment, if we can - // - // web can't be configured like this, so instead, we'll need to plumb a meta tag into the - // index.html during dev - match handle.app.build.platform() { - Platform::Desktop | Platform::Server | Platform::Liveview => { - let mut cmd = Command::new(handle.executable.clone()); - cmd.env( - dioxus_runtime_config::FULLSTACK_ADDRESS_ENV, - fullstack_address - .as_ref() - .map(|addr| addr.to_string()) - .unwrap_or_else(|| "127.0.0.1:8080".to_string()), - ) - .env( - dioxus_runtime_config::IOS_DEVSERVER_ADDR_ENV, - format!("ws://{}/_dioxus", ip), - ) - .env( - dioxus_runtime_config::DEVSERVER_RAW_ADDR_ENV, - format!("ws://{}/_dioxus", ip), - ) - .env("CARGO_MANIFEST_DIR", handle.app.build.krate.crate_dir()) - .env( - "SIMCTL_CHILD_CARGO_MANIFEST_DIR", - handle.app.build.krate.crate_dir(), - ) - .stderr(Stdio::piped()) - .stdout(Stdio::piped()) - .kill_on_drop(true); - - let mut child = cmd.spawn()?; - let stdout = BufReader::new(child.stdout.take().unwrap()); - let stderr = BufReader::new(child.stderr.take().unwrap()); - handle.stdout = Some(stdout.lines()); - handle.stderr = Some(stderr.lines()); - handle.child = Some(child); - } - Platform::Web => {} - Platform::Ios => {} - Platform::Android => {} - } - - Ok(handle) - } - /// Update an asset in the running apps - /// - /// Might need to upload the asset to the simulator or overwrite it within the bundle - /// - /// Returns the name of the asset in the bundle if it exists - pub(crate) fn hotreload_asset(&self, path: &PathBuf) -> Option { - let resource = self.app.assets.assets.get(path).cloned()?; - - _ = self - .app - .assets - .copy_asset_to(&self.app.asset_dir(), path, false, false); - - Some(resource.bundled.into()) - } - - #[allow(unused)] - fn open_bundled_ios_app(&self, build: &AppBundle) -> std::io::Result> { - // command = "xcrun" - // args = [ - // "simctl", - // "install", - // "booted", - // "target/aarch64-apple-ios-sim/debug/bundle/ios/DioxusApp.app", - // ] - - // [tasks.run_ios_sim] - // args = ["simctl", "launch", "--console", "booted", "com.dioxuslabs"] - // command = "xcrun" - // dependencies = ["build_ios_sim", "install_ios_sim"] - - // [tasks.serve-sim] - // dependencies = ["build_ios_sim", "install_ios_sim", "run_ios_sim"] - - // APP_PATH="target/aarch64-apple-ios/debug/bundle/ios/DioxusApp.app" - - // # get the device id by jq-ing the json of the device list - // xcrun devicectl list devices --json-output target/deviceid.json - // DEVICE_UUID=$(jq -r '.result.devices[0].identifier' target/deviceid.json) - - // xcrun devicectl device install app --device "${DEVICE_UUID}" "${APP_PATH}" --json-output target/xcrun.json - - // # get the installation url by jq-ing the json of the device install - // INSTALLATION_URL=$(jq -r '.result.installedApplications[0].installationURL' target/xcrun.json) - - // # launch the app - // # todo: we can just background it immediately and then pick it up for loading its logs - // xcrun devicectl device process launch --device "${DEVICE_UUID}" "${INSTALLATION_URL}" - - // # # launch the app and put it in background - // # xcrun devicectl device process launch --no-activate --verbose --device "${DEVICE_UUID}" "${INSTALLATION_URL}" --json-output "${XCRUN_DEVICE_PROCESS_LAUNCH_LOG_DIR}" - - // # # Extract background PID of status app - // # STATUS_PID=$(jq -r '.result.process.processIdentifier' "${XCRUN_DEVICE_PROCESS_LAUNCH_LOG_DIR}") - // # "${GIT_ROOT}/scripts/wait-for-metro-port.sh" 2>&1 - - // # # now that metro is ready, resume the app from background - // # xcrun devicectl device process resume --device "${DEVICE_UUID}" --pid "${STATUS_PID}" > "${XCRUN_DEVICE_PROCESS_RESUME_LOG_DIR}" 2>&1 - todo!("Open mobile apps") - } -} diff --git a/packages/cli/src/serve/hot_reloading_file_map.rs b/packages/cli/src/serve/hot_reloading_file_map.rs index 8d1129c017..579b3966d5 100644 --- a/packages/cli/src/serve/hot_reloading_file_map.rs +++ b/packages/cli/src/serve/hot_reloading_file_map.rs @@ -1,37 +1,37 @@ use dioxus_core::internal::{HotReloadTemplateWithLocation, HotReloadedTemplate}; use dioxus_core_types::HotReloadingContext; use dioxus_rsx::CallBody; -use dioxus_rsx_hotreload::{diff_rsx, ChangedRsx, HotReloadResult}; +use dioxus_rsx_hotreload::{diff_rsx, ChangedRsx}; use krates::cm::MetadataCommand; use krates::Cmd; -pub(crate) use std::collections::HashMap; +pub use std::collections::HashMap; use std::{ffi::OsStr, path::PathBuf}; -pub(crate) use std::{fs, io, path::Path}; -pub(crate) use std::{fs::File, io::Read}; +pub use std::{fs, io, path::Path}; +pub use std::{fs::File, io::Read}; use syn::spanned::Spanned; -pub(crate) struct FileMap { - pub(crate) map: HashMap, +pub struct FileMap { + pub map: HashMap, /// Any errors that occurred while building the FileMap that were not fatal - pub(crate) errors: Vec, + pub errors: Vec, - pub(crate) in_workspace: HashMap>, + pub in_workspace: HashMap>, } /// A cached file that has been parsed /// /// We store the templates found in this file -pub(crate) struct CachedSynFile { - pub(crate) raw: String, - pub(crate) templates: HashMap, +pub struct CachedSynFile { + pub raw: String, + pub templates: HashMap, } impl FileMap { /// Create a new FileMap from a crate directory /// /// TODO: this should be created with a gitignore filter - pub(crate) fn create(path: PathBuf) -> io::Result { + pub fn create(path: PathBuf) -> io::Result { Self::create_with_filter::(path, |p| { // skip some stuff we know is large by default p.file_name() == Some(OsStr::new("target")) @@ -43,7 +43,7 @@ impl FileMap { /// /// Takes a filter that when returns true, the file will be filtered out (ie not tracked) /// Note that this is inverted from a typical .filter() method. - pub(crate) fn create_with_filter( + pub fn create_with_filter( crate_dir: PathBuf, mut filter: impl FnMut(&Path) -> bool, ) -> io::Result { @@ -63,7 +63,7 @@ impl FileMap { /// Start watching assets for changes /// /// This just diffs every file against itself and populates the tracked assets as it goes - pub(crate) fn load_assets(&mut self, crate_dir: &Path) { + pub fn load_assets(&mut self, crate_dir: &Path) { let keys = self.map.keys().cloned().collect::>(); for file in keys { _ = self.update_rsx::(file.as_path(), crate_dir); @@ -82,7 +82,7 @@ impl FileMap { } /// Try to update the rsx in a file - pub(crate) fn update_rsx( + pub fn update_rsx( &mut self, file_path: &Path, crate_dir: &Path, @@ -154,7 +154,7 @@ impl FileMap { let template_location = template_location(old_start, file); // Returns a list of templates that are hotreloadable - let hotreload_result = HotReloadResult::new::( + let hotreload_result = dioxus_rsx_hotreload::HotReloadResult::new::( &old_call_body.body, &new_call_body.body, template_location.clone(), @@ -219,7 +219,7 @@ impl FileMap { } } -pub(crate) fn template_location(old_start: proc_macro2::LineColumn, file: &Path) -> String { +pub fn template_location(old_start: proc_macro2::LineColumn, file: &Path) -> String { let line = old_start.line; let column = old_start.column + 1; @@ -233,7 +233,7 @@ pub(crate) fn template_location(old_start: proc_macro2::LineColumn, file: &Path) path + ":" + line.to_string().as_str() + ":" + column.to_string().as_str() } -pub(crate) fn format_template_name(name: &str, index: usize) -> String { +pub fn format_template_name(name: &str, index: usize) -> String { format!("{}:{}", name, index) } @@ -290,7 +290,7 @@ fn find_rs_files(root: PathBuf, filter: &mut impl FnMut(&Path) -> bool) -> FileM } #[derive(Debug)] -pub(crate) enum HotreloadError { +pub enum HotreloadError { Failure(io::Error), Parse, Notreloadable, diff --git a/packages/cli/src/serve/mod.rs b/packages/cli/src/serve/mod.rs index f188d661da..10e0868aef 100644 --- a/packages/cli/src/serve/mod.rs +++ b/packages/cli/src/serve/mod.rs @@ -1,242 +1,243 @@ -use crate::builder::{BuildUpdate, BuildUpdateProgress, Builder, Platform, Stage, UpdateStage}; -use crate::cli::serve::ServeArgs; -use crate::DioxusCrate; -use crate::Result; -use crate::TraceSrc; -use std::ops::ControlFlow; +use std::future::{poll_fn, Future, IntoFuture}; +use std::task::Poll; -mod console_widget; -mod detect; -mod handle; +use crate::cli::serve::Serve; +use crate::dioxus_crate::DioxusCrate; +use crate::tracer::CLILogControl; +use crate::Result; +use crate::{ + builder::{Stage, TargetPlatform, UpdateBuildProgress, UpdateStage}, + TraceSrc, +}; +use futures_util::FutureExt; +use tokio::task::yield_now; + +mod builder; mod hot_reloading_file_map; mod logs_tab; mod output; mod proxy; -mod runner; mod server; -mod tracer; -mod update; mod watcher; -pub(crate) use handle::*; -pub(crate) use output::*; -pub(crate) use runner::*; -pub(crate) use server::*; -pub(crate) use tracer::*; -pub(crate) use update::*; -pub(crate) use watcher::*; +use builder::*; +use output::*; +use server::*; +use watcher::*; -/// For *all* builds, the CLI spins up a dedicated webserver, file watcher, and build infrastructure to serve the project. +/// For *all* builds the CLI spins up a dedicated webserver, file watcher, and build infrastructure to serve the project. /// /// This includes web, desktop, mobile, fullstack, etc. /// /// Platform specifics: -/// ------------------- -/// - Web: we need to attach a filesystem server to our devtools webserver to serve the project. We -/// want to emulate GithubPages here since most folks are deploying there and expect things like -/// basepath to match. -/// - Desktop: We spin up the dev server but without a filesystem server. -/// - Mobile: Basically the same as desktop. +/// - Web: we need to attach a filesystem server to our devtools webserver to serve the project. We +/// want to emulate GithubPages here since most folks are deploying there and expect things like +/// basepath to match. +/// - Fullstack: We spin up the same dev server but in this case the fullstack server itself needs to +/// proxy all dev requests to our dev server +/// - Desktop: We spin up the dev server but without a filesystem server. +/// - Mobile: Basically the same as desktop. /// -/// When fullstack is enabled, we'll also build for the `server` target and then hotreload the server. -/// The "server" is special here since "fullstack" is functionaly just an addition to the regular client -/// setup. +/// Notes: +/// - All filesystem changes are tracked here +/// - We send all updates to connected websocket connections. Even desktop connects via the websocket +/// - Right now desktop compiles tokio-tungstenite to do the connection but we could in theory reuse +/// the websocket logic from the webview for thinner builds. /// /// Todos(Jon): -/// - I'd love to be able to configure the CLI while it's running so we can change settings on the fly. +/// - I'd love to be able to configure the CLI while it's running so we can change settingaon the fly. +/// This would require some light refactoring and potentially pulling in something like ratatui. +/// - Build a custom subscriber for logs by tools within this /// - Handle logs from the build engine separately? +/// - Consume logs from the wasm for web/fullstack /// - I want us to be able to detect a `server_fn` in the project and then upgrade from a static server /// to a dynamic one on the fly. -pub(crate) async fn serve_all(args: ServeArgs, krate: DioxusCrate) -> Result<()> { - let mut tracer = tracer::TraceController::start(); +pub async fn serve_all( + serve: Serve, + dioxus_crate: DioxusCrate, + log_control: CLILogControl, +) -> Result<()> { + // Start the screen first so we collect build logs. + let mut screen = Output::start(&serve, log_control).expect("Failed to open terminal logger"); + let mut builder = Builder::new(&dioxus_crate, &serve); - // Note that starting the builder will queue up a build immediately - let mut builder = Builder::start(&krate, args.build_args())?; - let mut screen = Output::start(&args).expect("Failed to open terminal logger"); - let mut devserver = DevServer::start(&args, &krate); - let mut watcher = Watcher::start(&args, &krate); - let mut runner = AppRunner::start(); + // Start the first build + builder.build()?; - loop { - // Draw the state of the server to the screen - screen.render(&args, &krate, &builder, &devserver, &watcher); + let mut server = Server::start(&serve, &dioxus_crate); + let mut watcher = Watcher::start(&serve, &dioxus_crate); - // And then wait for any updates before redrawing - let msg = tokio::select! { - msg = builder.wait() => ServeUpdate::BuildUpdate(msg), - msg = watcher.wait() => msg, - msg = devserver.wait() => msg, - msg = screen.wait() => msg, - msg = runner.wait() => msg, - msg = tracer.wait() => msg, - }; - - let res = handle_update( - msg, - &args, - &mut devserver, - &mut screen, - &mut builder, - &mut runner, - &mut watcher, - ); - - match res.await { - Ok(ControlFlow::Continue(())) => continue, - Ok(ControlFlow::Break(())) => {} - Err(e) => tracing::error!("Error in TUI: {}", e), - } + let is_hot_reload = serve.server_arguments.hot_reload.unwrap_or(true); - break; - } - - _ = devserver.shutdown().await; - _ = screen.shutdown(); - _ = builder.abort_all(); - _ = tracer.shutdown(); - - Ok(()) -} + loop { + // Make sure we don't hog the CPU: these loop { select! {} } blocks can starve the executor + yield_now().await; -async fn handle_update( - msg: ServeUpdate, - args: &ServeArgs, - devserver: &mut DevServer, - screen: &mut Output, - builder: &mut Builder, - runner: &mut AppRunner, - watcher: &mut Watcher, -) -> Result> { - match msg { - ServeUpdate::FilesChanged { files } => { - if files.is_empty() || !args.should_hotreload() { - return Ok(ControlFlow::Continue(())); - } + // Draw the state of the server to the screen + screen.render(&serve, &dioxus_crate, &builder, &server, &watcher); - // if change is hotreloadable, hotreload it - // and then send that update to all connected clients - if let Some(hr) = watcher.attempt_hot_reload(files, &runner) { - // Only send a hotreload message for templates and assets - otherwise we'll just get a full rebuild - if hr.templates.is_empty() && hr.assets.is_empty() { - return Ok(ControlFlow::Continue(())); + // And then wait for any updates before redrawing + tokio::select! { + // rebuild the project or hotreload it + _ = watcher.wait(), if is_hot_reload => { + if !watcher.pending_changes() { + continue } - devserver.send_hotreload(hr).await; - } else { - // We're going to kick off a new build, interrupting the current build if it's ongoing - builder.build(args.build_arguments.clone())?; - - // Clear the hot reload changes - watcher.clear_hot_reload_changes(); - - // Tell the server to show a loading page for any new requests - devserver.start_build().await; - } - } - - // Run the server in the background - // Waiting for updates here lets us tap into when clients are added/removed - ServeUpdate::NewConnection => { - if let Some(msg) = watcher.applied_hot_reload_changes() { - devserver.send_hotreload(msg).await; - } - } - - // Received a message from the devtools server - currently we only use this for - // logging, so we just forward it the tui - ServeUpdate::WsMessage(msg) => { - screen.new_ws_message(Platform::Web, msg); - } - - // Wait for logs from the build engine - // These will cause us to update the screen - // We also can check the status of the builds here in case we have multiple ongoing builds - ServeUpdate::BuildUpdate(BuildUpdate::Progress(update)) => { - let update_clone = update.clone(); - screen.new_build_logs(update.platform, update_clone); - devserver - .update_build_status(screen.build_progress.progress(), update.stage.to_string()) - .await; - - match update { - // Send rebuild start message. - BuildUpdateProgress { - stage: Stage::Compiling, - update: UpdateStage::Start, - platform: _, - } => devserver.send_reload_start().await, - - // Send rebuild failed message. - BuildUpdateProgress { - stage: Stage::Finished, - update: UpdateStage::Failed(_), - platform: _, - } => devserver.send_reload_failed().await, - - _ => {} + let changed_files = watcher.dequeue_changed_files(&dioxus_crate); + let changed = changed_files.first().cloned(); + + // if change is hotreloadable, hotreload it + // and then send that update to all connected clients + if let Some(hr) = watcher.attempt_hot_reload(&dioxus_crate, changed_files) { + // Only send a hotreload message for templates and assets - otherwise we'll just get a full rebuild + if hr.templates.is_empty() && hr.assets.is_empty() && hr.unknown_files.is_empty() { + continue + } + + if let Some(changed_path) = changed { + let path_relative = changed_path.strip_prefix(dioxus_crate.crate_dir()).map(|p| p.display().to_string()).unwrap_or_else(|_| changed_path.display().to_string()); + tracing::info!(dx_src = ?TraceSrc::Dev, "Hotreloaded {}", path_relative); + } + + server.send_hotreload(hr).await; + } else { + // If the change is not binary patchable, rebuild the project + // We're going to kick off a new build, interrupting the current build if it's ongoing + builder.build()?; + + // Clear the hot reload changes + watcher.clear_hot_reload_changes(); + + // Tell the server to show a loading page for any new requests + server.start_build().await; + } } - } - - ServeUpdate::BuildUpdate(BuildUpdate::BuildFailed { err, .. }) => { - devserver.send_build_error(err).await; - } - - ServeUpdate::BuildUpdate(BuildUpdate::BuildReady { target, result }) => { - tracing::info!(dx_src = ?TraceSrc::Dev, "Opening app for [{}]", target); - let handle = runner - .open(result, devserver.ip, devserver.fullstack_address()) - .await; - - match handle { - Ok(handle) => { - // Make sure we immediately capture the stdout/stderr of the executable - - // otherwise it'll clobber our terminal output - screen.new_ready_app(handle); - - // And then finally tell the server to reload - devserver.send_reload_command().await; + // reload the page + msg = server.wait() => { + // Run the server in the background + // Waiting for updates here lets us tap into when clients are added/removed + match msg { + Some(ServerUpdate::NewConnection) => { + if let Some(msg) = watcher.applied_hot_reload_changes() { + server.send_hotreload(msg).await; + } + } + Some(ServerUpdate::Message(msg)) => { + screen.new_ws_message(TargetPlatform::Web, msg); + } + None => {} } + } - Err(e) => { - tracing::error!("Failed to open app: {}", e); + // Handle updates from the build engine + application = builder.wait() => { + // Wait for logs from the build engine + // These will cause us to update the screen + // We also can check the status of the builds here in case we have multiple ongoing builds + match application { + Ok(BuilderUpdate::Progress { platform, update }) => { + let update_clone = update.clone(); + screen.new_build_progress(platform, update_clone); + server.update_build_status(screen.build_progress.progress(), update.stage.to_string()).await; + + match update { + // Send rebuild start message. + UpdateBuildProgress { stage: Stage::Compiling, update: UpdateStage::Start } => server.send_reload_start().await, + // Send rebuild failed message. + UpdateBuildProgress { stage: Stage::Finished, update: UpdateStage::Failed(_) } => server.send_reload_failed().await, + _ => {}, + } + } + Ok(BuilderUpdate::Ready { results }) => { + if !results.is_empty() { + builder.children.clear(); + } + + // If we have a build result, open it + for build_result in results.iter() { + let child = build_result.open(&serve.server_arguments, server.fullstack_address(), &dioxus_crate.workspace_dir()); + match child { + Ok(Some(child_proc)) => builder.children.push((build_result.target_platform, child_proc)), + Err(e) => { + tracing::error!(dx_src = ?TraceSrc::Build, "Failed to open build result: {e}"); + break; + }, + _ => {} + } + } + + // Make sure we immediately capture the stdout/stderr of the executable - + // otherwise it'll clobber our terminal output + screen.new_ready_app(&mut builder, results); + + // And then finally tell the server to reload + server.send_reload_command().await; + }, + + // If the process exited *cleanly*, we can exit + Ok(BuilderUpdate::ProcessExited { status, target_platform }) => { + // Then remove the child process + builder.children.retain(|(platform, _)| *platform != target_platform); + match status { + Ok(status) => { + if status.success() { + break; + } + else { + tracing::error!(dx_src = ?TraceSrc::Dev, "Application exited with status: {status}"); + } + }, + Err(e) => { + tracing::error!(dx_src = ?TraceSrc::Dev, "Application exited with error: {e}"); + } + } + } + Err(err) => { + server.send_build_error(err).await; + } } } - } - - // nothing - the builder just signals that there are no more pending builds - ServeUpdate::BuildUpdate(BuildUpdate::AllFinished) => {} - // If the process exited *cleanly*, we can exit - ServeUpdate::ProcessExited { status, platform } => { - if !status.success() { - tracing::error!(dx_src = ?TraceSrc::Dev, "Application [{platform}] exited with status: {status}"); - return Ok(ControlFlow::Break(())); + // Handle input from the user using our settings + res = screen.wait() => { + match res { + Ok(false) => {} + // Request a rebuild. + Ok(true) => { + builder.build()?; + server.start_build().await + }, + // Shutdown the server. + Err(_) => break, + } } - - runner.kill(platform).await; - } - - ServeUpdate::StdoutReceived { platform, msg } => { - screen.push_stdout(platform, msg); } + } - ServeUpdate::StderrReceived { platform, msg } => { - screen.push_stderr(platform, msg); - } + // Run our cleanup logic here - maybe printing as we go? + // todo: more printing, logging, error handling in this phase + _ = screen.shutdown(); + _ = server.shutdown().await; + builder.shutdown(); - ServeUpdate::TracingLog { log } => { - screen.push_inner_log(log); - } + Ok(()) +} - ServeUpdate::TuiInput { event } => { - let should_rebuild = screen.handle_input(event)?; - if should_rebuild { - builder.build(args.build_arguments.clone())?; - devserver.start_build().await - } +// Grab the output of a future that returns an option or wait forever +pub(crate) fn next_or_pending(f: F) -> impl Future +where + F: IntoFuture>, +{ + let pinned = f.into_future().fuse(); + let mut pinned = Box::pin(pinned); + poll_fn(move |cx| { + let next = pinned.as_mut().poll(cx); + match next { + Poll::Ready(Some(next)) => Poll::Ready(next), + _ => Poll::Pending, } - } - - Ok(ControlFlow::Continue(())) + }) + .fuse() } diff --git a/packages/cli/src/serve/output.rs b/packages/cli/src/serve/output.rs index 89feb9f468..75d4a13c3d 100644 --- a/packages/cli/src/serve/output.rs +++ b/packages/cli/src/serve/output.rs @@ -1,8 +1,10 @@ use crate::{ - builder::{BuildMessage, BuildUpdateProgress, Platform, Stage, UpdateStage}, - cli::serve::ServeArgs, + builder::{BuildResult, UpdateStage}, + builder::{Stage, TargetPlatform, UpdateBuildProgress}, dioxus_crate::DioxusCrate, - serve::{Builder, Watcher}, + serve::next_or_pending, + serve::Serve, + serve::{Builder, Server, Watcher}, tracer::CLILogControl, TraceMsg, TraceSrc, }; @@ -16,29 +18,24 @@ use crossterm::{ tty::IsTty, ExecutableCommand, }; -use dioxus_devtools_types::ClientMsg; -use futures_util::{ - future::{select_all, OptionFuture}, - Future, FutureExt, StreamExt, -}; -use ratatui::{ - prelude::*, - widgets::{Block, Borders, Paragraph}, - TerminalOptions, Viewport, -}; +use dioxus_cli_config::{AddressArguments, Platform}; +use dioxus_hot_reload::ClientMsg; +use futures_util::{future::select_all, Future, FutureExt, StreamExt}; +use ratatui::{prelude::*, TerminalOptions, Viewport}; use std::{ cell::RefCell, collections::{HashMap, HashSet}, - fmt::Display, io::{self, stdout}, rc::Rc, + sync::atomic::Ordering, time::{Duration, Instant}, }; - +use tokio::{ + io::{AsyncBufReadExt, BufReader, Lines}, + process::{ChildStderr, ChildStdout}, +}; use tracing::Level; -use super::{AppHandle, DevServer, ServeUpdate}; - mod render; // How many lines should be scroll on each mouse scroll or arrow key input. @@ -48,14 +45,38 @@ const SCROLL_MODIFIER: u16 = 4; // Scroll modifier key. const SCROLL_MODIFIER_KEY: KeyModifiers = KeyModifiers::SHIFT; +#[derive(Default)] +pub struct BuildProgress { + current_builds: HashMap, +} + +impl BuildProgress { + pub fn progress(&self) -> f64 { + self.current_builds + .values() + .min_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)) + .map(|build| match build.stage { + Stage::Initializing => 0.0, + Stage::InstallingWasmTooling => 0.0, + Stage::Compiling => build.progress, + Stage::OptimizingWasm | Stage::OptimizingAssets | Stage::Finished => 1.0, + }) + .unwrap_or_default() + } +} + pub struct Output { term: Rc>>, + log_control: CLILogControl, + + // optional since when there's no tty there's no eventstream to read from - just stdin events: Option, pub(crate) build_progress: BuildProgress, + running_apps: HashMap, // A list of all messages from build, dev, app, and more. - messages: Vec, + messages: Vec, num_lines_wrapping: u16, scroll_position: u16, @@ -66,7 +87,9 @@ pub struct Output { anim_start: Instant, interactive: bool, + _is_cli_release: bool, platform: Platform, + addr: AddressArguments, // Filters show_filter_menu: bool, @@ -75,17 +98,21 @@ pub struct Output { filter_search_mode: bool, filter_search_input: Option, - dx_version: String, + _rustc_version: String, + _rustc_nightly: bool, + _dx_version: String, } type TerminalBackend = Terminal>; impl Output { - pub(crate) fn start(cfg: &ServeArgs) -> io::Result { - let interactive = cfg.interactive_tty(); + pub fn start(cfg: &Serve, log_control: CLILogControl) -> io::Result { + let interactive = std::io::stdout().is_tty() && cfg.interactive.unwrap_or(true); + let mut events = None; + if interactive { - // log_control.output_enabled.store(true, Ordering::SeqCst); + log_control.output_enabled.store(true, Ordering::SeqCst); enable_raw_mode()?; stdout().execute(EnterAlternateScreen)?.execute(Hide)?; @@ -109,9 +136,20 @@ impl Output { ) .ok(); - let mut dx_version = format!("{}", env!("CARGO_PKG_VERSION")); - if crate::build_info::PROFILE != "release" { - if let Some(hash) = crate::build_info::GIT_COMMIT_HASH_SHORT { + // todo: re-enable rustc version + // let rustc_version = rustc_version().await; + // let rustc_nightly = rustc_version.contains("nightly") || cfg.target_args.nightly; + let _rustc_version = String::from("1.0.0"); + let _rustc_nightly = false; + + let mut dx_version = String::new(); + + dx_version.push_str(env!("CARGO_PKG_VERSION")); + + // todo: we want the binstalls / cargo installs to be exempt, but installs from git are not + let is_cli_release = crate::dx_build_info::PROFILE == "release"; + if !is_cli_release { + if let Some(hash) = crate::dx_build_info::GIT_COMMIT_HASH_SHORT { let hash = &hash.trim_start_matches('g')[..4]; dx_version.push('-'); dx_version.push_str(hash); @@ -122,19 +160,24 @@ impl Output { Ok(Self { term: Rc::new(RefCell::new(term)), + log_control, events, - - dx_version, + _rustc_version, + _rustc_nightly, + _dx_version: dx_version, interactive, + _is_cli_release: is_cli_release, platform, messages: Vec::new(), more_modal_open: false, build_progress: Default::default(), + running_apps: HashMap::new(), scroll_position: 0, num_lines_wrapping: 0, console_width: 0, console_height: 0, anim_start: Instant::now(), + addr: cfg.server_arguments.address.clone(), // Filter show_filter_menu: false, @@ -146,12 +189,21 @@ impl Output { } /// Add a message from stderr to the logs - pub fn push_stderr(&mut self, platform: Platform, stderr: String) { - self.messages.push(ConsoleMessage::Log(TraceMsg { + fn push_stderr(&mut self, platform: TargetPlatform, stderr: String) { + self.running_apps + .get_mut(&platform) + .unwrap() + .output + .as_mut() + .unwrap() + .stderr_line + .push_str(&stderr); + + self.messages.push(TraceMsg { source: TraceSrc::App(platform), level: Level::ERROR, content: stderr, - })); + }); if self.is_snapped() { self.scroll_to_bottom(); @@ -159,12 +211,21 @@ impl Output { } /// Add a message from stdout to the logs - pub fn push_stdout(&mut self, platform: Platform, stdout: String) { - self.messages.push(ConsoleMessage::Log(TraceMsg { + fn push_stdout(&mut self, platform: TargetPlatform, stdout: String) { + self.running_apps + .get_mut(&platform) + .unwrap() + .output + .as_mut() + .unwrap() + .stdout_line + .push_str(&stdout); + + self.messages.push(TraceMsg { source: TraceSrc::App(platform), level: Level::INFO, content: stdout, - })); + }); if self.is_snapped() { self.scroll_to_bottom(); @@ -176,21 +237,80 @@ impl Output { /// Why is the ctrl_c handler here? /// /// Also tick animations every few ms - pub(crate) async fn wait(&mut self) -> ServeUpdate { - let event = tokio::select! { - Some(Some(Ok(event))) = OptionFuture::from(self.events.as_mut().map(|f| f.next())) => event, - else => futures_util::future::pending().await + pub async fn wait(&mut self) -> io::Result { + fn ok_and_some(f: F) -> impl Future + where + F: Future, E>>, + { + next_or_pending(async move { f.await.ok().flatten() }) + } + let user_input = async { + let events = self.events.as_mut()?; + events.next().await + }; + let user_input = ok_and_some(user_input.map(|e| e.transpose())); + + let has_running_apps = !self.running_apps.is_empty(); + let next_stdout = self.running_apps.values_mut().map(|app| { + let future = async move { + let (stdout, stderr) = match &mut app.output { + Some(out) => ( + ok_and_some(out.stdout.next_line()), + ok_and_some(out.stderr.next_line()), + ), + None => return futures_util::future::pending().await, + }; + + tokio::select! { + line = stdout => (app.result.target_platform, Some(line), None), + line = stderr => (app.result.target_platform, None, Some(line)), + } + }; + Box::pin(future) + }); + + let next_stdout = async { + if has_running_apps { + select_all(next_stdout).await.0 + } else { + futures_util::future::pending().await + } }; - ServeUpdate::TuiInput { event } + let tui_log_rx = &mut self.log_control.output_rx; + let next_tui_log = next_or_pending(tui_log_rx.next()); + + tokio::select! { + (platform, stdout, stderr) = next_stdout => { + if let Some(stdout) = stdout { + self.push_stdout(platform, stdout); + } + if let Some(stderr) = stderr { + self.push_stderr(platform, stderr); + } + }, + + // Handle internal CLI tracing logs. + log = next_tui_log => { + self.push_log(log); + } + + event = user_input => { + if self.handle_events(event).await? { + return Ok(true) + } + } + } + + Ok(false) } - pub(crate) fn shutdown(&mut self) -> io::Result<()> { + pub fn shutdown(&mut self) -> io::Result<()> { // if we're a tty then we need to disable the raw mode if self.interactive { - // self.log_control - // .output_enabled - // .store(false, Ordering::SeqCst); + self.log_control + .output_enabled + .store(false, Ordering::SeqCst); disable_raw_mode()?; stdout().execute(LeaveAlternateScreen)?.execute(Show)?; self.drain_print_logs(); @@ -208,25 +328,17 @@ impl Output { let messages = self.messages.drain(..); for msg in messages { - match msg { - ConsoleMessage::Log(msg) => { - if msg.source != TraceSrc::Cargo { - println!("[{}] {}: {}", msg.source, msg.level, msg.content); - } else { - println!("{}", msg.content); - } - } - - ConsoleMessage::BuildReady => { - // TODO: Better formatting for different content lengths. - } - ConsoleMessage::OnngoingBuild { stage, progress } => todo!(), + // TODO: Better formatting for different content lengths. + if msg.source != TraceSrc::Cargo { + println!("[{}] {}: {}", msg.source, msg.level, msg.content); + } else { + println!("{}", msg.content); } } } /// Handle an input event, returning `true` if the event should cause the program to restart. - pub(crate) fn handle_input(&mut self, input: Event) -> io::Result { + pub fn handle_input(&mut self, input: Event) -> io::Result { // handle ctrlc if let Event::Key(key) = input { if let KeyCode::Char('c') = key.code { @@ -340,7 +452,7 @@ impl Output { if key.code == KeyCode::Char('o') && key.kind == KeyEventKind::Press => { // Open the running app. - // open::that(format!("http://{}:{}", self.addr, self.port))?; + open::that(format!("http://{}:{}", self.addr.addr, self.addr.port))?; } Event::Key(key) @@ -373,9 +485,9 @@ impl Output { Ok(false) } - pub(crate) fn new_ws_message( + pub fn new_ws_message( &mut self, - platform: Platform, + platform: TargetPlatform, message: axum::extract::ws::Message, ) { // Deccode the message and push it to our logs. @@ -404,29 +516,6 @@ impl Output { } } - // pub(crate) fn scroll_to_bottom(&mut self) { - // self.scroll = (self.num_lines_with_wrapping).saturating_sub(self.term_height); - // } - - pub(crate) fn push_inner_log(&mut self, msg: String) { - self.push_log( - TraceMsg::new(TraceSrc::Build, Level::INFO, msg), - // crate::builder::BuildMessage { - // level: tracing::Level::INFO, - // message: crate::builder::MessageType::Text(msg), - // source: crate::builder::MessageSource::Dev, - // }, - ); - } - - pub(crate) fn new_build_logs(&mut self, platform: Platform, update: BuildUpdateProgress) { - self.push_log(TraceMsg::new( - TraceSrc::Build, - Level::INFO, - format!("Build progress for {platform:?}: {update:?}"), - )) - } - fn is_snapped(&self) -> bool { true } @@ -436,30 +525,69 @@ impl Output { } pub fn push_log(&mut self, message: TraceMsg) { - self.messages.push(ConsoleMessage::Log(message)); + self.messages.push(message); if self.is_snapped() { self.scroll_to_bottom(); } } - pub(crate) fn new_ready_app(&mut self, handle: &AppHandle) { - // Finish the build progress for the platform that just finished building - if let Some(build) = self - .build_progress + pub fn new_build_progress(&mut self, platform: TargetPlatform, update: UpdateBuildProgress) { + self.build_progress .current_builds - .get_mut(&handle.app.build.platform()) - { - build.stage = Stage::Finished; + .entry(platform) + .or_default() + .update(update); + + if self.is_snapped() { + self.scroll_to_bottom(); + } + } + + pub fn new_ready_app(&mut self, build_engine: &mut Builder, results: Vec) { + for result in results { + let out = build_engine + .children + .iter_mut() + .find_map(|(platform, child)| { + if platform == &result.target_platform { + let stdout = child.stdout.take().unwrap(); + let stderr = child.stderr.take().unwrap(); + Some((stdout, stderr)) + } else { + None + } + }); + + let platform = result.target_platform; + + let stdout = out.map(|(stdout, stderr)| RunningAppOutput { + stdout: BufReader::new(stdout).lines(), + stderr: BufReader::new(stderr).lines(), + stdout_line: String::new(), + stderr_line: String::new(), + }); + + let app = RunningApp { + result, + output: stdout, + }; + + self.running_apps.insert(platform, app); + + // Finish the build progress for the platform that just finished building + if let Some(build) = self.build_progress.current_builds.get_mut(&platform) { + build.stage = Stage::Finished; + } } } pub fn render( &mut self, - _opts: &ServeArgs, + _opts: &Serve, _config: &DioxusCrate, _build_engine: &Builder, - _server: &DevServer, + _server: &Server, _watcher: &Watcher, ) { // just drain the build logs @@ -521,7 +649,7 @@ impl Output { &self.build_progress, self.more_modal_open, self.show_filter_menu, - &self.dx_version, + &self._dx_version, ); if self.more_modal_open { @@ -536,45 +664,68 @@ impl Output { ); }); } -} -/// Our console has "special" messages that get better rendering. -/// -/// We want to display them differently since they have their own state and are rendered differently. -enum ConsoleMessage { - Log(TraceMsg), - OnngoingBuild { stage: Stage, progress: f64 }, - BuildReady, + async fn handle_events(&mut self, event: Event) -> io::Result { + let mut events = vec![event]; + + // Collect all the events within the next 10ms in one stream + let collect_events = async { + loop { + let Some(Ok(next)) = self.events.as_mut().unwrap().next().await else { + break; + }; + events.push(next); + } + }; + tokio::select! { + _ = collect_events => {}, + _ = tokio::time::sleep(Duration::from_millis(10)) => {} + } + + // Debounce events within the same frame + let mut handled = HashSet::new(); + for event in events { + if !handled.contains(&event) { + if self.handle_input(event.clone())? { + // Restart the running app. + return Ok(true); + } + handled.insert(event); + } + } + + Ok(false) + } } #[derive(Default, Debug, PartialEq)] -pub(crate) struct ActiveBuild { +pub struct ActiveBuild { stage: Stage, progress: f64, failed: Option, } impl ActiveBuild { - // fn update(&mut self, update: BuildUpdateProgress) { - // match update.update { - // UpdateStage::Start => { - // // If we are already past the stage, don't roll back, but allow a fresh build to update. - // if self.stage > update.stage && self.stage < Stage::Finished { - // return; - // } - // self.stage = update.stage; - // self.progress = 0.0; - // self.failed = None; - // } - // UpdateStage::SetProgress(progress) => { - // self.progress = progress; - // } - // UpdateStage::Failed(failed) => { - // self.stage = Stage::Finished; - // self.failed = Some(failed.clone()); - // } - // } - // } + fn update(&mut self, update: UpdateBuildProgress) { + match update.update { + UpdateStage::Start => { + // If we are already past the stage, don't roll back, but allow a fresh build to update. + if self.stage > update.stage && self.stage < Stage::Finished { + return; + } + self.stage = update.stage; + self.progress = 0.0; + self.failed = None; + } + UpdateStage::SetProgress(progress) => { + self.progress = progress; + } + UpdateStage::Failed(failed) => { + self.stage = Stage::Finished; + self.failed = Some(failed.clone()); + } + } + } fn make_spans(&self, area: Rect) -> Vec { let mut spans = Vec::new(); @@ -658,64 +809,14 @@ async fn rustc_version() -> String { .unwrap_or_else(|| "".to_string()) } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub(crate) enum LogSource { - Internal, - Target(Platform), -} - -impl Display for LogSource { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - LogSource::Internal => write!(f, "CLI"), - LogSource::Target(platform) => write!(f, "{platform}"), - } - } -} - -impl From for LogSource { - fn from(platform: Platform) -> Self { - LogSource::Target(platform) - } -} - -#[derive(Default)] -pub(crate) struct BuildProgress { - internal_logs: Vec, - current_builds: HashMap, +pub struct RunningApp { + result: BuildResult, + output: Option, } -// impl BuildProgress { -// pub(crate) fn progress(&self) -> f64 { -// self.build_logs -// .values() -// .min_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)) -// .map(|build| match build.stage { -// Stage::Initializing => 0.0, -// Stage::InstallingWasmTooling => 0.0, -// Stage::Compiling => build.progress, -// Stage::OptimizingWasm | Stage::OptimizingAssets | Stage::Finished => 1.0, -// }) -// .unwrap_or_default() -// } -// } - -// #[derive(Default)] -// pub struct BuildProgress { -// current_builds: HashMap, -// } - -impl BuildProgress { - pub fn progress(&self) -> f64 { - self.current_builds - .values() - .min_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)) - .map(|build| match build.stage { - Stage::Initializing => 0.0, - Stage::InstallingWasmTooling => 0.0, - Stage::Compiling => build.progress, - Stage::OptimizingWasm | Stage::OptimizingAssets | Stage::Finished => 1.0, - }) - .unwrap_or_default() - } +struct RunningAppOutput { + stdout: Lines>, + stderr: Lines>, + stdout_line: String, + stderr_line: String, } diff --git a/packages/cli/src/serve/output/render.rs b/packages/cli/src/serve/output/render.rs index 4158ee1a57..02aa93fe21 100644 --- a/packages/cli/src/serve/output/render.rs +++ b/packages/cli/src/serve/output/render.rs @@ -1,9 +1,8 @@ -use super::{BuildProgress, ConsoleMessage, TraceMsg, TraceSrc}; -use crate::Platform; +use super::{BuildProgress, TraceMsg, TraceSrc}; use ansi_to_tui::IntoText as _; +use dioxus_cli_config::Platform; use ratatui::{ layout::{Alignment, Constraint, Direction, Layout, Rect}, - prelude::Buffer, style::{Color, Style, Stylize}, text::{Line, Span, Text}, widgets::{Block, Borders, Clear, List, ListState, Paragraph, Widget, Wrap}, @@ -118,7 +117,7 @@ impl TuiLayout { &self, frame: &mut Frame, scroll_position: u16, - messages: &[ConsoleMessage], + messages: &[TraceMsg], enabled_filters: &[String], ) -> u16 { const LEVEL_MAX: usize = "BUILD: ".len(); @@ -129,82 +128,75 @@ impl TuiLayout { for msg in messages.iter() { let mut sub_line_padding = 0; - match msg { - ConsoleMessage::BuildReady => {} - ConsoleMessage::OnngoingBuild { stage, progress } => {} - - ConsoleMessage::Log(msg) => { - let text = msg.content.trim_end().into_text().unwrap_or_default(); - - for (idx, line) in text.lines.into_iter().enumerate() { - // Don't add any formatting for cargo messages. - let out_line = if msg.source != TraceSrc::Cargo { - if idx == 0 { - match msg.source { - TraceSrc::Dev => { - let mut spans = vec![Span::from(" DEV: ").light_magenta()]; - - for span in line.spans { - spans.push(span); - } - spans - } - TraceSrc::Build => { - let mut spans = vec![Span::from("BUILD: ").light_blue()]; - - for span in line.spans { - spans.push(span); - } - spans - } - _ => { - // Build level tag: `INFO:`` - // We don't subtract 1 here for `:` because we still want at least 1 padding. - let padding = build_msg_padding( - LEVEL_MAX - msg.level.to_string().len() - 2, - ); - let level = format!("{padding}{}: ", msg.level); - sub_line_padding += level.len(); - - let level_span = Span::from(level); - let level_span = match msg.level { - Level::TRACE => level_span.black(), - Level::DEBUG => level_span.light_magenta(), - Level::INFO => level_span.light_green(), - Level::WARN => level_span.light_yellow(), - Level::ERROR => level_span.light_red(), - }; - - let mut out_line = vec![level_span]; - for span in line.spans { - out_line.push(span); - } - - out_line - } + let text = msg.content.trim_end().into_text().unwrap_or_default(); + + for (idx, line) in text.lines.into_iter().enumerate() { + // Don't add any formatting for cargo messages. + let out_line = if msg.source != TraceSrc::Cargo { + if idx == 0 { + match msg.source { + TraceSrc::Dev => { + let mut spans = vec![Span::from(" DEV: ").light_magenta()]; + + for span in line.spans { + spans.push(span); } - } else { - // Not the first line. Append the padding and merge into list. - let padding = build_msg_padding(sub_line_padding); + spans + } + TraceSrc::Build => { + let mut spans = vec![Span::from("BUILD: ").light_blue()]; - let mut out_line = vec![Span::from(padding)]; + for span in line.spans { + spans.push(span); + } + spans + } + _ => { + // Build level tag: `INFO:`` + // We don't subtract 1 here for `:` because we still want at least 1 padding. + let padding = + build_msg_padding(LEVEL_MAX - msg.level.to_string().len() - 2); + let level = format!("{padding}{}: ", msg.level); + sub_line_padding += level.len(); + + let level_span = Span::from(level); + let level_span = match msg.level { + Level::TRACE => level_span.black(), + Level::DEBUG => level_span.light_magenta(), + Level::INFO => level_span.light_green(), + Level::WARN => level_span.light_yellow(), + Level::ERROR => level_span.light_red(), + }; + + let mut out_line = vec![level_span]; for span in line.spans { out_line.push(span); } + out_line } - } else { - line.spans - }; + } + } else { + // Not the first line. Append the padding and merge into list. + let padding = build_msg_padding(sub_line_padding); - out_text.push_line(Line::from(out_line)); + let mut out_line = vec![Span::from(padding)]; + for span in line.spans { + out_line.push(span); + } + out_line } - } + } else { + line.spans + }; + + out_text.push_line(Line::from(out_line)); } } // Only show messages for filters that are enabled. let mut included_line_ids = Vec::new(); + for filter in enabled_filters { let re = Regex::new(filter); for (index, line) in out_text.lines.iter().enumerate() { @@ -244,9 +236,6 @@ impl TuiLayout { } } - // out_text.push_line(line); - // let lines: Buffer = Paragraph::new(out_text).try_into().unwrap(); - let (console_width, _console_height) = self.get_console_size(); let paragraph = Paragraph::new(out_text) diff --git a/packages/cli/src/serve/proxy.rs b/packages/cli/src/serve/proxy.rs index b26616f1f4..bb3cb3e57b 100644 --- a/packages/cli/src/serve/proxy.rs +++ b/packages/cli/src/serve/proxy.rs @@ -1,6 +1,6 @@ -use crate::config::WebProxyConfig; use crate::TraceSrc; use crate::{Error, Result}; +use dioxus_cli_config::WebProxyConfig; use anyhow::{anyhow, Context}; use axum::body::Body as MyBody; @@ -56,7 +56,7 @@ impl ProxyClient { /// - the exact path of the proxy config's backend URL, e.g. /api /// - the exact path with a trailing slash, e.g. /api/ /// - any subpath of the backend URL, e.g. /api/foo/bar -pub(crate) fn add_proxy(mut router: Router, proxy: &WebProxyConfig) -> Result { +pub fn add_proxy(mut router: Router, proxy: &WebProxyConfig) -> Result { let url: Uri = proxy.backend.parse()?; let path = url.path().to_string(); let trimmed_path = path.trim_start_matches('/'); diff --git a/packages/cli/src/serve/runner.rs b/packages/cli/src/serve/runner.rs deleted file mode 100644 index e389023b83..0000000000 --- a/packages/cli/src/serve/runner.rs +++ /dev/null @@ -1,76 +0,0 @@ -use super::{handle::AppHandle, ServeUpdate}; -use crate::{builder::Platform, bundler::AppBundle, Result}; -use futures_util::{future::OptionFuture, stream::FuturesUnordered}; -use std::{collections::HashMap, net::SocketAddr}; -use tokio_stream::StreamExt; - -pub(crate) struct AppRunner { - /// Ongoing apps running in place - /// - /// They might be actively being being, running, or have exited. - /// - /// When a new full rebuild occurs, we will keep these requests here - pub(crate) running: HashMap, -} - -impl AppRunner { - pub(crate) fn start() -> Self { - Self { - running: Default::default(), - } - } - - pub(crate) async fn wait(&mut self) -> ServeUpdate { - // If there are no running apps, we can just return pending to avoid deadlocking - if self.running.is_empty() { - return futures_util::future::pending().await; - } - - self.running - .iter_mut() - .map(|(platform, handle)| async { - use ServeUpdate::*; - let platform = *platform; - tokio::select! { - Some(Ok(Some(msg))) = OptionFuture::from(handle.stdout.as_mut().map(|f| f.next_line())) => { - StdoutReceived { platform, msg } - }, - Some(Ok(Some(msg))) = OptionFuture::from(handle.stderr.as_mut().map(|f| f.next_line())) => { - StderrReceived { platform, msg } - }, - Some(status) = OptionFuture::from(handle.child.as_mut().map(|f| f.wait())) => { - tracing::info!("Child process exited with status: {status:?}"); - match status { - Ok(status) => ProcessExited { status, platform }, - Err(_err) => todo!("handle error in process joining?"), - } - } - else => futures_util::future::pending().await - } - }) - .collect::>() - .next() - .await - .expect("Stream to pending if not empty") - } - - /// Finally "bundle" this app and return a handle to it - pub(crate) async fn open( - &mut self, - app: AppBundle, - devserver_ip: SocketAddr, - fullstack_address: Option, - ) -> Result<&AppHandle> { - let platform = app.build.build.platform(); - self.kill(platform).await; - - let handle = AppHandle::start(app, devserver_ip, fullstack_address).await?; - self.running.insert(platform, handle); - - Ok(self.running.get(&platform).unwrap()) - } - - pub(crate) async fn kill(&mut self, platform: Platform) { - self.running.remove(&platform); - } -} diff --git a/packages/cli/src/serve/server.rs b/packages/cli/src/serve/server.rs index dcafca1d7b..26d98196e3 100644 --- a/packages/cli/src/serve/server.rs +++ b/packages/cli/src/serve/server.rs @@ -1,42 +1,41 @@ -use crate::dioxus_crate::DioxusCrate; -use crate::TraceSrc; -use crate::{builder::Platform, serve::ServeArgs}; -use crate::{config::WebHttpsConfig, serve::update::ServeUpdate}; +use crate::serve::{next_or_pending, Serve}; +use crate::{dioxus_crate::DioxusCrate, TraceSrc}; use crate::{Error, Result}; +use axum::extract::{Request, State}; +use axum::middleware::{self, Next}; use axum::{ body::Body, extract::{ ws::{Message, WebSocket}, WebSocketUpgrade, }, - extract::{Request, State}, http::{ header::{HeaderName, HeaderValue, CACHE_CONTROL, EXPIRES, PRAGMA}, Method, Response, StatusCode, }, - middleware::{self, Next}, response::IntoResponse, routing::{get, get_service}, Extension, Router, }; use axum_server::tls_rustls::RustlsConfig; -use dioxus_devtools_types::{DevserverMsg, HotReloadMsg}; +use dioxus_cli_config::{Platform, WebHttpsConfig}; +use dioxus_hot_reload::{DevserverMsg, HotReloadMsg}; use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender}; -use futures_util::{ - future, - stream::{self, FuturesUnordered}, - StreamExt, -}; -use hyper::{header::ACCEPT, HeaderMap}; +use futures_util::stream; +use futures_util::{stream::FuturesUnordered, StreamExt}; +use hyper::header::ACCEPT; +use hyper::HeaderMap; use serde::{Deserialize, Serialize}; +use std::net::TcpListener; +use std::path::Path; +use std::sync::Arc; +use std::sync::RwLock; use std::{ convert::Infallible, fs, io, - net::{IpAddr, SocketAddr, TcpListener}, - sync::RwLock, + net::{IpAddr, SocketAddr}, + process::Command, }; -use std::{path::Path, sync::Arc}; -use tokio::process::Command; use tokio::task::JoinHandle; use tower::ServiceBuilder; use tower_http::{ @@ -45,25 +44,62 @@ use tower_http::{ ServiceBuilderExt, }; -pub(crate) struct DevServer { - pub(crate) _args: ServeArgs, - pub(crate) hot_reload_sockets: Vec, - pub(crate) build_status_sockets: Vec, - pub(crate) ip: SocketAddr, - pub(crate) new_hot_reload_sockets: UnboundedReceiver, - pub(crate) new_build_status_sockets: UnboundedReceiver, - _server_task: JoinHandle>, +pub enum ServerUpdate { + NewConnection, + Message(Message), +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(tag = "type", content = "data")] +enum Status { + ClientInit { + application_name: String, + platform: String, + }, + Building { + progress: f64, + build_message: String, + }, + BuildError { + error: String, + }, + Ready, +} + +#[derive(Debug, Clone)] +struct SharedStatus(Arc>); + +impl SharedStatus { + fn new(status: Status) -> Self { + Self(Arc::new(RwLock::new(status))) + } + + fn set(&self, status: Status) { + *self.0.write().unwrap() = status; + } + + fn get(&self) -> Status { + self.0.read().unwrap().clone() + } +} +pub(crate) struct Server { + pub hot_reload_sockets: Vec, + pub build_status_sockets: Vec, + pub ip: SocketAddr, + pub new_hot_reload_sockets: UnboundedReceiver, + pub new_build_status_sockets: UnboundedReceiver, + _server_task: JoinHandle>, /// We proxy (not hot reloading) fullstack requests to this port - pub(crate) fullstack_port: Option, + pub fullstack_port: Option, build_status: SharedStatus, application_name: String, platform: String, } -impl DevServer { - pub(crate) fn start(args: &ServeArgs, cfg: &DioxusCrate) -> Self { +impl Server { + pub fn start(serve: &Serve, cfg: &DioxusCrate) -> Self { let (hot_reload_sockets_tx, hot_reload_sockets_rx) = futures_channel::mpsc::unbounded(); let (build_status_sockets_tx, build_status_sockets_rx) = futures_channel::mpsc::unbounded(); @@ -72,24 +108,27 @@ impl DevServer { build_message: "Starting the build...".to_string(), }); - let addr = args.address.address(); - let start_browser = args.open.unwrap_or_default(); + let addr = serve.server_arguments.address.address(); + let start_browser = serve.server_arguments.open.unwrap_or_default(); // If we're serving a fullstack app, we need to find a port to proxy to - let proxied_port = if args.should_proxy_build() { + let fullstack_port = if matches!( + serve.build_arguments.platform(), + Platform::Liveview | Platform::Fullstack | Platform::StaticGeneration + ) { get_available_port(addr.ip()) } else { None }; - let proxied_address = proxied_port.map(|port| SocketAddr::new(addr.ip(), port)); + let fullstack_address = fullstack_port.map(|port| SocketAddr::new(addr.ip(), port)); - let router = Self::setup_router( - args, + let router = setup_router( + serve, cfg, hot_reload_sockets_tx, build_status_sockets_tx, - proxied_address, + fullstack_address, build_status.clone(), ) .unwrap(); @@ -97,13 +136,7 @@ impl DevServer { // Actually just start the server, cloning in a few bits of config let web_config = cfg.dioxus_config.web.https.clone(); let base_path = cfg.dioxus_config.web.app.base_path.clone(); - let platform = args.platform(); - - let listener = std::net::TcpListener::bind(addr).expect("Failed to bind port"); - _ = listener.set_nonblocking(true); - - let addr = listener.local_addr().unwrap(); - + let platform = serve.platform(); let _server_task = tokio::spawn(async move { let web_config = web_config.clone(); // HTTPS @@ -118,13 +151,13 @@ impl DevServer { // Start the server with or without rustls if let Some(rustls) = rustls { - axum_server::from_tcp_rustls(listener, rustls) + axum_server::bind_rustls(addr, rustls) .serve(router.into_make_service()) .await? } else { // Create a TCP listener bound to the address axum::serve( - tokio::net::TcpListener::from_std(listener).unwrap(), + tokio::net::TcpListener::bind(&addr).await?, router.into_make_service(), ) .await? @@ -134,18 +167,17 @@ impl DevServer { }); Self { - _args: args.clone(), hot_reload_sockets: Default::default(), build_status_sockets: Default::default(), new_hot_reload_sockets: hot_reload_sockets_rx, new_build_status_sockets: build_status_sockets_rx, _server_task, ip: addr, - fullstack_port: proxied_port, + fullstack_port, build_status, application_name: cfg.dioxus_config.application.name.clone(), - platform: args.build_arguments.platform().to_string(), + platform: serve.build_arguments.platform().to_string(), } } @@ -166,7 +198,7 @@ impl DevServer { } /// Sends a start build message to all clients. - pub(crate) async fn start_build(&mut self) { + pub async fn start_build(&mut self) { self.build_status.set(Status::Building { progress: 0.0, build_message: "Starting the build...".to_string(), @@ -175,7 +207,7 @@ impl DevServer { } /// Sends an updated build status to all clients. - pub(crate) async fn update_build_status(&mut self, progress: f64, build_message: String) { + pub async fn update_build_status(&mut self, progress: f64, build_message: String) { if !matches!(self.build_status.get(), Status::Building { .. }) { return; } @@ -187,11 +219,7 @@ impl DevServer { } /// Sends hot reloadable changes to all clients. - pub(crate) async fn send_hotreload(&mut self, reload: HotReloadMsg) { - if !reload.assets.is_empty() { - tracing::debug!("Hot reloading assets {:?}", reload.assets); - } - + pub async fn send_hotreload(&mut self, reload: HotReloadMsg) { let msg = DevserverMsg::HotReload(reload); let msg = serde_json::to_string(&msg).unwrap(); @@ -208,7 +236,7 @@ impl DevServer { } /// Wait for new clients to be connected and then save them - pub(crate) async fn wait(&mut self) -> ServeUpdate { + pub async fn wait(&mut self) -> Option { let mut new_hot_reload_socket = self.new_hot_reload_sockets.next(); let mut new_build_status_socket = self.new_build_status_sockets.next(); let mut new_message = self @@ -217,13 +245,14 @@ impl DevServer { .enumerate() .map(|(idx, socket)| async move { (idx, socket.next().await) }) .collect::>(); + let next_new_message = next_or_pending(new_message.next()); tokio::select! { new_hot_reload_socket = &mut new_hot_reload_socket => { if let Some(new_socket) = new_hot_reload_socket { drop(new_message); self.hot_reload_sockets.push(new_socket); - return ServeUpdate::NewConnection; + return Some(ServerUpdate::NewConnection); } else { panic!("Could not receive a socket - the devtools could not boot - the port is likely already in use"); } @@ -238,14 +267,14 @@ impl DevServer { _ = send_build_status_to(&self.build_status, &mut new_socket).await; self.build_status_sockets.push(new_socket); } - return future::pending::().await; + return None; } else { panic!("Could not receive a socket - the devtools could not boot - the port is likely already in use"); } } - Some((idx, message)) = new_message.next() => { + (idx, message) = next_new_message => { match message { - Some(Ok(message)) => return ServeUpdate::WsMessage(message), + Some(Ok(message)) => return Some(ServerUpdate::Message(message)), _ => { drop(new_message); _ = self.hot_reload_sockets.remove(idx); @@ -254,11 +283,11 @@ impl DevServer { } } - future::pending().await + None } /// Converts a `cargo` error to HTML and sends it to clients. - pub(crate) async fn send_build_error(&mut self, error: Error) { + pub async fn send_build_error(&mut self, error: Error) { let error = error.to_string(); self.build_status.set(Status::BuildError { error: ansi_to_html::convert(&error).unwrap_or(error), @@ -267,19 +296,19 @@ impl DevServer { } /// Tells all clients that a full rebuild has started. - pub(crate) async fn send_reload_start(&mut self) { + pub async fn send_reload_start(&mut self) { self.send_devserver_message(DevserverMsg::FullReloadStart) .await; } /// Tells all clients that a full rebuild has failed. - pub(crate) async fn send_reload_failed(&mut self) { + pub async fn send_reload_failed(&mut self) { self.send_devserver_message(DevserverMsg::FullReloadFailed) .await; } /// Tells all clients to reload if possible for new changes. - pub(crate) async fn send_reload_command(&mut self) { + pub async fn send_reload_command(&mut self) { self.build_status.set(Status::Ready); self.send_build_status().await; self.send_devserver_message(DevserverMsg::FullReloadCommand) @@ -287,7 +316,7 @@ impl DevServer { } /// Send a shutdown message to all connected clients. - pub(crate) async fn send_shutdown(&mut self) { + pub async fn send_shutdown(&mut self) { self.send_devserver_message(DevserverMsg::Shutdown).await; } @@ -300,7 +329,7 @@ impl DevServer { } } - pub(crate) async fn shutdown(&mut self) { + pub async fn shutdown(&mut self) { self.send_shutdown().await; for socket in self.hot_reload_sockets.drain(..) { _ = socket.close().await; @@ -308,36 +337,57 @@ impl DevServer { } /// Get the address the fullstack server should run on if we're serving a fullstack app - pub(crate) fn fullstack_address(&self) -> Option { + pub fn fullstack_address(&self) -> Option { self.fullstack_port .map(|port| SocketAddr::new(self.ip.ip(), port)) } +} - /// Sets up and returns a router - /// - /// Steps include: - /// - Setting up cors - /// - Setting up the proxy to the endpoint specified in the config - /// - Setting up the file serve service - /// - Setting up the websocket endpoint for devtools - fn setup_router( - args: &ServeArgs, - krate: &DioxusCrate, - hot_reload_sockets: UnboundedSender, - build_status_sockets: UnboundedSender, - fullstack_address: Option, - build_status: SharedStatus, - ) -> Result { - let mut router = Router::new(); - - // Setup proxy for the endpoint specified in the config - for proxy_config in krate.dioxus_config.web.proxy.iter() { - router = super::proxy::add_proxy(router, proxy_config)?; - } +/// Sets up and returns a router +/// +/// Steps include: +/// - Setting up cors +/// - Setting up the proxy to the endpoint specified in the config +/// - Setting up the file serve service +/// - Setting up the websocket endpoint for devtools +fn setup_router( + serve: &Serve, + config: &DioxusCrate, + hot_reload_sockets: UnboundedSender, + build_status_sockets: UnboundedSender, + fullstack_address: Option, + build_status: SharedStatus, +) -> Result { + let mut router = Router::new(); + let platform = serve.build_arguments.platform(); + + // Setup proxy for the endpoint specified in the config + for proxy_config in config.dioxus_config.web.proxy.iter() { + router = super::proxy::add_proxy(router, proxy_config)?; + } + + // server the dir if it's web, otherwise let the fullstack server itself handle it + match platform { + Platform::Web => { + // Route file service to output the .wasm and assets if this is a web build + let base_path = format!( + "/{}", + config + .dioxus_config + .web + .app + .base_path + .as_deref() + .unwrap_or_default() + .trim_matches('/') + ); - if args.should_proxy_build() { - // For fullstack, liveview, and server, forward all requests to the inner server + router = router.nest_service(&base_path, build_serve_dir(serve, config)); + } + Platform::Liveview | Platform::Fullstack | Platform::StaticGeneration => { + // For fullstack and static generation, forward all requests to the server let address = fullstack_address.unwrap(); + router = router.nest_service("/",super::proxy::proxy_to( format!("http://{address}").parse().unwrap(), true, @@ -351,69 +401,54 @@ impl DevServer { .unwrap() }, )); - } else { - // Otherwise, just serve the dir ourselves - // Route file service to output the .wasm and assets if this is a web build - let base_path = format!( - "/{}", - krate - .dioxus_config - .web - .app - .base_path - .as_deref() - .unwrap_or_default() - .trim_matches('/') - ); - - router = router.nest_service(&base_path, build_serve_dir(args, krate)); } - - // Setup middleware to intercept html requests if the build status is "Building" - router = router.layer(middleware::from_fn_with_state( - build_status, - build_status_middleware, - )); - - // Setup websocket endpoint - and pass in the extension layer immediately after - router = router.nest( - "/_dioxus", - Router::new() - .route( - "/", - get( - |ws: WebSocketUpgrade, ext: Extension>| async move { - ws.on_upgrade(move |socket| async move { _ = ext.0.unbounded_send(socket) }) - }, - ), - ) - .layer(Extension(hot_reload_sockets)) - .route( - "/build_status", - get( - |ws: WebSocketUpgrade, ext: Extension>| async move { - ws.on_upgrade(move |socket| async move { _ = ext.0.unbounded_send(socket) }) - }, - ), - ) - .layer(Extension(build_status_sockets)), - ); - - // Setup cors - router = router.layer( - CorsLayer::new() - // allow `GET` and `POST` when accessing the resource - .allow_methods([Method::GET, Method::POST]) - // allow requests from any origin - .allow_origin(Any) - .allow_headers(Any), - ); - - Ok(router) + _ => {} } + + // Setup middleware to intercept html requests if the build status is "Building" + router = router.layer(middleware::from_fn_with_state( + build_status, + build_status_middleware, + )); + + // Setup websocket endpoint - and pass in the extension layer immediately after + router = router.nest( + "/_dioxus", + Router::new() + .route( + "/", + get( + |ws: WebSocketUpgrade, ext: Extension>| async move { + ws.on_upgrade(move |socket| async move { _ = ext.0.unbounded_send(socket) }) + }, + ), + ) + .layer(Extension(hot_reload_sockets)) + .route( + "/build_status", + get( + |ws: WebSocketUpgrade, ext: Extension>| async move { + ws.on_upgrade(move |socket| async move { _ = ext.0.unbounded_send(socket) }) + }, + ), + ) + .layer(Extension(build_status_sockets)), + ); + + // Setup cors + router = router.layer( + CorsLayer::new() + // allow `GET` and `POST` when accessing the resource + .allow_methods([Method::GET, Method::POST]) + // allow requests from any origin + .allow_origin(Any) + .allow_headers(Any), + ); + + Ok(router) } -fn build_serve_dir(args: &ServeArgs, cfg: &DioxusCrate) -> axum::routing::MethodRouter { +fn build_serve_dir(serve: &Serve, cfg: &DioxusCrate) -> axum::routing::MethodRouter { static CORS_UNSAFE: (HeaderValue, HeaderValue) = ( HeaderValue::from_static("unsafe-none"), HeaderValue::from_static("unsafe-none"), @@ -424,12 +459,12 @@ fn build_serve_dir(args: &ServeArgs, cfg: &DioxusCrate) -> axum::routing::Method HeaderValue::from_static("same-origin"), ); - let (coep, coop) = match args.cross_origin_policy { + let (coep, coop) = match serve.server_arguments.cross_origin_policy { true => CORS_REQUIRE.clone(), false => CORS_UNSAFE.clone(), }; - let out_dir = cfg.workdir(Platform::Web); + let out_dir = cfg.out_dir(); let index_on_404 = cfg.dioxus_config.web.watcher.index_on_404; get_service( @@ -476,20 +511,20 @@ fn no_cache( response } -pub(crate) fn insert_no_cache_headers(headers: &mut HeaderMap) { +pub fn insert_no_cache_headers(headers: &mut HeaderMap) { headers.insert(CACHE_CONTROL, HeaderValue::from_static("no-cache")); headers.insert(PRAGMA, HeaderValue::from_static("no-cache")); headers.insert(EXPIRES, HeaderValue::from_static("0")); } /// Returns an enum of rustls config -pub(crate) async fn get_rustls(web_config: &WebHttpsConfig) -> Result> { +pub async fn get_rustls(web_config: &WebHttpsConfig) -> Result> { if web_config.enabled != Some(true) { return Ok(None); } let (cert_path, key_path) = match web_config.mkcert { - Some(true) => get_rustls_with_mkcert(web_config).await?, + Some(true) => get_rustls_with_mkcert(web_config)?, _ => get_rustls_without_mkcert(web_config)?, }; @@ -498,9 +533,7 @@ pub(crate) async fn get_rustls(web_config: &WebHttpsConfig) -> Result Result<(String, String)> { +pub fn get_rustls_with_mkcert(web_config: &WebHttpsConfig) -> Result<(String, String)> { const DEFAULT_KEY_PATH: &str = "ssl/key.pem"; const DEFAULT_CERT_PATH: &str = "ssl/cert.pem"; @@ -546,14 +579,14 @@ pub(crate) async fn get_rustls_with_mkcert( return Err("failed to generate mkcert certificates".into()); } Ok(mut cmd) => { - cmd.wait().await?; + cmd.wait()?; } } Ok((cert_path, key_path)) } -pub(crate) fn get_rustls_without_mkcert(web_config: &WebHttpsConfig) -> Result<(String, String)> { +pub fn get_rustls_without_mkcert(web_config: &WebHttpsConfig) -> Result<(String, String)> { // get paths to cert & key if let (Some(key), Some(cert)) = (web_config.key_path.clone(), web_config.cert_path.clone()) { Ok((cert, key)) @@ -617,37 +650,3 @@ async fn send_build_status_to( let msg = serde_json::to_string(&build_status.get()).unwrap(); socket.send(Message::Text(msg)).await } - -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(tag = "type", content = "data")] -enum Status { - ClientInit { - application_name: String, - platform: String, - }, - Building { - progress: f64, - build_message: String, - }, - BuildError { - error: String, - }, - Ready, -} - -#[derive(Debug, Clone)] -struct SharedStatus(Arc>); - -impl SharedStatus { - fn new(status: Status) -> Self { - Self(Arc::new(RwLock::new(status))) - } - - fn set(&self, status: Status) { - *self.0.write().unwrap() = status; - } - - fn get(&self) -> Status { - self.0.read().unwrap().clone() - } -} diff --git a/packages/cli/src/serve/tracer.rs b/packages/cli/src/serve/tracer.rs deleted file mode 100644 index c9a8a7caa2..0000000000 --- a/packages/cli/src/serve/tracer.rs +++ /dev/null @@ -1,104 +0,0 @@ -use futures_channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender}; -use futures_util::StreamExt; -use once_cell::sync::OnceCell; -use std::{ - env, io, - sync::{ - atomic::{AtomicBool, Ordering}, - Mutex, - }, -}; -use tracing_subscriber::{prelude::*, EnvFilter}; -const LOG_ENV: &str = "DIOXUS_LOG"; - -use super::ServeUpdate; - -static TUI_ENABLED: AtomicBool = AtomicBool::new(false); -static TUI_TX: OnceCell> = OnceCell::new(); - -pub(crate) struct TraceController { - pub(crate) tui_rx: UnboundedReceiver, -} - -impl TraceController { - pub(crate) fn initialize() { - // Start a tracing instance just for serving. - // This ensures that any tracing we do while serving doesn't break the TUI itself, and instead is - // redirected to the serve process. - // If {LOG_ENV} is set, default to env, otherwise filter to cli - // and manganis warnings and errors from other crates - let mut filter = EnvFilter::new("error,dx=info,devdx=info,dioxus-cli=info"); - - if env::var(LOG_ENV).is_ok() { - filter = EnvFilter::from_env(LOG_ENV); - } - - let sub = tracing_subscriber::registry().with( - tracing_subscriber::fmt::layer() - .with_writer(Mutex::new(Writer { - stdout: io::stdout(), - })) - .with_filter(filter), - ); - - #[cfg(feature = "tokio-console")] - let sub = sub.with(console_subscriber::spawn()); - - sub.init(); - } - - pub(crate) fn start() -> Self { - // Create writer controller and custom writer. - let (tui_tx, tui_rx) = unbounded(); - TUI_TX.set(tui_tx.clone()).unwrap(); - TUI_ENABLED.store(true, Ordering::SeqCst); - - Self { tui_rx } - } - - /// Wait for the internal logger to send a message - pub(crate) async fn wait(&mut self) -> ServeUpdate { - ServeUpdate::TracingLog { - log: self.tui_rx.next().await.expect("tracer should never die"), - } - } - - pub(crate) fn shutdown(&self) { - TUI_ENABLED.store(false, Ordering::SeqCst); - } -} - -/// Represents the CLI's custom tracing writer for conditionally writing logs between outputs. -struct Writer { - stdout: io::Stdout, -} - -// Implement a conditional writer so that logs are routed to the appropriate place. -impl io::Write for Writer { - fn write(&mut self, buf: &[u8]) -> io::Result { - if TUI_ENABLED.load(Ordering::SeqCst) { - let len = buf.len(); - - let as_string = String::from_utf8(buf.to_vec()) - .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; - - TUI_TX - .get() - .unwrap() - .unbounded_send(as_string) - .map_err(|e| io::Error::new(io::ErrorKind::BrokenPipe, e))?; - - Ok(len) - } else { - self.stdout.write(buf) - } - } - - fn flush(&mut self) -> io::Result<()> { - if !TUI_ENABLED.load(Ordering::SeqCst) { - self.stdout.flush() - } else { - Ok(()) - } - } -} diff --git a/packages/cli/src/serve/update.rs b/packages/cli/src/serve/update.rs deleted file mode 100644 index f32e786070..0000000000 --- a/packages/cli/src/serve/update.rs +++ /dev/null @@ -1,49 +0,0 @@ -use crate::builder::{BuildUpdate, Platform}; -use axum::extract::ws::Message as WsMessage; -use std::{path::PathBuf, process::ExitStatus}; - -/// One fat enum to rule them all.... -/// -/// Thanks to libraries like winit for the inspiration -pub(crate) enum ServeUpdate { - NewConnection, - WsMessage(WsMessage), - - /// A build update from the build engine - BuildUpdate(BuildUpdate), - - /// A running process has received a stdout. - /// May or may not be a complete line - do not treat it as a line. It will include a line if it is a complete line. - /// - /// We will poll lines and any content in a 50ms interval - StdoutReceived { - platform: Platform, - msg: String, - }, - - /// A running process has received a stderr. - /// May or may not be a complete line - do not treat it as a line. It will include a line if it is a complete line. - /// - /// We will poll lines and any content in a 50ms interval - StderrReceived { - platform: Platform, - msg: String, - }, - - ProcessExited { - platform: Platform, - status: ExitStatus, - }, - - FilesChanged { - files: Vec, - }, - - TuiInput { - event: crossterm::event::Event, - }, - - TracingLog { - log: String, - }, -} diff --git a/packages/cli/src/serve/watcher.rs b/packages/cli/src/serve/watcher.rs index 0d7e01c99a..38581e909c 100644 --- a/packages/cli/src/serve/watcher.rs +++ b/packages/cli/src/serve/watcher.rs @@ -1,8 +1,11 @@ -use super::{detect::is_wsl, AppRunner}; -use super::{hot_reloading_file_map::HotreloadError, update::ServeUpdate}; -use crate::{cli::serve::ServeArgs, dioxus_crate::DioxusCrate}; -use crate::{serve::hot_reloading_file_map::FileMap, TraceSrc}; -use dioxus_devtools_types::HotReloadMsg; +use std::collections::{HashMap, HashSet}; +use std::{fs, path::PathBuf, time::Duration}; + +use super::hot_reloading_file_map::HotreloadError; +use crate::serve::hot_reloading_file_map::FileMap; +use crate::TraceSrc; +use crate::{cli::serve::Serve, dioxus_crate::DioxusCrate}; +use dioxus_hot_reload::HotReloadMsg; use dioxus_html::HtmlCtx; use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender}; use futures_util::StreamExt; @@ -11,44 +14,41 @@ use notify::{ event::{MetadataKind, ModifyKind}, Config, EventKind, }; -use std::collections::{HashMap, HashSet}; -use std::{path::PathBuf, time::Duration}; /// This struct stores the file watcher and the filemap for the project. /// /// This is where we do workspace discovery and recursively listen for changes in Rust files and asset /// directories. -pub(crate) struct Watcher { +pub struct Watcher { + _tx: UnboundedSender, rx: UnboundedReceiver, - krate: DioxusCrate, + _last_update_time: i64, + _watcher: Box, + queued_events: Vec, file_map: FileMap, ignore: Gitignore, applied_hot_reload_message: Option, - _tx: UnboundedSender, - _last_update_time: i64, - _watcher: Box, } impl Watcher { - pub(crate) fn start(serve: &ServeArgs, krate: &DioxusCrate) -> Self { + pub fn start(serve: &Serve, config: &DioxusCrate) -> Self { let (tx, rx) = futures_channel::mpsc::unbounded(); // Extend the watch path to include: // - the assets directory - this is so we can hotreload CSS and other assets by default // - the Cargo.toml file - this is so we can hotreload the project if the user changes dependencies // - the Dioxus.toml file - this is so we can hotreload the project if the user changes the Dioxus config - let mut allow_watch_path = krate.dioxus_config.web.watcher.watch_path.clone(); - allow_watch_path.push(krate.dioxus_config.application.asset_dir.clone()); + let mut allow_watch_path = config.dioxus_config.web.watcher.watch_path.clone(); + allow_watch_path.push(config.dioxus_config.application.asset_dir.clone()); allow_watch_path.push("Cargo.toml".to_string().into()); allow_watch_path.push("Dioxus.toml".to_string().into()); - allow_watch_path.push("assets".to_string().into()); allow_watch_path.dedup(); - let crate_dir = krate.crate_dir(); + let crate_dir = config.crate_dir(); let mut builder = ignore::gitignore::GitignoreBuilder::new(&crate_dir); builder.add(crate_dir.join(".gitignore")); - let out_dir = krate.out_dir(); + let out_dir = config.out_dir(); let out_dir_str = out_dir.display().to_string(); let excluded_paths = vec![ @@ -86,8 +86,9 @@ impl Watcher { // Create the file watcher. let mut watcher: Box = match is_wsl { true => { - let poll_interval = - Duration::from_secs(serve.wsl_file_poll_interval.unwrap_or(2) as u64); + let poll_interval = Duration::from_secs( + serve.server_arguments.wsl_file_poll_interval.unwrap_or(2) as u64, + ); Box::new( notify::PollWatcher::new( @@ -105,7 +106,7 @@ impl Watcher { // Watch the specified paths // todo: make sure we don't double-watch paths if they're nested for sub_path in allow_watch_path { - let path = &krate.crate_dir().join(sub_path); + let path = &config.crate_dir().join(sub_path); // If the path is ignored, don't watch it if ignore.matched(path, path.is_dir()).is_ignore() { @@ -121,38 +122,48 @@ impl Watcher { // Probe the entire project looking for our rsx calls // Whenever we get an update from the file watcher, we'll try to hotreload against this file map - let file_map = FileMap::create_with_filter::(krate.crate_dir(), |path| { + let file_map = FileMap::create_with_filter::(config.crate_dir(), |path| { ignore.matched(path, path.is_dir()).is_ignore() }) .unwrap(); Self { _tx: tx, - krate: krate.clone(), rx, _watcher: watcher, file_map, ignore, + queued_events: Vec::new(), _last_update_time: chrono::Local::now().timestamp(), applied_hot_reload_message: None, } } - /// Wait for changed files to be detected - pub(crate) async fn wait(&mut self) -> ServeUpdate { - // Wait for the next file to change - let mut changes: Vec<_> = self.rx.next().await.into_iter().collect(); + /// A cancel safe handle to the file watcher + /// + /// todo: this should be simpler logic? + pub async fn wait(&mut self) { + // Pull off any queued events in succession + while let Ok(Some(event)) = self.rx.try_next() { + self.queued_events.push(event); + } - // Dequeue in bulk if we can, we might've received a lot of events in one go - while let Some(event) = self.rx.try_next().ok().flatten() { - changes.push(event); + if !self.queued_events.is_empty() { + return; } - // Filter the changes + // If there are no queued events, wait for the next event + if let Some(event) = self.rx.next().await { + self.queued_events.push(event); + } + } + + /// Deques changed files from the event queue, doing the proper intelligent filtering + pub fn dequeue_changed_files(&mut self, config: &DioxusCrate) -> Vec { let mut all_mods: Vec = vec![]; // Decompose the events into a list of all the files that have changed - for event in changes.drain(..) { + for event in self.queued_events.drain(..) { // We only care about certain events. if !is_allowed_notify_event(&event) { continue; @@ -163,8 +174,18 @@ impl Watcher { } } - // Collect the files that have changed - let mut files = vec![]; + let mut modified_files = vec![]; + + // For the non-rust files, we want to check if it's an asset file + // This would mean the asset lives somewhere under the /assets directory or is referenced by magnanis in the linker + // todo: mg integration here + let _asset_dir = config + .dioxus_config + .application + .asset_dir + .canonicalize() + .ok(); + for path in all_mods.iter() { if path.extension().is_none() { continue; @@ -182,35 +203,34 @@ impl Watcher { // If the extension is a backup file, or a hidden file, ignore it completely (no rebuilds) if is_backup_file(path.to_path_buf()) { + tracing::trace!("Ignoring backup file: {:?}", path); continue; } // If the path is ignored, don't watch it if self.ignore.matched(path, path.is_dir()).is_ignore() { - tracing::info!("Ignoring update to file: {:?}", path); continue; } - tracing::info!("Enqueuing hotreload update to file: {:?}", path); - - files.push(path.clone()); + modified_files.push(path.clone()); } - ServeUpdate::FilesChanged { files } + modified_files } - pub(crate) fn attempt_hot_reload( + pub fn attempt_hot_reload( &mut self, + config: &DioxusCrate, modified_files: Vec, - runner: &AppRunner, ) -> Option { // If we have any changes to the rust files, we need to update the file map - let crate_dir = self.krate.crate_dir(); + let crate_dir = config.crate_dir(); let mut templates = vec![]; // Prepare the hotreload message we need to send let mut edited_rust_files = Vec::new(); let mut assets = Vec::new(); + let mut unknown_files = Vec::new(); for path in modified_files { // for various assets that might be linked in, we just try to hotreloading them forcefully @@ -221,35 +241,22 @@ impl Watcher { match ext { "rs" => edited_rust_files.push(path), - - // Look through the runners to see if any of them have an asset that matches the path - _ => { - for runner in runner.running.values() { - if let Some(bundled_name) = runner.hotreload_asset(&path) { - assets.push(bundled_name); - } - } - } + _ if path.starts_with("assets") => assets.push(path), + _ => unknown_files.push(path), } } - assets.dedup(); - - // Process the rust files for rust_file in edited_rust_files { match self.file_map.update_rsx::(&rust_file, &crate_dir) { Ok(hotreloaded_templates) => { templates.extend(hotreloaded_templates); } - // If the file is not reloadable, we need to rebuild Err(HotreloadError::Notreloadable) => return None, - // The rust file may have failed to parse, but that is most likely // because the user is in the middle of adding new code // We just ignore the error and let Rust analyzer warn about the problem Err(HotreloadError::Parse) => {} - // Otherwise just log the error Err(err) => { tracing::error!(dx_src = ?TraceSrc::Dev, "Error hotreloading file {rust_file:?}: {err}") @@ -257,7 +264,11 @@ impl Watcher { } } - let msg = HotReloadMsg { templates, assets }; + let msg = HotReloadMsg { + templates, + assets, + unknown_files, + }; self.add_hot_reload_message(&msg); @@ -265,37 +276,50 @@ impl Watcher { } /// Get any hot reload changes that have been applied since the last full rebuild - pub(crate) fn applied_hot_reload_changes(&mut self) -> Option { + pub fn applied_hot_reload_changes(&mut self) -> Option { self.applied_hot_reload_message.clone() } /// Clear the hot reload changes. This should be called any time a new build is starting - pub(crate) fn clear_hot_reload_changes(&mut self) { + pub fn clear_hot_reload_changes(&mut self) { self.applied_hot_reload_message.take(); } /// Store the hot reload changes for any future clients that connect fn add_hot_reload_message(&mut self, msg: &HotReloadMsg) { - let Some(applied) = &mut self.applied_hot_reload_message else { - self.applied_hot_reload_message = Some(msg.clone()); - return; - }; - - // Merge the assets, unknown files, and templates - // We keep the newer change if there is both a old and new change - let mut templates: HashMap = std::mem::take(&mut applied.templates) - .into_iter() - .map(|template| (template.location.clone(), template)) - .collect(); - let mut assets: HashSet = - std::mem::take(&mut applied.assets).into_iter().collect(); - for template in &msg.templates { - templates.insert(template.location.clone(), template.clone()); + match &mut self.applied_hot_reload_message { + Some(applied) => { + // Merge the assets, unknown files, and templates + // We keep the newer change if there is both a old and new change + let mut templates: HashMap = std::mem::take(&mut applied.templates) + .into_iter() + .map(|template| (template.location.clone(), template)) + .collect(); + let mut assets: HashSet = + std::mem::take(&mut applied.assets).into_iter().collect(); + let mut unknown_files: HashSet = + std::mem::take(&mut applied.unknown_files) + .into_iter() + .collect(); + for template in &msg.templates { + templates.insert(template.location.clone(), template.clone()); + } + assets.extend(msg.assets.iter().cloned()); + unknown_files.extend(msg.unknown_files.iter().cloned()); + applied.templates = templates.into_values().collect(); + applied.assets = assets.into_iter().collect(); + applied.unknown_files = unknown_files.into_iter().collect(); + } + None => { + self.applied_hot_reload_message = Some(msg.clone()); + } } + } - assets.extend(msg.assets.iter().cloned()); - applied.templates = templates.into_values().collect(); - applied.assets = assets.into_iter().collect(); + /// Ensure the changes we've received from the queue are actually legit changes to either assets or + /// rust code. We don't care about changes otherwise, unless we get a signal elsewhere to do a full rebuild + pub fn pending_changes(&mut self) -> bool { + !self.queued_events.is_empty() } } @@ -323,7 +347,7 @@ fn is_backup_file(path: PathBuf) -> bool { /// Tests if the provided [`notify::Event`] is something we listen to so we can avoid unescessary hot reloads. fn is_allowed_notify_event(event: ¬ify::Event) -> bool { - let allowed = match event.kind { + match event.kind { EventKind::Modify(ModifyKind::Data(_)) => true, EventKind::Modify(ModifyKind::Name(_)) => true, EventKind::Create(_) => true, @@ -334,11 +358,40 @@ fn is_allowed_notify_event(event: ¬ify::Event) -> bool { EventKind::Modify(ModifyKind::Any) => true, // Don't care about anything else. _ => false, - }; + } +} - tracing::info!("is_allowed_notify_event: {allowed:?} for {event:#?}"); +const WSL_1: &str = "/proc/sys/kernel/osrelease"; +const WSL_2: &str = "/proc/version"; +const WSL_KEYWORDS: [&str; 2] = ["microsoft", "wsl"]; - allowed +/// Detects if `dx` is being ran in a WSL environment. +/// +/// We determine this based on whether the keyword `microsoft` or `wsl` is contained within the [`WSL_1`] or [`WSL_2`] files. +/// This may fail in the future as it isn't guaranteed by Microsoft. +/// See https://github.com/microsoft/WSL/issues/423#issuecomment-221627364 +fn is_wsl() -> bool { + // Test 1st File + if let Ok(content) = fs::read_to_string(WSL_1) { + let lowercase = content.to_lowercase(); + for keyword in WSL_KEYWORDS { + if lowercase.contains(keyword) { + return true; + } + } + } + + // Test 2nd File + if let Ok(content) = fs::read_to_string(WSL_2) { + let lowercase = content.to_lowercase(); + for keyword in WSL_KEYWORDS { + if lowercase.contains(keyword) { + return true; + } + } + } + + false } #[test] diff --git a/packages/cli/src/settings.rs b/packages/cli/src/settings.rs index 50f3d7be20..bc680dfed1 100644 --- a/packages/cli/src/settings.rs +++ b/packages/cli/src/settings.rs @@ -18,26 +18,26 @@ const GLOBAL_SETTINGS_FILE_NAME: &str = "dioxus/settings.toml"; /// /// This allows users to control the cli settings with ease. #[derive(Debug, Clone, Serialize, Deserialize, Default)] -pub(crate) struct CliSettings { +pub struct CliSettings { /// Describes whether hot reload should always be on. - pub(crate) always_hot_reload: Option, + pub always_hot_reload: Option, /// Describes whether the CLI should always open the browser for Web targets. - pub(crate) always_open_browser: Option, + pub always_open_browser: Option, /// Describes whether desktop apps in development will be pinned always-on-top. - pub(crate) always_on_top: Option, + pub always_on_top: Option, /// Describes the interval in seconds that the CLI should poll for file changes on WSL. #[serde(default = "default_wsl_file_poll_interval")] - pub(crate) wsl_file_poll_interval: Option, + pub wsl_file_poll_interval: Option, } impl CliSettings { /// Load the settings from the local, global, or default config in that order - pub(crate) fn load() -> Self { + pub fn load() -> Self { Self::from_global().unwrap_or_default() } /// Get the current settings structure from global. - pub(crate) fn from_global() -> Option { + pub fn from_global() -> Option { let Some(path) = dirs::data_local_dir() else { warn!("failed to get local data directory, some config keys may be missing"); return None; @@ -63,7 +63,7 @@ impl CliSettings { /// Save the current structure to the global settings toml. /// This does not save to project-level settings. - pub(crate) fn save(self) -> Result { + pub fn save(self) -> Result { let path = Self::get_settings_path().ok_or_else(|| { error!(dx_src = ?TraceSrc::Dev, "failed to get settings path"); CrateConfigError::Io(Error::new( @@ -100,7 +100,7 @@ impl CliSettings { } /// Get the path to the settings toml file. - pub(crate) fn get_settings_path() -> Option { + pub fn get_settings_path() -> Option { let Some(path) = dirs::data_local_dir() else { warn!("failed to get local data directory, some config keys may be missing"); return None; @@ -110,9 +110,7 @@ impl CliSettings { } /// Modify the settings toml file - pub(crate) fn modify_settings( - with: impl FnOnce(&mut CliSettings), - ) -> Result<(), CrateConfigError> { + pub fn modify_settings(with: impl FnOnce(&mut CliSettings)) -> Result<(), CrateConfigError> { let mut settings = Self::load(); with(&mut settings); settings.save()?; diff --git a/packages/cli/src/tooling/android.rs b/packages/cli/src/tooling/android.rs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/packages/cli/src/tooling/ios.rs b/packages/cli/src/tooling/ios.rs deleted file mode 100644 index 8b13789179..0000000000 --- a/packages/cli/src/tooling/ios.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/packages/cli/src/tooling/mac.rs b/packages/cli/src/tooling/mac.rs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/packages/cli/src/tooling/mod.rs b/packages/cli/src/tooling/mod.rs deleted file mode 100644 index 1bf3888d05..0000000000 --- a/packages/cli/src/tooling/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod android; -mod ios; -mod mac; -mod web; -mod wsl; diff --git a/packages/cli/src/tooling/web.rs b/packages/cli/src/tooling/web.rs deleted file mode 100644 index 6d3004304e..0000000000 --- a/packages/cli/src/tooling/web.rs +++ /dev/null @@ -1,63 +0,0 @@ -use crate::{Error, Result}; -use tokio::process::Command; - -pub struct ToolingProvider {} - -impl ToolingProvider { - /// Check if the wasm32-unknown-unknown target is installed and try to install it if not - pub(crate) async fn install_web_build_tooling(&self) -> Result<()> { - // If the user has rustup, we can check if the wasm32-unknown-unknown target is installed - // Otherwise we can just assume it is installed - which is not great... - // Eventually we can poke at the errors and let the user know they need to install the target - if let Ok(wasm_check_command) = Command::new("rustup").args(["show"]).output().await { - let wasm_check_output = String::from_utf8(wasm_check_command.stdout).unwrap(); - if !wasm_check_output.contains("wasm32-unknown-unknown") { - // _ = self.progress.unbounded_send(BuildUpdateProgress { - // stage: Stage::InstallingWasmTooling, - // update: UpdateStage::Start, - // platform: self.platform(), - // }); - tracing::info!("wasm32-unknown-unknown target not detected, installing.."); - let _ = Command::new("rustup") - .args(["target", "add", "wasm32-unknown-unknown"]) - .output() - .await?; - } - } - - Ok(()) - } - - // Attempt to automatically recover from a bindgen failure by updating the wasm-bindgen version - pub(crate) async fn update_wasm_bindgen_version() -> Result<()> { - let cli_bindgen_version = wasm_bindgen_shared::version(); - tracing::info!("Attempting to recover from bindgen failure by setting the wasm-bindgen version to {cli_bindgen_version}..."); - - let output = Command::new("cargo") - .args([ - "update", - "-p", - "wasm-bindgen", - "--precise", - &cli_bindgen_version, - ]) - .output() - .await; - - let mut error_message = None; - if let Ok(output) = output { - if output.status.success() { - tracing::info!("Successfully updated wasm-bindgen to {cli_bindgen_version}"); - return Ok(()); - } else { - error_message = Some(output); - } - } - - if let Some(output) = error_message { - tracing::error!("Failed to update wasm-bindgen: {:#?}", output); - } - - Err(Error::BuildFailed(format!("WASM bindgen build failed!\nThis is probably due to the Bindgen version, dioxus-cli is using `{cli_bindgen_version}` which is not compatible with your crate.\nPlease reinstall the dioxus cli to fix this issue.\nYou can reinstall the dioxus cli by running `cargo install dioxus-cli --force` and then rebuild your project"))) - } -} diff --git a/packages/cli/src/tooling/wsl.rs b/packages/cli/src/tooling/wsl.rs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/packages/cli/src/tracer.rs b/packages/cli/src/tracer.rs index a7765cde37..5016d79d9e 100644 --- a/packages/cli/src/tracer.rs +++ b/packages/cli/src/tracer.rs @@ -14,7 +14,7 @@ //! 3. Build CLI layer for routing tracing logs to the TUI. //! 4. Build fmt layer for non-interactive logging with a custom writer that prevents output during interactive mode. -use crate::Platform as TargetPlatform; +use crate::builder::TargetPlatform; use console::strip_ansi_codes; use futures_channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender}; use std::fmt::Display; @@ -404,8 +404,6 @@ impl Display for TraceSrc { TargetPlatform::Desktop => write!(f, "desktop"), TargetPlatform::Server => write!(f, "server"), TargetPlatform::Liveview => write!(f, "server"), - TargetPlatform::Ios => write!(f, "ios"), - TargetPlatform::Android => write!(f, "android"), }, Self::Dev => write!(f, "dev"), Self::Build => write!(f, "build"), diff --git a/packages/manganis-core/Cargo.toml b/packages/manganis-core/Cargo.toml deleted file mode 100644 index e4b10aa6d5..0000000000 --- a/packages/manganis-core/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "manganis-core" -edition = "2021" -version.workspace = true - -[dependencies] -serde_json = "1.0" -serde = { workspace = true, features = ["derive"] } diff --git a/packages/manganis-core/src/asset.rs b/packages/manganis-core/src/asset.rs deleted file mode 100644 index 3e01ac5edd..0000000000 --- a/packages/manganis-core/src/asset.rs +++ /dev/null @@ -1,119 +0,0 @@ -use serde::{Deserialize, Serialize}; -use std::{ - hash::{Hash, Hasher}, - path::PathBuf, - time::SystemTime, -}; - -/// The location we'll write to the link section - needs to be serializable -/// -/// This basically is 1:1 with `manganis/Asset` but with more metadata to be useful to the macro and cli -#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] -pub struct ResourceAsset { - /// The input path `/assets/blah.css` - pub input: PathBuf, - - /// The canonicalized asset - /// - /// `Users/dioxus/dev/app/assets/blah.css` - pub absolute: PathBuf, - - /// The post-bundle name of the asset - do we include the `assets` name? - /// - /// `blahcss123.css` - pub bundled: String, -} - -/// The maximum length of a path segment -const MAX_PATH_LENGTH: usize = 128; - -/// The length of the hash in the output path -const HASH_SIZE: usize = 16; - -#[derive(Debug)] -pub struct AssetError {} - -impl ResourceAsset { - pub fn parse_any(raw: &str) -> Result { - // get the location where the asset is absolute, relative to - // - // IE - // /users/dioxus/dev/app/ - // is the root of - // /users/dioxus/dev/app/assets/blah.css - let manifest_dir = std::env::var("CARGO_MANIFEST_DIR") - .map(PathBuf::from) - .unwrap(); - - // 1. the input file should be a pathbuf - let input = PathBuf::from(raw); - - // 2. absolute path to the asset - let absolute = manifest_dir - .join(raw.trim_start_matches('/')) - .canonicalize() - .unwrap(); - - // 3. the bundled path is the unique name of the asset - let bundled = Self::make_unique_name(absolute.clone()); - - Ok(Self { - input, - absolute, - bundled, - }) - } - - fn make_unique_name(file_path: PathBuf) -> String { - // Create a hasher - let mut hash = std::collections::hash_map::DefaultHasher::new(); - - // Open the file to get its options - let file = std::fs::File::open(&file_path).unwrap(); - let metadata = file.metadata().unwrap(); - let modified = metadata - .modified() - .unwrap_or_else(|_| SystemTime::UNIX_EPOCH); - - // Hash a bunch of metadata - // name, options, modified time, and maybe the version of our crate - // Hash the last time the file was updated and the file source. If either of these change, we need to regenerate the unique name - modified.hash(&mut hash); - file_path.hash(&mut hash); - - let uuid = hash.finish(); - let extension = file_path - .extension() - .map(|f| f.to_string_lossy()) - .map(|e| format!(".{e}")) - .unwrap_or_default(); - let file_name = Self::normalize_file_name(file_path); - - let out = format!("{file_name}{uuid:x}{extension}"); - assert!(out.len() <= MAX_PATH_LENGTH); - out - } - - fn normalize_file_name(location: PathBuf) -> String { - let file_name = location.file_name().unwrap(); - let last_segment = file_name.to_string_lossy(); - let extension = location.extension(); - let mut file_name = Self::to_alphanumeric_string_lossy(&last_segment); - - let extension_len = extension.map(|e| e.len() + 1).unwrap_or_default(); - let extension_and_hash_size = extension_len + HASH_SIZE; - - // If the file name is too long, we need to truncate it - if file_name.len() + extension_and_hash_size > MAX_PATH_LENGTH { - file_name = file_name[..MAX_PATH_LENGTH - extension_and_hash_size].to_string(); - } - - file_name - } - - fn to_alphanumeric_string_lossy(name: &str) -> String { - name.chars() - .filter(|c| c.is_alphanumeric()) - .collect::() - } -} diff --git a/packages/manganis-core/src/folder.rs b/packages/manganis-core/src/folder.rs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/packages/manganis-core/src/lib.rs b/packages/manganis-core/src/lib.rs deleted file mode 100644 index d4e991ad7e..0000000000 --- a/packages/manganis-core/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -mod asset; -mod folder; -mod linker; -mod options; - -pub use asset::*; -pub use linker::*; -pub use options::*; diff --git a/packages/manganis-core/src/linker.rs b/packages/manganis-core/src/linker.rs deleted file mode 100644 index e86444cf42..0000000000 --- a/packages/manganis-core/src/linker.rs +++ /dev/null @@ -1,69 +0,0 @@ -/// Information about the manganis link section for a given platform -#[derive(Debug, Clone, Copy)] -pub struct LinkSection { - /// The link section we pass to the static - pub link_section: &'static str, - /// The name of the section we find in the binary - pub name: &'static str, -} - -impl LinkSection { - /// The list of link sections for all supported platforms - pub const ALL: &'static [&'static LinkSection] = - &[Self::WASM, Self::MACOS, Self::WINDOWS, Self::ILLUMOS]; - - /// Returns the link section used in linux, android, fuchsia, psp, freebsd, and wasm32 - pub const WASM: &'static LinkSection = &LinkSection { - link_section: "manganis", - name: "manganis", - }; - - /// Returns the link section used in macOS, iOS, tvOS - pub const MACOS: &'static LinkSection = &LinkSection { - link_section: "__DATA,manganis,regular,no_dead_strip", - name: "manganis", - }; - - /// Returns the link section used in windows - pub const WINDOWS: &'static LinkSection = &LinkSection { - link_section: "mg", - name: "mg", - }; - - /// Returns the link section used in illumos - pub const ILLUMOS: &'static LinkSection = &LinkSection { - link_section: "set_manganis", - name: "set_manganis", - }; - - /// The link section used on the current platform - pub const CURRENT: &'static LinkSection = { - #[cfg(any( - target_os = "none", - target_os = "linux", - target_os = "android", - target_os = "fuchsia", - target_os = "psp", - target_os = "freebsd", - target_arch = "wasm32" - ))] - { - Self::WASM - } - - #[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos"))] - { - Self::MACOS - } - - #[cfg(target_os = "windows")] - { - Self::WINDOWS - } - - #[cfg(target_os = "illumos")] - { - Self::ILLUMOS - } - }; -} diff --git a/packages/manganis-core/src/options.rs b/packages/manganis-core/src/options.rs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/packages/manganis-macro/Cargo.toml b/packages/manganis-macro/Cargo.toml deleted file mode 100644 index e74cc7b899..0000000000 --- a/packages/manganis-macro/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -[package] -name = "manganis-macro" -version.workspace = true -edition = "2021" -authors = ["Evan Almloff"] -description = "Ergonomic, automatic, cross crate asset collection and optimization" -license = "MIT OR Apache-2.0" -repository = "https://github.com/DioxusLabs/manganis/" -homepage = "https://dioxuslabs.com" -keywords = ["assets"] - -[lib] -proc-macro = true - -[dependencies] -proc-macro2 = { version = "1.0" } -quote = "1.0" -syn = { version = "2.0", features = ["full", "extra-traits"] } -serde_json = "1.0" -serde = { workspace = true, features = ["derive"] } -manganis-core = { workspace = true } - -[features] -default = [] diff --git a/packages/manganis-macro/README.md b/packages/manganis-macro/README.md deleted file mode 100644 index fde983fef5..0000000000 --- a/packages/manganis-macro/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Manganis Macro - -This crate contains the macro used to interact with the Manganis asset system. diff --git a/packages/manganis-macro/src/asset.rs b/packages/manganis-macro/src/asset.rs deleted file mode 100644 index 7fbb25c041..0000000000 --- a/packages/manganis-macro/src/asset.rs +++ /dev/null @@ -1,139 +0,0 @@ -use core::panic; -use manganis_core::ResourceAsset; -use proc_macro::TokenStream; -use proc_macro2::Ident; -use proc_macro2::TokenStream as TokenStream2; -use quote::{quote, quote_spanned, ToTokens}; -use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, fs::File, path::Path, sync::atomic::AtomicBool}; -use std::{path::PathBuf, sync::atomic::Ordering}; -use syn::{ - parenthesized, - parse::{Parse, ParseStream}, - parse_macro_input, - punctuated::Punctuated, - token::Token, - Expr, ExprLit, Lit, LitStr, PatLit, Token, -}; - -use crate::asset_options::MethodCallOption; - -pub struct AssetParser { - /// The source of the trailing builder pattern - option_source: TokenStream2, - - /// The asset itself - asset: ResourceAsset, -} - -impl Parse for AssetParser { - // we can take - // - // This gives you the Asset type - it's generic and basically unrefined - // ``` - // asset!("myfile.png") - // ``` - // - // To narrow the type, use a method call to get the refined type - // ``` - // asset!( - // "myfile.png" - // .image() - // .format(ImageType::Jpg) - // .size(512, 512) - // ) - // ``` - // - // But we need to decide the hint first before parsing the options - fn parse(input: ParseStream) -> syn::Result { - // Get the source of the macro, excluding the first token - let option_source = { - let fork = input.fork(); - fork.parse::()?; - fork.parse::()? - }; - - // And then parse the options - let src = input.parse::()?; - let src = src.value(); - let resource = ResourceAsset::parse_any(&src).unwrap(); - - fn parse_call(input: ParseStream) -> syn::Result { - let method = input.parse::()?; - let content; - parenthesized!(content in input); - - let args = Punctuated::::parse_separated_nonempty(&content)?; - - Ok(MethodCallOption { method, args }) - } - - let mut options = vec![]; - - while !input.is_empty() { - let option = parse_call(input); - if let Ok(option) = option { - options.push(option); - } else { - // todo: make sure we toss a warning in the output - let _remaining: TokenStream2 = input.parse()?; - } - } - - Ok(Self { - option_source, - asset: resource, - }) - } -} - -impl ToTokens for AssetParser { - // Need to generate: - // - // - 1. absolute file path on the user's system: `/users/dioxus/dev/project/assets/blah.css` - // - 2. original input in case that's useful: `../blah.css` - // - 3. path relative to the CARGO_MANIFEST_DIR - and then we'll add a `/`: `/assets/blah.css - // - 4. file from which this macro was called: `/users/dioxus/dev/project/src/lib.rs` - // - 5: The link section containing all this data - // - 6: the input tokens such that the builder gets validated by the const code - // - 7: the bundled name `/blahcss123.css` - // - // Not that we'll use everything, but at least we have this metadata for more post-processing. - // - // For now, `2` and `3` will be the same since we don't support relative paths... a bit of - // a limitation from rust itself. We technically could support them but not without some hoops - // to jump through - fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { - // 1. the link section itself - let link_section = crate::generate_link_section(&self.asset); - - // 2. original - let input = self.asset.input.display().to_string(); - - // 3. resolved on the user's system - let local = self.asset.absolute.display().to_string(); - - // 4. bundled - let bundled = self.asset.bundled.to_string(); - - // 5. source tokens - let option_source = &self.option_source; - - tokens.extend(quote! { - Asset::new( - { - #link_section - manganis::Asset { - // "/assets/blah.css" - input: #input, - - // "/users/dioxus/dev/app/assets/blah.css" - local: #local, - - bundled: #bundled, - } - } - ) #option_source - }) - } -} diff --git a/packages/manganis-macro/src/asset_options.rs b/packages/manganis-macro/src/asset_options.rs deleted file mode 100644 index cda33625d3..0000000000 --- a/packages/manganis-macro/src/asset_options.rs +++ /dev/null @@ -1,77 +0,0 @@ -use core::panic; -use manganis_core::ResourceAsset; -use proc_macro::TokenStream; -use proc_macro2::Ident; -use proc_macro2::TokenStream as TokenStream2; -use quote::{quote, quote_spanned, ToTokens}; -use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, fs::File, path::Path, sync::atomic::AtomicBool}; -use std::{path::PathBuf, sync::atomic::Ordering}; -use syn::{ - parenthesized, - parse::{Parse, ParseStream}, - parse_macro_input, - punctuated::Punctuated, - token::Token, - Expr, ExprLit, Lit, LitStr, PatLit, Token, -}; - -pub struct MethodCalls { - pub options: Vec, -} - -/// A builder method in the form of `.method(arg1, arg2)` -pub struct MethodCallOption { - pub method: syn::Ident, - pub args: Punctuated, -} - -impl MethodCalls { - // fn new(args: Vec) -> Option { - // let asset_type = args.first()?.method.to_string(); - - // let stack = args - // .into_iter() - // .skip(1) - // .map(|x| (x.method.to_string(), x.args.into_iter().collect::>())) - // .collect::>>(); - - // let opts = match asset_type.as_str() { - // "image" => { - // let mut opts = ImageOptions::new(manganis_common::ImageType::Avif, Some((32, 32))); - // // opts.set_preload(preload); - // // opts.set_url_encoded(url_encoded); - // // opts.set_low_quality_preview(low_quality_preview); - // FileOptions::Image(opts) - // } - - // "video" => FileOptions::Video(VideoOptions::new(todo!())), - // "font" => FileOptions::Font(FontOptions::new(todo!())), - // "css" => FileOptions::Css(CssOptions::new()), - // "js" => FileOptions::Js(JsOptions::new(todo!())), - // "json" => FileOptions::Json(JsonOptions::new()), - // other => FileOptions::Other(UnknownFileOptions::new(todo!())), - // }; - - // Some(opts) - // None - // } -} - -// let local = match asset.local.as_ref() { -// Some(local) => { -// let local = local.display().to_string(); -// quote! { #local } -// } -// None => { -// todo!("relative paths are not supported yet") -// // quote! { -// // { -// // // ensure it exists by throwing away the include_bytes -// // static _BLAH: &[u8] = include_bytes!(#input); -// // // But then pass along the path -// // concat!(env!("CARGO_MANIFEST_DIR"), "/", file!(), "//", #input) -// // } -// // } -// } -// }; diff --git a/packages/manganis-macro/src/font.rs b/packages/manganis-macro/src/font.rs deleted file mode 100644 index 3bbac2412e..0000000000 --- a/packages/manganis-macro/src/font.rs +++ /dev/null @@ -1,173 +0,0 @@ -use manganis_common::{AssetType, CssOptions, ManganisSupportError, ResourceAsset}; -use quote::{quote, ToTokens}; -use syn::{bracketed, parenthesized, parse::Parse}; - -use crate::{generate_link_section, resource::ResourceAssetParser}; - -#[derive(Default)] -struct FontFamilies { - families: Vec, -} - -impl Parse for FontFamilies { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let inside; - bracketed!(inside in input); - let array = - syn::punctuated::Punctuated::::parse_separated_nonempty( - &inside, - )?; - Ok(FontFamilies { - families: array.into_iter().map(|f| f.value()).collect(), - }) - } -} - -#[derive(Default)] -struct FontWeights { - weights: Vec, -} - -impl Parse for FontWeights { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let inside; - bracketed!(inside in input); - let array = - syn::punctuated::Punctuated::::parse_separated_nonempty( - &inside, - )?; - Ok(FontWeights { - weights: array - .into_iter() - .map(|f| f.base10_parse().unwrap()) - .collect(), - }) - } -} - -struct ParseFontOptions { - families: FontFamilies, - weights: FontWeights, - text: Option, - display: Option, -} - -impl ParseFontOptions { - fn url(&self) -> String { - let mut segments = Vec::new(); - - let families: Vec<_> = self - .families - .families - .iter() - .map(|f| f.replace(' ', "+")) - .collect(); - if !families.is_empty() { - segments.push(format!("family={}", families.join("&"))); - } - - let weights: Vec<_> = self.weights.weights.iter().map(|w| w.to_string()).collect(); - if !weights.is_empty() { - segments.push(format!("weight={}", weights.join(","))); - } - - if let Some(text) = &self.text { - segments.push(format!("text={}", text.replace(' ', "+"))); - } - - if let Some(display) = &self.display { - segments.push(format!("display={}", display.replace(' ', "+"))); - } - - let query = if segments.is_empty() { - String::new() - } else { - format!("?{}", segments.join("&")) - }; - - format!("https://fonts.googleapis.com/css2{}", query) - } -} - -impl Parse for ParseFontOptions { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let mut families = None; - let mut weights = None; - let mut text = None; - let mut display = None; - loop { - if input.is_empty() { - break; - } - let _ = input.parse::()?; - let ident = input.parse::()?; - let inside; - parenthesized!(inside in input); - match ident.to_string().to_lowercase().as_str() { - "families" => { - families = Some(inside.parse::()?); - } - "weights" => { - weights = Some(inside.parse::()?); - } - "text" => { - text = Some(inside.parse::()?.value()); - } - "display" => { - display = Some(inside.parse::()?.value()); - } - _ => { - return Err(syn::Error::new( - proc_macro2::Span::call_site(), - format!("Unknown font option: {ident}. Supported options are families, weights, text, display"), - )) - } - } - } - - Ok(ParseFontOptions { - families: families.unwrap_or_default(), - weights: weights.unwrap_or_default(), - text, - display, - }) - } -} - -pub struct FontAssetParser { - asset: ResourceAsset, -} - -impl Parse for FontAssetParser { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let inside; - parenthesized!(inside in input); - if !inside.is_empty() { - return Err(syn::Error::new( - proc_macro2::Span::call_site(), - "Font assets do not support paths. Please use file() if you want to import a local font file", - )); - } - - let options = input.parse::()?; - - let url = options.url(); - let asset: ResourceAsset = match ResourceAsset::parse_file(&url) { - Ok(url) => url, - Err(e) => { - return Err(syn::Error::new( - proc_macro2::Span::call_site(), - format!("Failed to parse url: {url:?}\n{e}"), - )) - } - }; - - Ok(FontAssetParser { asset }) - } -} - -impl ToTokens for FontAssetParser { - fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { - ResourceAssetParser::to_ref_tokens(&self.asset, tokens) - } -} diff --git a/packages/manganis-macro/src/image.rs b/packages/manganis-macro/src/image.rs deleted file mode 100644 index 769cd57c09..0000000000 --- a/packages/manganis-macro/src/image.rs +++ /dev/null @@ -1,305 +0,0 @@ -use manganis_common::ManganisSupportError; -use manganis_common::{AssetType, FileOptions, ImageOptions, ResourceAsset}; -use quote::{quote, ToTokens}; -use syn::{parenthesized, parse::Parse, Token}; - -use crate::generate_link_section; - -struct ParseImageOptions { - options: Vec, -} - -impl ParseImageOptions { - fn apply_to_options(self, file: &mut ResourceAsset, low_quality_preview: &mut bool) { - for option in self.options { - option.apply_to_options(file, low_quality_preview); - } - } -} - -impl Parse for ParseImageOptions { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let mut options = Vec::new(); - while !input.is_empty() { - options.push(input.parse::()?); - } - Ok(ParseImageOptions { options }) - } -} - -enum ParseImageOption { - Format(manganis_common::ImageType), - Size((u32, u32)), - Preload(bool), - UrlEncoded(bool), - Lqip(bool), -} - -impl ParseImageOption { - fn apply_to_options(self, file: &mut ResourceAsset, low_quality_preview: &mut bool) { - match self { - ParseImageOption::Format(_) - | ParseImageOption::Size(_) - | ParseImageOption::Preload(_) => file.with_options_mut(|options| { - if let FileOptions::Image(options) = options { - match self { - ParseImageOption::Format(format) => { - options.set_ty(format); - } - ParseImageOption::Size(size) => { - options.set_size(Some(size)); - } - ParseImageOption::Preload(preload) => { - options.set_preload(preload); - } - _ => {} - } - } - }), - ParseImageOption::UrlEncoded(url_encoded) => { - file.set_url_encoded(url_encoded); - } - ParseImageOption::Lqip(lqip) => { - *low_quality_preview = lqip; - } - } - } -} - -impl Parse for ParseImageOption { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let _ = input.parse::()?; - let ident = input.parse::()?; - let content; - parenthesized!(content in input); - match ident.to_string().as_str() { - "format" => { - let format = content.parse::()?; - Ok(ParseImageOption::Format(format.into())) - } - "size" => { - let size = content.parse::()?; - Ok(ParseImageOption::Size((size.width, size.height))) - } - "preload" => { - crate::verify_preload_valid(&ident)?; - Ok(ParseImageOption::Preload(true)) - } - "url_encoded" => { - Ok(ParseImageOption::UrlEncoded(true)) - } - "low_quality_preview" => { - Ok(ParseImageOption::Lqip(true)) - } - _ => Err(syn::Error::new( - proc_macro2::Span::call_site(), - format!( - "Unknown image option: {}. Supported options are format, size, preload, url_encoded, low_quality_preview", - ident - ), - )), - } - } -} - -struct ImageSize { - width: u32, - height: u32, -} - -impl Parse for ImageSize { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let width = input.parse::()?; - let _ = input.parse::()?; - let height = input.parse::()?; - Ok(ImageSize { - width: width.base10_parse()?, - height: height.base10_parse()?, - }) - } -} - -impl From for manganis_common::ImageType { - fn from(val: ImageType) -> Self { - val.0 - } -} - -impl Parse for ImageType { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let _ = input.parse::()?; - let _ = input.parse::()?; - let ident = input.parse::()?; - ident - .to_string() - .to_lowercase() - .as_str() - .parse::() - .map_err(|_| { - syn::Error::new( - proc_macro2::Span::call_site(), - format!( - "Unknown image type: {}. Supported types are png, jpeg, webp, avif", - ident - ), - ) - }) - .map(Self) - } -} - -#[derive(Clone, Copy)] -struct ImageType(manganis_common::ImageType); - -impl Default for ImageType { - fn default() -> Self { - Self(manganis_common::ImageType::Avif) - } -} - -pub struct ImageAssetParser { - asset: ResourceAsset, - low_quality_preview: Option, -} - -impl Parse for ImageAssetParser { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let inside; - parenthesized!(inside in input); - let path = inside.parse::()?; - - let parsed_options = { - if input.is_empty() { - None - } else { - Some(input.parse::()?) - } - }; - - let path_as_str = path.value(); - let mut asset: ResourceAsset = match ResourceAsset::parse_file(&path_as_str) { - Ok(path) => path.with_options(manganis_common::FileOptions::Image(ImageOptions::new( - manganis_common::ImageType::Avif, - None, - ))), - Err(e) => { - return Err(syn::Error::new( - proc_macro2::Span::call_site(), - format!("{e}"), - )) - } - }; - - let mut low_quality_preview = false; - if let Some(parsed_options) = parsed_options { - parsed_options.apply_to_options(&mut asset, &mut low_quality_preview); - } - - // let asset = manganis_common::AssetType::Resource(asset.clone()); - - // let file_name = if asset.url_encoded() { - // #[cfg(not(feature = "url-encoding"))] - // return Err(syn::Error::new( - // proc_macro2::Span::call_site(), - // "URL encoding is not enabled. Enable the url-encoding feature to use this feature", - // )); - // #[cfg(feature = "url-encoding")] - // Ok(crate::url_encoded_asset(&asset).map_err(|e| { - // syn::Error::new( - // proc_macro2::Span::call_site(), - // format!("Failed to encode file: {}", e), - // ) - // })?) - // } else { - // asset.served_location() - // }; - - // let low_quality_preview = if low_quality_preview { - // #[cfg(not(feature = "url-encoding"))] - // return Err(syn::Error::new( - // proc_macro2::Span::call_site(), - // "Low quality previews require URL encoding. Enable the url-encoding feature to use this feature", - // )); - - // #[cfg(feature = "url-encoding")] - // { - // let current_image_size = match asset.options() { - // manganis_common::FileOptions::Image(options) => options.size(), - // _ => None, - // }; - // let low_quality_preview_size = current_image_size - // .map(|(width, height)| { - // let width = width / 10; - // let height = height / 10; - // (width, height) - // }) - // .unwrap_or((32, 32)); - // let lqip = ResourceAsset::new(asset).with_options( - // manganis_common::FileOptions::Image(ImageOptions::new( - // manganis_common::ImageType::Avif, - // Some(low_quality_preview_size), - // )), - // ); - - // Some(crate::url_encoded_asset(&lqip).map_err(|e| { - // syn::Error::new( - // proc_macro2::Span::call_site(), - // format!("Failed to encode file: {}", e), - // ) - // })?) - // } - // } else { - // None - // }; - - let low_quality_preview = None; - - Ok(ImageAssetParser { - low_quality_preview, - asset, - }) - } -} - -impl ToTokens for ImageAssetParser { - fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { - let link_section = generate_link_section(&self.asset); - let input = self.asset.input.to_string(); - - let bundled = self.asset.bundled.to_string(); - - let low_quality_preview = match &self.low_quality_preview { - Some(lqip) => quote! { Some(#lqip) }, - None => quote! { None }, - }; - - // If the asset is relative, we use concat!(env!("CARGO_MANIFEST_DIR"), "/", asset.input.path()) - let local = match self.asset.local.as_ref() { - Some(local) => { - let local = local.to_string(); - quote! { #local } - } - None => { - quote! { - { - static _: &[_] = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/", #input.path())); - concat!(env!("CARGO_MANIFEST_DIR"), "/", #input.path()) - } - } - } - }; - - tokens.extend(quote! { - { - #link_section - manganis::ImageAsset::new( - manganis::Asset { - input: #input, - local: #local, - bundled: #bundled, - } - ).with_preview(#low_quality_preview) - } - }) - } -} diff --git a/packages/manganis-macro/src/lib.rs b/packages/manganis-macro/src/lib.rs deleted file mode 100644 index 46f73bf840..0000000000 --- a/packages/manganis-macro/src/lib.rs +++ /dev/null @@ -1,104 +0,0 @@ -#![doc = include_str!("../README.md")] -#![deny(missing_docs)] - -use proc_macro::TokenStream; -use proc_macro2::Ident; -use proc_macro2::TokenStream as TokenStream2; -use quote::{quote, quote_spanned, ToTokens}; -use serde::Serialize; -use std::sync::atomic::AtomicBool; -use syn::{parse::Parse, parse_macro_input, LitStr}; - -pub(crate) mod asset; -pub(crate) mod asset_options; -pub(crate) mod linker; - -use linker::generate_link_section; - -/// The mg macro collects assets that will be included in the final binary -/// -/// # Files -/// -/// The file builder collects an arbitrary file. Relative paths are resolved relative to the package root -/// ```rust -/// const _: &str = manganis::asset!("src/asset.txt"); -/// ``` -/// Or you can use URLs to read the asset at build time from a remote location -/// ```rust -/// const _: &str = manganis::asset!("https://rustacean.net/assets/rustacean-flat-happy.png"); -/// ``` -/// -/// # Images -/// -/// You can collect images which will be automatically optimized with the image builder: -/// ```rust -/// const _: manganis::ImageAsset = manganis::asset!(image("rustacean-flat-gesture.png")); -/// ``` -/// Resize the image at compile time to make the assets file size smaller: -/// ```rust -/// const _: manganis::ImageAsset = manganis::asset!(image("rustacean-flat-gesture.png").size(52, 52)); -/// ``` -/// Or convert the image at compile time to a web friendly format: -/// ```rust -/// const _: manganis::ImageAsset = manganis::asset!(image("rustacean-flat-gesture.png").format(ImageFormat::Avif).size(52, 52)); -/// ``` -/// You can mark images as preloaded to make them load faster in your app -/// ```rust -/// const _: manganis::ImageAsset = manganis::asset!(image("rustacean-flat-gesture.png").preload()); -/// ``` -/// -/// # Fonts -/// -/// You can use the font builder to collect fonts that will be included in the final binary from google fonts -/// ```rust -/// const _: &str = manganis::asset!(font().families(["Roboto"])); -/// ``` -/// You can specify weights for the fonts -/// ```rust -/// const _: &str = manganis::asset!(font().families(["Roboto"]).weights([200])); -/// ``` -/// Or set the text to only include the characters you need -/// ```rust -/// const _: &str = manganis::asset!(font().families(["Roboto"]).weights([200]).text("Hello, world!")); -/// ``` -#[proc_macro] -pub fn asset(input: TokenStream) -> TokenStream { - let asset = parse_macro_input!(input as asset::AssetParser); - - quote! { #asset }.into_token_stream().into() -} - -/// // You can also collect arbitrary key-value pairs. The meaning of these pairs is determined by the CLI that processes your assets -/// ```rust -/// const _: () = manganis::meta!("opt-level": "3"); -/// ``` -#[proc_macro] -pub fn meta(input: TokenStream) -> TokenStream { - struct MetadataValue { - key: String, - value: String, - } - - impl Parse for MetadataValue { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let key = input.parse::()?.to_string(); - input.parse::()?; - let value = input.parse::()?.value(); - Ok(Self { key, value }) - } - } - - todo!() - - // let md = parse_macro_input!(input as MetadataValue); - // let asset = MetadataAsset::new(md.key.as_str(), md.value.as_str()); - // let link_section = generate_link_section(&asset); - - // quote! { - // { - // #link_section - // } - // } - // .into_token_stream() - // .into() -} diff --git a/packages/manganis-macro/src/linker.rs b/packages/manganis-macro/src/linker.rs deleted file mode 100644 index de77ab9240..0000000000 --- a/packages/manganis-macro/src/linker.rs +++ /dev/null @@ -1,100 +0,0 @@ -use proc_macro::TokenStream; -use proc_macro2::Ident; -use proc_macro2::TokenStream as TokenStream2; -use quote::{quote, quote_spanned, ToTokens}; -use serde::Serialize; -use std::sync::atomic::AtomicBool; -use syn::{parse::Parse, parse_macro_input, LitStr}; - -/// this new approach will store the assets descriptions *inside the executable*. -/// The trick is to use the `link_section` attribute. -/// We force rust to store a json representation of the asset description -/// inside a particular region of the binary, with the label "manganis". -/// After linking, the "manganis" sections of the different executables will be merged. -pub fn generate_link_section(asset: &impl Serialize) -> TokenStream2 { - let position = proc_macro2::Span::call_site(); - - let asset_description = serde_json::to_string(asset).unwrap(); - - let len = asset_description.as_bytes().len(); - - let asset_bytes = syn::LitByteStr::new(asset_description.as_bytes(), position); - - let section_name = syn::LitStr::new(LinkSection::CURRENT.link_section, position); - - quote! { - #[link_section = #section_name] - #[used] - static ASSET: [u8; #len] = * #asset_bytes; - } -} - -/// Information about the manganis link section for a given platform -#[derive(Debug, Clone, Copy)] -struct LinkSection { - /// The link section we pass to the static - pub link_section: &'static str, - /// The name of the section we find in the binary - pub name: &'static str, -} - -impl LinkSection { - /// The list of link sections for all supported platforms - pub const ALL: &'static [&'static LinkSection] = - &[Self::WASM, Self::MACOS, Self::WINDOWS, Self::ILLUMOS]; - - /// Returns the link section used in linux, android, fuchsia, psp, freebsd, and wasm32 - pub const WASM: &'static LinkSection = &LinkSection { - link_section: "manganis", - name: "manganis", - }; - - /// Returns the link section used in macOS, iOS, tvOS - pub const MACOS: &'static LinkSection = &LinkSection { - link_section: "__DATA,manganis,regular,no_dead_strip", - name: "manganis", - }; - - /// Returns the link section used in windows - pub const WINDOWS: &'static LinkSection = &LinkSection { - link_section: "mg", - name: "mg", - }; - - /// Returns the link section used in illumos - pub const ILLUMOS: &'static LinkSection = &LinkSection { - link_section: "set_manganis", - name: "set_manganis", - }; - - /// The link section used on the current platform - pub const CURRENT: &'static LinkSection = { - #[cfg(any( - target_os = "none", - target_os = "linux", - target_os = "android", - target_os = "fuchsia", - target_os = "psp", - target_os = "freebsd", - target_arch = "wasm32" - ))] - { - Self::WASM - } - - #[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos"))] - { - Self::MACOS - } - - #[cfg(target_os = "windows")] - { - Self::WINDOWS - } - - #[cfg(target_os = "illumos")] - { - Self::ILLUMOS - } - }; -} diff --git a/packages/manganis/Cargo.toml b/packages/manganis/Cargo.toml deleted file mode 100644 index 82d74cc674..0000000000 --- a/packages/manganis/Cargo.toml +++ /dev/null @@ -1,41 +0,0 @@ -[package] -# Manganese is a rusting catalyst. Manganis makes it faster to collect rust assets (and has almost no google search results) -name = "manganis" -version.workspace = true -authors = ["Evan Almloff"] -edition = "2021" -description = "Ergonomic, automatic, cross crate asset collection and optimization" -license = "MIT OR Apache-2.0" -repository = "https://github.com/DioxusLabs/manganis/" -homepage = "https://dioxuslabs.com" -keywords = ["assets"] - -[lib] - -[dependencies] -manganis-macro = { workspace = true, optional = true } -dioxus-core-types = { workspace = true } - -# dunce = "1.0.2" -# [target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies] -# core-foundation = "0.10.0" -# [target.'cfg(target_os = "macos")'.dependencies] -# core-foundation = "0.9.3" -# [target.'cfg(target_os ="macos")'.dependencies] -# infer = { workspace = true } -# dirs = "5.0.1" -# infer = { workspace = true } -# manganis-common = { workspace = true } - -once_cell = "1.19.0" -dunce = "1.0.2" -serde = { version = "1.0.183", features = ["derive"] } -anyhow = "1" -base64 = { workspace = true } - - -[features] -default = ["macro"] -html = [] -# url-encoding = ["manganis-macro/url-encoding"] -macro = ["dep:manganis-macro"] diff --git a/packages/manganis/src/asset.rs b/packages/manganis/src/asset.rs deleted file mode 100644 index 0f2993a3ef..0000000000 --- a/packages/manganis/src/asset.rs +++ /dev/null @@ -1,132 +0,0 @@ -/// This is basically a compile-time version of ResourceAsset -/// A struct that contains the relative and absolute paths of an asset -#[derive(Debug, PartialEq, PartialOrd, Clone, Hash)] -pub struct Asset { - /// The input URI given to the macro - pub input: &'static str, - - /// The sourcefile of the asset - pub source_file: &'static str, - - /// - pub local: &'static str, - - /// - pub bundled: &'static str, -} - -impl std::fmt::Display for Asset { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.resolve().fmt(f) - } -} - -impl From for String { - fn from(asset: Asset) -> Self { - asset.resolve() - } -} - -impl From for Option { - fn from(asset: Asset) -> Self { - Some(asset.resolve()) - } -} - -impl Asset { - /// Resolve the asset against the bundle - pub fn resolve(&self) -> String { - // A fallback for non-bundled apps with no support for manganis - // - // Necessary to get `cargo run` to work when folks use `cargo run --example demo` on the main - // dioxus repo. - // - // We could also say, suggest that they install `dioxus-cli` and use that instead. - if local_fallback() { - return self.bundled.to_string(); - } - - // the rest of the platforms are bundled, so we need to resolve the asset against the bundle - - // for web, we just do the basepath thing - #[cfg(target_arch = "wasm32")] - { - return format!("/{}", self.bundled); - } - - // On mac do a bundle lookup - #[cfg(target_os = "macos")] - { - let bundle = core_foundation::bundle::CFBundle::main_bundle(); - let bundle_path = bundle.path().unwrap(); - let resources_path = bundle.resources_path().unwrap(); - let absolute_resources_root = bundle_path.join(resources_path); - return dunce::canonicalize(absolute_resources_root) - .ok() - .unwrap() - .display() - .to_string(); - } - - // // on ios do a bundle lookup - // #[cfg(target_os = "ios")] - // { - // let bundle = core_foundation::bundle::CFBundle::main_bundle(); - // let bundle_path = bundle.path().unwrap(); - // let resources_path = bundle.resources_path().unwrap(); - // let absolute_resources_root = bundle_path.join(resources_path); - // return dunce::canonicalize(absolute_resources_root) - // .ok() - // .unwrap() - // .display() - // .to_string(); - // } - - // on android do a bundle lookup - - // on windows, - - todo!() - } - - fn name(&self) -> String { - if BUNDLED { - self.input.to_string() - } else { - self.local.to_string() - } - } -} - -static BUNDLED: bool = false; -// static BUNDLED: bool = option_env!("MG_BUNDLED").is_some(); - -/// Returns whether the app should use the local fallback or not -/// -/// A `cargo run` will not be bundled but the asset will be resolved against the filesystem through -/// dependencies. -pub fn local_fallback() -> bool { - // If we're bundled, manganis is active - if BUNDLED { - return false; - } - - // Otherwise, check if the MG_RUNTIME env var is set - // this prevents us from thrashing the cache when running `cargo run` - static USE_FALLBACK: once_cell::sync::OnceCell = once_cell::sync::OnceCell::new(); - *USE_FALLBACK.get_or_init(|| { - // If the env var is present, we use the bundled path - if std::env::var("MG_RUNTIME").is_ok() { - return false; - } - - // on wasm, there's no env vars... but the app is not bundled - // for now we just assume you're using manganis in a wasm app - if cfg!(target_arch = "wasm32") { - return false; - } - - // No env var, not wasm, not bundled, so we're not using manganis - true - }) -} diff --git a/packages/manganis/src/builder.rs b/packages/manganis/src/builder.rs deleted file mode 100644 index fff6d2fc83..0000000000 --- a/packages/manganis/src/builder.rs +++ /dev/null @@ -1,111 +0,0 @@ -use dioxus_core_types::DioxusFormattable; -use std::path::PathBuf; - -/// Asset -#[derive(Debug, PartialEq, PartialOrd, Clone, Copy, Hash)] -pub struct Asset { - /// The input URI given to the macro - pub input: &'static str, - - /// The absolute path to the asset on the filesystem - pub local: &'static str, - - /// The asset location after its been bundled - /// - /// `blah123.css`` - pub bundled: &'static str, -} - -impl Asset { - /// Create a new asset - pub const fn new(self) -> Self { - self - } - - /// Get the path to the asset - pub fn path(&self) -> PathBuf { - PathBuf::from(self.input.to_string()) - } - - /// Get the path to the asset - pub fn relative_path(&self) -> PathBuf { - PathBuf::from(self.input.trim_start_matches("/").to_string()) - } - - /// Return a canonicalized path to the asset - pub fn resolve(&self) -> PathBuf { - // if we're running with cargo in the loop, we can use the absolute path. - // this is non-bundled situations - if let Ok(_manifest_dir) = std::env::var("CARGO_MANIFEST_DIR") { - return PathBuf::from(self.local); - } - - // todo: actually properly resolve this - base_path() - .unwrap_or_else(|| std::env::current_dir().unwrap_or("/assets/".into())) - .join(PathBuf::from(self.bundled.trim_start_matches('/'))) - } -} - -impl From for String { - fn from(value: Asset) -> Self { - value.to_string() - } -} -impl From for Option { - fn from(value: Asset) -> Self { - Some(value.to_string()) - } -} - -impl std::fmt::Display for Asset { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.resolve().display()) - } -} - -impl DioxusFormattable for Asset { - fn format(&self) -> std::borrow::Cow<'static, str> { - std::borrow::Cow::Owned(self.to_string()) - } -} - -#[allow(unreachable_code)] -fn base_path() -> Option { - // Use the prescence of the bundle to determine if we're in dev mode - // todo: for other platforms, we should check their bundles too. This currently only works for macOS and iOS - #[cfg(any(target_os = "macos", target_os = "ios"))] - { - // usually the bundle is - // .app - // Contents - // Resources - // some_asset - // macOS - // somebinary - // - // but not always! - // - // we fallback to using the .app's directory itself if it doesn't exist - which is inline - // with how tauri-bundle works - // - // we would normally just want to use core-foundation, but it's much faster for compile times - // to not pull in CF in a build/proc-macro, so it's a teeny bit hand-rolled - let cur_exe = std::env::current_exe().ok()?; - let mut resources_dir = cur_exe.parent()?.parent()?.join("Resources"); - if !resources_dir.exists() { - resources_dir = cur_exe.parent()?.to_path_buf(); - } - - // Note that this will return `target/debug` if you're in debug mode - not reliable check if we're in dev mode - return dunce::canonicalize(resources_dir).ok(); - } - - // web-wasm - #[cfg(target_os = "wasm32-unknown-unknown")] - { - return = Some(PathBuf::from("/")) - } - - None -} diff --git a/packages/manganis/src/common/asset.rs b/packages/manganis/src/common/asset.rs deleted file mode 100644 index 6e1bb632cb..0000000000 --- a/packages/manganis/src/common/asset.rs +++ /dev/null @@ -1,47 +0,0 @@ -use std::{ - fmt::Display, - hash::{DefaultHasher, Hash, Hasher}, - path::{Path, PathBuf}, -}; - -use anyhow::Context; -use base64::Engine; -use serde::{Deserialize, Serialize}; - -use crate::{config, FileOptions}; - -// mod file; -// mod folder; -mod error; -mod file; -mod meta; -mod resource; -mod tailwind; - -// pub use folder::*; -pub use error::*; -pub use file::*; -pub use meta::*; -pub use resource::*; -pub use tailwind::*; - -/// The maximum length of a path segment -const MAX_PATH_LENGTH: usize = 128; - -/// The length of the hash in the output path -const HASH_SIZE: usize = 16; - -/// The type of asset -#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] -pub enum AssetType { - /// A resource asset in the form of a URI - /// - /// Typically a file, but could be a folder or a remote URL - Resource(ResourceAsset), - - /// A tailwind class asset - Tailwind(TailwindAsset), - - /// A metadata asset - Metadata(MetadataAsset), -} diff --git a/packages/manganis/src/common/asset/error.rs b/packages/manganis/src/common/asset/error.rs deleted file mode 100644 index e20e5a32e3..0000000000 --- a/packages/manganis/src/common/asset/error.rs +++ /dev/null @@ -1,62 +0,0 @@ -use std::{ - fmt::Display, - hash::{DefaultHasher, Hash, Hasher}, - path::{Path, PathBuf}, -}; - -use anyhow::Context; -use base64::Engine; -use serde::{Deserialize, Serialize}; - -use crate::{config, FileOptions}; - -/// Error while checking an asset exists -#[derive(Debug)] -pub enum AssetError { - /// The relative path does not exist - NotFoundRelative(PathBuf, String), - /// The path exist but is not a file - NotFile(PathBuf), - /// The path exist but is not a folder - NotFolder(PathBuf), - /// Unknown IO error - IO(PathBuf, std::io::Error), -} - -impl Display for AssetError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - AssetError::NotFoundRelative(manifest_dir, path) => - write!(f,"cannot find file `{}` in `{}`, please make sure it exists.\nAny relative paths are resolved relative to the manifest directory.", - path, - manifest_dir.display() - ), - AssetError::NotFile(absolute_path) => - write!(f, "`{}` is not a file, please choose a valid asset.\nAny relative paths are resolved relative to the manifest directory.", absolute_path.display()), - AssetError::NotFolder(absolute_path) => - write!(f, "`{}` is not a folder, please choose a valid asset.\nAny relative paths are resolved relative to the manifest directory.", absolute_path.display()), - AssetError::IO(absolute_path, err) => - write!(f, "unknown error when accessing `{}`: \n{}", absolute_path.display(), err) - } - } -} - -/// An error that can occur while collecting assets without CLI support -#[derive(Debug)] -pub enum ManganisSupportError { - /// An error that can occur while collecting assets from other packages without CLI support - ExternalPackageCollection, - /// Manganis failed to find the current package's manifest - FailedToFindCargoManifest, -} - -impl Display for ManganisSupportError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::ExternalPackageCollection => write!(f, "Attempted to collect assets from other packages without a CLI that supports Manganis. Please recompile with a CLI that supports Manganis like the `dioxus-cli`."), - Self::FailedToFindCargoManifest => write!(f, "Manganis failed to find the current package's manifest. Please recompile with a CLI that supports Manganis like the `dioxus-cli`."), - } - } -} - -impl std::error::Error for ManganisSupportError {} diff --git a/packages/manganis/src/common/asset/file.rs b/packages/manganis/src/common/asset/file.rs deleted file mode 100644 index 149bd8a8b7..0000000000 --- a/packages/manganis/src/common/asset/file.rs +++ /dev/null @@ -1,169 +0,0 @@ -use std::{ - fmt::Display, - hash::{DefaultHasher, Hash, Hasher}, - path::{Path, PathBuf}, -}; - -use anyhow::Context; -use base64::Engine; -use serde::{Deserialize, Serialize}; - -use crate::{config, FileOptions, ResourceAsset as AssetSource}; - -/// A file asset -#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] -pub struct FileAsset { - location: AssetSource, - options: FileOptions, - url_encoded: bool, -} - -/// A folder asset -#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] -pub struct FolderAsset { - location: AssetSource, -} - -impl FolderAsset { - /// - pub fn path(&self) -> &Path { - todo!() - } -} - -impl std::ops::Deref for FolderAsset { - type Target = AssetSource; - - fn deref(&self) -> &Self::Target { - &self.location - } -} - -impl std::ops::Deref for FileAsset { - type Target = AssetSource; - - fn deref(&self) -> &Self::Target { - &self.location - } -} - -impl FileAsset { - /// Creates a new file asset - pub fn new(source: AssetSource) -> Self { - todo!() - // if let Some(path) = source.as_path() { - // assert!(!path.is_dir()); - // } - - // let options = FileOptions::default_for_extension(source.extension().as_deref()); - - // let mut myself = Self { - // location: AssetSource { - // unique_name: Default::default(), - // source, - // }, - // options, - // url_encoded: false, - // }; - - // myself.regenerate_unique_name(); - - // myself - } - - /// Set the file options - pub fn with_options(self, options: FileOptions) -> Self { - let mut myself = Self { - location: self.location, - options, - url_encoded: false, - }; - - myself.regenerate_unique_name(); - - myself - } - - /// Set whether the file asset should be url encoded - pub fn set_url_encoded(&mut self, url_encoded: bool) { - self.url_encoded = url_encoded; - } - - /// Returns whether the file asset should be url encoded - pub fn url_encoded(&self) -> bool { - self.url_encoded - } - - // /// Returns the location where the file asset will be served from or None if the asset cannot be served - // pub fn served_location(&self) -> Result { - // if self.url_encoded { - // let data = self.location.source.read_to_bytes().unwrap(); - // let data = base64::engine::general_purpose::STANDARD_NO_PAD.encode(data); - // let mime = self.location.source.mime_type().unwrap(); - // Ok(format!("data:{mime};base64,{data}")) - // } else { - // resolve_asset_location(&self.location) - // } - // } - - /// Returns the location of the file asset - pub fn location(&self) -> &AssetSource { - &self.location - } - - /// Returns the options for the file asset - pub fn options(&self) -> &FileOptions { - &self.options - } - - /// - pub fn path(&self) -> &Path { - todo!() - } - - /// Returns the options for the file asset mutably - pub fn with_options_mut(&mut self, f: impl FnOnce(&mut FileOptions)) { - f(&mut self.options); - self.regenerate_unique_name(); - } - - /// Hash the file asset source and options - fn hash(&self) -> u64 { - todo!() - // let mut hash = std::collections::hash_map::DefaultHasher::new(); - // hash_file(&self.location.source, &mut hash); - // self.options.hash(&mut hash); - // hash_version(&mut hash); - // hash.finish() - } - - /// Regenerates the unique name of the file asset - fn regenerate_unique_name(&mut self) { - todo!() - // // Generate an unique name for the file based on the options, source, and the current version of manganis - // let uuid = self.hash(); - // let extension = self.options.extension(); - // let file_name = normalized_file_name(&self.location.source, extension); - // let extension = extension.map(|e| format!(".{e}")).unwrap_or_default(); - // self.location.unique_name = format!("{file_name}{uuid:x}{extension}"); - // assert!(self.location.unique_name.len() <= MAX_PATH_LENGTH); - } -} - -// impl Display for FileAsset { -// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { -// let url_encoded = if self.url_encoded { -// " [url encoded]" -// } else { -// "" -// }; - -// write!( -// f, -// "{} [{}]{}", -// self.location.source(), -// self.options, -// url_encoded -// ) -// } -// } diff --git a/packages/manganis/src/common/asset/folder.rs b/packages/manganis/src/common/asset/folder.rs deleted file mode 100644 index c782453da2..0000000000 --- a/packages/manganis/src/common/asset/folder.rs +++ /dev/null @@ -1,114 +0,0 @@ -use std::{ - fmt::Display, - hash::{DefaultHasher, Hash, Hasher}, - path::{Path, PathBuf}, -}; - -use anyhow::Context; -use base64::Engine; -use serde::{Deserialize, Serialize}; -use url::Url; - -use crate::{config, AssetSource, FileOptions}; - -/// A folder asset -#[derive(Serialize, Deserialize, Debug, PartialEq, PartialOrd, Clone)] -pub struct FolderAsset { - location: AssetSource, -} - -impl Display for FolderAsset { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}/**", self.location.source(),) - } -} - -impl FolderAsset { - /// Creates a new folder asset - pub fn new(source: AssetSource) -> Self { - let AssetSource::Local(source) = source else { - panic!("Folder asset must be a local path"); - }; - assert!(source.canonicalized.is_dir()); - - let mut myself = Self { - location: AssetSource { - unique_name: Default::default(), - source: AssetSource::Local(source), - }, - }; - - myself.regenerate_unique_name(); - - myself - } - - /// Returns the location where the folder asset will be served from or None if the asset cannot be served - pub fn served_location(&self) -> Result { - resolve_asset_location(&self.location) - } - - /// Returns the unique name of the folder asset - pub fn unique_name(&self) -> &str { - &self.location.unique_name - } - - /// Returns the location of the folder asset - pub fn location(&self) -> &AssetSource { - &self.location - } - - /// Create a unique hash for the source folder by recursively hashing the files - fn hash(&self) -> u64 { - let mut hash = std::collections::hash_map::DefaultHasher::new(); - let folder = self - .location - .source - .as_path() - .expect("Folder asset must be a local path"); - - let mut folders_queued = vec![folder.clone()]; - - while let Some(folder) = folders_queued.pop() { - // Add the folder to the hash - for segment in folder.iter() { - segment.hash(&mut hash); - } - - let files = std::fs::read_dir(folder).into_iter().flatten().flatten(); - for file in files { - let path = file.path(); - let metadata = path.metadata().unwrap(); - - // If the file is a folder, add it to the queue otherwise add it to the hash - if metadata.is_dir() { - folders_queued.push(path); - } else { - // todo: these relative/original paths are not correct - let local = self.location.source().local().unwrap(); - hash_file( - &AssetSource::Local(LocalAssetSource { - original: local.original.clone(), - relative: local.relative.clone(), - canonicalized: path, - }), - &mut hash, - ); - } - } - } - - // Add the manganis version to the hash - hash_version(&mut hash); - - hash.finish() - } - - /// Regenerate the unique name of the folder asset - fn regenerate_unique_name(&mut self) { - let uuid = self.hash(); - let file_name = normalized_file_name(&self.location.source, None); - self.location.unique_name = format!("{file_name}{uuid:x}"); - assert!(self.location.unique_name.len() <= MAX_PATH_LENGTH); - } -} diff --git a/packages/manganis/src/common/asset/meta.rs b/packages/manganis/src/common/asset/meta.rs deleted file mode 100644 index c62248861f..0000000000 --- a/packages/manganis/src/common/asset/meta.rs +++ /dev/null @@ -1,38 +0,0 @@ -use std::{ - fmt::Display, - hash::{DefaultHasher, Hash, Hasher}, - path::{Path, PathBuf}, -}; - -use anyhow::Context; -use base64::Engine; -use serde::{Deserialize, Serialize}; - -use crate::{config, FileOptions}; - -/// A metadata asset -#[derive(Serialize, Deserialize, Debug, PartialEq, PartialOrd, Clone)] -pub struct MetadataAsset { - key: String, - value: String, -} - -impl MetadataAsset { - /// Creates a new metadata asset - pub fn new(key: &str, value: &str) -> Self { - Self { - key: key.to_string(), - value: value.to_string(), - } - } - - /// Returns the key of the metadata asset - pub fn key(&self) -> &str { - &self.key - } - - /// Returns the value of the metadata asset - pub fn value(&self) -> &str { - &self.value - } -} diff --git a/packages/manganis/src/common/asset/resource.rs b/packages/manganis/src/common/asset/resource.rs deleted file mode 100644 index 7e3d79abe4..0000000000 --- a/packages/manganis/src/common/asset/resource.rs +++ /dev/null @@ -1,535 +0,0 @@ -use std::{ - fmt::Display, - hash::{DefaultHasher, Hash, Hasher}, - path::{Path, PathBuf}, - str::FromStr, -}; - -use anyhow::Context; -use base64::Engine; -// use fluent_uri::{ -// component::{Authority, Scheme}, -// encoding::EStr, -// UriRef, -// }; -use serde::{Deserialize, Serialize}; - -use crate::{config, AssetError, FileOptions}; - -/// An asset identified by a URI -/// -/// This could be a file, a folder, a remote URL, a data-encoded string, etc. -/// -/// We don't want to download or copy the resource itself, just the metadata about it such that -/// we can resolve it later. -#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Hash, Eq)] -pub struct ResourceAsset { - /// The input URI - /// - /// This is basically whatever the user passed in to the macro - pub input: PathBuf, - - /// The local URI for fallbacks - /// - /// This generally retains the original URI that was used to resolve the asset, but for files, - /// it's resolved to an absolute path since we transform all schema-less URIs to file:// URIs. - /// - /// If the aset is relative, this will be None since we can't figure it out at compile time. - pub local: Option, - - /// The output URI that makes it into the final bundle. - /// This explicitly has the `bundle://` scheme to make it clear that it is a bundle URI. - /// - /// The bundler will generate a unique name for the asset and use that as the path to generate a - /// final "flat" architecture. - /// - /// bundle://asset/path/to/file.txt - pub bundled: PathBuf, - - /// The options for the resource - pub options: Option, -} - -impl ResourceAsset { - /// - pub fn new(raw: &str) -> Self { - todo!() - } - - /// - pub fn unique_name(&self) -> &str { - todo!() - } - - /// Set the file options - pub fn with_options(self, options: FileOptions) -> Self { - todo!() - // let mut myself = Self { - // options, - // url_encoded: false, - // ..self - // }; - - // myself.regenerate_unique_name(); - - // myself - } - - /// - pub fn set_options(&mut self, options: FileOptions) { - self.options = Some(options); - } - - /// Set whether the file asset should be url encoded - pub fn set_url_encoded(&mut self, url_encoded: bool) { - todo!() - // self.url_encoded = url_encoded; - } - - /// Returns whether the file asset should be url encoded - pub fn url_encoded(&self) -> bool { - todo!() - // self.url_encoded - } - - /// Parse a string as a file source - pub fn parse_file(path: &str) -> Result { - // let myself = Self::parse_any(path)?; - // if let Self::Local(path) = &myself { - // if !path.canonicalized.is_file() { - // return Err(AssetError::NotFile(path.canonicalized.to_path_buf())); - // } - // } - // Ok(myself) - todo!() - } - - /// Parse a string as a folder source - pub fn parse_folder(path: &str) -> Result { - // let myself = Self::parse_any(path)?; - // if let Self::Local(path) = &myself { - // if !path.canonicalized.is_dir() { - // return Err(AssetError::NotFolder(path.canonicalized.to_path_buf())); - // } - // } - // Ok(myself) - todo!() - } - - /// - pub fn parse_url(url: &str) -> Result { - todo!() - } - - /// Parse a string as a file or folder source - pub fn parse_any(src: &str) -> Result { - todo!() - // // Process the input as a URI - // let input: UriRef = src.parse().unwrap(); - - // let local = match input.scheme().map(|x| x.as_str()) { - // // For http and https, we just use the input as is - // // In fallback mode we end up just passing the URI through - // Some("http") | Some("https") => Some(input.clone()), - - // // For file, we use the local path - // // This will be `file://` in dev - // // In release this will be `bundle://` - // // Join the URI against the filesystem - // None if input.path().is_absolute() => { - // tood!() - // // let manifest_dir: PathBuf = std::env::var("CARGO_MANIFEST_DIR").unwrap().into(); - // // let manifest_dir = manifest_dir.canonicalize().unwrap(); - // // let _local = manifest_dir.join(input.path().as_str()); - // // // Some(UriRef::::parse(format!("file://{}", _local.display())).unwrap()) - // } - // None => None, - - // Some(scheme) => { - // panic!("Unsupported scheme: {}", scheme); - // } - // }; - - // // Generate the bundled URI - // // - // // We: - // // - flatten the URI with a hash - // // - change the scheme to `bundle` - // // - add the authority of pkg-name.bundle - // // - // // This results in a bundle-per dependency - // let pkg_name = std::env::var("CARGO_PKG_NAME").unwrap(); - // let bundled = UriRef::builder() - // .scheme(Scheme::new_or_panic("bundle")) - // .authority_with(|b| b.host(EStr::new_or_panic(&format!("{}.bundle", pkg_name)))) - // .path(local.as_ref().map(|x| x.path()).unwrap_or_default()) - // .build() - // .unwrap(); - - // Ok(Self { - // input, - // local, - // bundled, - // options: None, - // }) - } - - // /// - // pub fn make_unique_id(uri: &UriRef) -> String { - // todo!() - // } - - /// - pub fn is_dir(&self) -> bool { - todo!() - } - - /// - pub fn resolve(&self) -> String { - // fn resolve_asset_location(location: &AssetSource) -> Result { - // if !config::is_bundled() { - // return Ok(location.source().raw()); - // } - - // let root = crate::config::base_path(); - // let path = root.join(location.unique_name()); - // Ok(path.display().to_string()) - // } - - todo!() - } - - /// - pub fn normalized(&self, extension: Option<&str>) -> String { - // /// Create a normalized file name from the source - // fn normalized_file_name(location: &AssetSource, extension: Option<&str>) -> String { - // let last_segment = location.last_segment(); - // let mut file_name = to_alphanumeric_string_lossy(last_segment); - - // let extension_len = extension.map(|e| e.len() + 1).unwrap_or_default(); - // let extension_and_hash_size = extension_len + HASH_SIZE; - // // If the file name is too long, we need to truncate it - // if file_name.len() + extension_and_hash_size > MAX_PATH_LENGTH { - // file_name = file_name[..MAX_PATH_LENGTH - extension_and_hash_size].to_string(); - // } - // file_name - // } - - // /// Normalize a string to only contain alphanumeric characters - // fn to_alphanumeric_string_lossy(name: &str) -> String { - // name.chars() - // .filter(|c| c.is_alphanumeric()) - // .collect::() - // } - - // fn hash_file(location: &AssetSource, hash: &mut DefaultHasher) { - // // Hash the last time the file was updated and the file source. If either of these change, we need to regenerate the unique name - // let updated = location.last_updated(); - // updated.hash(hash); - // location.hash(hash); - // } - - todo!() - } - - // /// Covnert the asset source to a string - // pub fn raw(&self) -> String { - // match self { - // Self::Local(path) => path.relative.display().to_string(), - // Self::Remote(url) => url.to_string(), - // } - // } - - // /// Try to convert the asset source to a local asset source - // pub fn local(&self) -> Option<&AssetSource> { - // match self { - // Self::Local(path) => Some(path), - // Self::Remote(_) => None, - // } - // } - - // /// Try to convert the asset source to a path - // pub fn as_path(&self) -> Option<&PathBuf> { - // match self { - // Self::Local(path) => Some(&path.canonicalized), - // Self::Remote(_) => None, - // } - // } - - // /// Try to convert the asset source to a url - // pub fn as_url(&self) -> Option<&Url> { - // match self { - // Self::Local(_) => None, - // Self::Remote(url) => Some(url), - // } - // } - - // /// Returns the last segment of the file source used to generate a unique name - // pub fn last_segment(&self) -> &str { - // match self { - // Self::Local(path) => path.canonicalized.file_name().unwrap().to_str().unwrap(), - // Self::Remote(url) => url.path_segments().unwrap().last().unwrap(), - // } - // } - - /// Returns the extension of the file source - pub fn extension(&self) -> Option { - // match self { - // Self::Local(path) => path - // .canonicalized - // .extension() - // .map(|e| e.to_str().unwrap().to_string()), - // Self::Remote(url) => reqwest::blocking::get(url.as_str()) - // .ok() - // .and_then(|request| { - // request - // .headers() - // .get("content-type") - // .and_then(|content_type| { - // content_type - // .to_str() - // .ok() - // .map(|ty| ext_of_mime(ty).to_string()) - // }) - // }), - // } - todo!() - } - - // /// Attempts to get the mime type of the file source - // pub fn mime_type(&self) -> Option { - // match self { - // Self::Local(path) => get_mime_from_path(&path.canonicalized) - // .ok() - // .map(|mime| mime.to_string()), - // Self::Remote(url) => reqwest::blocking::get(url.as_str()) - // .ok() - // .and_then(|request| { - // request - // .headers() - // .get("content-type") - // .and_then(|content_type| Some(content_type.to_str().ok()?.to_string())) - // }), - // } - // } - - // /// Find when the asset was last updated - // pub fn last_updated(&self) -> Option { - // match self { - // Self::Local(path) => path.canonicalized.metadata().ok().and_then(|metadata| { - // metadata - // .modified() - // .ok() - // .map(|modified| format!("{:?}", modified)) - // .or_else(|| { - // metadata - // .created() - // .ok() - // .map(|created| format!("{:?}", created)) - // }) - // }), - // Self::Remote(url) => reqwest::blocking::get(url.as_str()) - // .ok() - // .and_then(|request| { - // request - // .headers() - // .get("last-modified") - // .and_then(|last_modified| { - // last_modified - // .to_str() - // .ok() - // .map(|last_modified| last_modified.to_string()) - // }) - // }), - // } - // } - - /// Reads the file to a string - pub fn read_to_string(&self) -> anyhow::Result { - // match &self { - // AssetSource::Local(path) => Ok(std::fs::read_to_string(&path.canonicalized) - // .with_context(|| { - // format!( - // "Failed to read file from location: {}", - // path.canonicalized.display() - // ) - // })?), - // AssetSource::Remote(url) => { - // let response = reqwest::blocking::get(url.as_str()) - // .with_context(|| format!("Failed to asset from url: {}", url.as_str()))?; - // Ok(response.text().with_context(|| { - // format!("Failed to read text for asset from url: {}", url.as_str()) - // })?) - // } - // } - todo!() - } - - /// Reads the file to bytes - pub fn read_to_bytes(&self) -> anyhow::Result> { - // match &self { - // AssetSource::Local(path) => { - // Ok(std::fs::read(&path.canonicalized).with_context(|| { - // format!( - // "Failed to read file from location: {}", - // path.canonicalized.display() - // ) - // })?) - // } - // AssetSource::Remote(url) => { - // let response = reqwest::blocking::get(url.as_str()) - // .with_context(|| format!("Failed to asset from url: {}", url.as_str()))?; - // Ok(response.bytes().map(|b| b.to_vec()).with_context(|| { - // format!("Failed to read text for asset from url: {}", url.as_str()) - // })?) - // } - // } - todo!() - } - - /// The location where the asset will be served from post-bundle - /// This is not the "resolved" location at runtime - pub fn served_location(&self) -> Result { - todo!() - } - - // /// Returns the unique name of the file that the asset will be served from - // pub fn unique_name(&self) -> &str { - // &self.unique_name - // } - - // /// Returns the source of the file that the asset will be collected from - // pub fn source(&self) -> &AssetSource { - // &self.source - // } - - /// Returns the location of the file asset - pub fn location(&self) -> &ResourceAsset { - todo!() - // &self.location - } - - /// Returns the options for the file asset - pub fn options(&self) -> &FileOptions { - todo!() - // &self.options - } - - /// Returns the options for the file asset mutably - pub fn with_options_mut(&mut self, f: impl FnOnce(&mut FileOptions)) { - todo!() - // f(&mut self.options); - // self.regenerate_unique_name(); - } - - /// Regenerates the unique name of the file asset - fn regenerate_unique_name(&mut self) { - // // Generate an unique name for the file based on the options, source, and the current version of manganis - // let uuid = self.hash(); - // let extension = self.options.extension(); - // let file_name = normalized_file_name(&self.location.source, extension); - // let extension = extension.map(|e| format!(".{e}")).unwrap_or_default(); - // self.location.unique_name = format!("{file_name}{uuid:x}{extension}"); - // assert!(self.location.unique_name.len() <= MAX_PATH_LENGTH); - } - - // /// Hash the file asset source and options - // fn hash(&self) -> u64 { - // let mut hash = std::collections::hash_map::DefaultHasher::new(); - // hash_file(&self.location.source, &mut hash); - // self.options.hash(&mut hash); - // hash_version(&mut hash); - // hash.finish() - // } -} - -/// Get the mime type from a URI using its extension -fn ext_of_mime(mime: &str) -> &str { - let mime = mime.split(';').next().unwrap_or_default(); - match mime.trim() { - "application/octet-stream" => "bin", - "text/css" => "css", - "text/csv" => "csv", - "text/html" => "html", - "image/vnd.microsoft.icon" => "ico", - "text/javascript" => "js", - "application/json" => "json", - "application/ld+json" => "jsonld", - "application/rtf" => "rtf", - "image/svg+xml" => "svg", - "video/mp4" => "mp4", - "text/plain" => "txt", - "application/xml" => "xml", - "application/zip" => "zip", - "image/png" => "png", - "image/jpeg" => "jpg", - "image/gif" => "gif", - "image/webp" => "webp", - "image/avif" => "avif", - "font/ttf" => "ttf", - "font/woff" => "woff", - "font/woff2" => "woff2", - other => other.split('/').last().unwrap_or_default(), - } -} - -/// Get the mime type from a path-like string -fn get_mime_from_path(trimmed: &Path) -> std::io::Result<&'static str> { - todo!() - // if trimmed.extension().is_some_and(|ext| ext == "svg") { - // return Ok("image/svg+xml"); - // } - - // let res = match infer::get_from_path(trimmed)?.map(|f| f.mime_type()) { - // Some(f) => { - // if f == "text/plain" { - // get_mime_by_ext(trimmed) - // } else { - // f - // } - // } - // None => get_mime_by_ext(trimmed), - // }; - - // Ok(res) -} - -/// Get the mime type from a URI using its extension -fn get_mime_by_ext(trimmed: &Path) -> &'static str { - get_mime_from_ext(trimmed.extension().and_then(|e| e.to_str())) -} - -/// Get the mime type from a URI using its extension -pub fn get_mime_from_ext(extension: Option<&str>) -> &'static str { - match extension { - Some("bin") => "application/octet-stream", - Some("css") => "text/css", - Some("csv") => "text/csv", - Some("html") => "text/html", - Some("ico") => "image/vnd.microsoft.icon", - Some("js") => "text/javascript", - Some("json") => "application/json", - Some("jsonld") => "application/ld+json", - Some("mjs") => "text/javascript", - Some("rtf") => "application/rtf", - Some("svg") => "image/svg+xml", - Some("mp4") => "video/mp4", - Some("png") => "image/png", - Some("jpg") => "image/jpeg", - Some("gif") => "image/gif", - Some("webp") => "image/webp", - Some("avif") => "image/avif", - Some("txt") => "text/plain", - // Assume HTML when a TLD is found for eg. `dioxus:://dioxuslabs.app` | `dioxus://hello.com` - Some(_) => "text/html", - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types - // using octet stream according to this: - None => "application/octet-stream", - } -} - -fn hash_version(hash: &mut DefaultHasher) { - // // Hash the current version of manganis. If this changes, we need to regenerate the unique name - // crate::built::PKG_VERSION.hash(hash); - // crate::built::GIT_COMMIT_HASH.hash(hash); -} diff --git a/packages/manganis/src/common/asset/tailwind.rs b/packages/manganis/src/common/asset/tailwind.rs deleted file mode 100644 index 8284a80d2d..0000000000 --- a/packages/manganis/src/common/asset/tailwind.rs +++ /dev/null @@ -1,31 +0,0 @@ -use std::{ - fmt::Display, - hash::{DefaultHasher, Hash, Hasher}, - path::{Path, PathBuf}, -}; - -use anyhow::Context; -use base64::Engine; -use serde::{Deserialize, Serialize}; - -use crate::{config, FileOptions}; - -/// A tailwind class asset -#[derive(Serialize, Deserialize, Debug, PartialEq, PartialOrd, Clone)] -pub struct TailwindAsset { - classes: String, -} - -impl TailwindAsset { - /// Creates a new tailwind class asset - pub fn new(classes: &str) -> Self { - Self { - classes: classes.to_string(), - } - } - - /// Returns the classes of the tailwind class asset - pub fn classes(&self) -> &str { - &self.classes - } -} diff --git a/packages/manganis/src/common/built.rs b/packages/manganis/src/common/built.rs deleted file mode 100644 index cc9895a6a8..0000000000 --- a/packages/manganis/src/common/built.rs +++ /dev/null @@ -1,2 +0,0 @@ -// The file `built.rs` was placed there by cargo and `build.rs` -include!(concat!(env!("OUT_DIR"), "/built.rs")); diff --git a/packages/manganis/src/common/config.rs b/packages/manganis/src/common/config.rs deleted file mode 100644 index 590e94f92c..0000000000 --- a/packages/manganis/src/common/config.rs +++ /dev/null @@ -1,41 +0,0 @@ -use std::path::PathBuf; - -/// Get the base path for assets defined by the MG_BASEPATH environment variable -/// -/// The basepath should always start and end with a `/` -/// -/// If no basepath is set, the default is `/` which is the root of the assets folder. -pub fn base_path() -> PathBuf { - "/".into() - // match option_env!("MG_BASEPATH") { - // Some(path) => { - // let path = path.trim_end_matches('/').trim_start_matches('/'); - // PathBuf::from(format!("/{path}/")) - // } - // None => "/".into(), - // } -} - -/// MG_BUNDLED is set to true when the application is bundled. -/// -/// When running under a dev server, this is false to prevent thrashing of the cache since an ordinary -/// `cargo check` will not pass MG_BUNDLED. -pub const fn is_bundled() -> bool { - false - // option_env!("MG_BUNDLED").is_some() -} - -/// The location of the manifest directory used to build this crate -pub fn manifest_dir() -> Option { - std::env::var("CARGO_MANIFEST_DIR").ok().map(PathBuf::from) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn base_path_works() { - assert_eq!(base_path(), PathBuf::from("/")); - } -} diff --git a/packages/manganis/src/common/file.rs b/packages/manganis/src/common/file.rs deleted file mode 100644 index afd3a08f12..0000000000 --- a/packages/manganis/src/common/file.rs +++ /dev/null @@ -1,574 +0,0 @@ -use serde::{Deserialize, Serialize}; -use std::{fmt::Display, str::FromStr}; - -/// The options for a file asset -#[derive(Serialize, Deserialize, Debug, PartialEq, PartialOrd, Clone, Hash, Eq)] -pub enum FileOptions { - /// An image asset - Image(ImageOptions), - /// A video asset - Video(VideoOptions), - /// A font asset - Font(FontOptions), - /// A css asset - Css(CssOptions), - /// A JavaScript asset - Js(JsOptions), - /// A Json asset - Json(JsonOptions), - /// Any other asset - Other(UnknownFileOptions), -} - -impl Display for FileOptions { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Image(options) => write!(f, "{}", options), - Self::Video(options) => write!(f, "{}", options), - Self::Font(options) => write!(f, "{}", options), - Self::Css(options) => write!(f, "{}", options), - Self::Js(options) => write!(f, "{}", options), - Self::Json(options) => write!(f, "{}", options), - Self::Other(options) => write!(f, "{}", options), - } - } -} - -impl FileOptions { - /// Returns the default options for a given extension - pub fn default_for_extension(extension: Option<&str>) -> Self { - if let Some(extension) = extension { - if extension == CssOptions::EXTENSION { - return Self::Css(CssOptions::default()); - } else if extension == JsonOptions::EXTENSION { - return Self::Json(JsonOptions::default()); - } else if let Ok(ty) = extension.parse::() { - return Self::Image(ImageOptions::new(ty, None)); - } else if let Ok(ty) = extension.parse::() { - return Self::Video(VideoOptions::new(ty)); - } else if let Ok(ty) = extension.parse::() { - return Self::Font(FontOptions::new(ty)); - } else if let Ok(ty) = extension.parse::() { - return Self::Js(JsOptions::new(ty)); - } - } - Self::Other(UnknownFileOptions { - extension: extension.map(String::from), - }) - } - - /// Returns the extension for this file - pub fn extension(&self) -> Option<&str> { - match self { - Self::Image(options) => Some(options.ty.extension()), - Self::Video(options) => Some(options.ty.extension()), - Self::Font(options) => Some(options.ty.extension()), - Self::Css(_) => Some(CssOptions::EXTENSION), - Self::Js(js) => Some(js.ty.extension()), - Self::Json(_) => Some(JsonOptions::EXTENSION), - Self::Other(extension) => extension.extension.as_deref(), - } - } -} - -impl Default for FileOptions { - fn default() -> Self { - Self::Other(UnknownFileOptions { extension: None }) - } -} - -/// The options for an image asset -#[derive(Serialize, Deserialize, Debug, PartialEq, PartialOrd, Clone, Hash, Eq)] -pub struct ImageOptions { - compress: bool, - size: Option<(u32, u32)>, - preload: bool, - ty: ImageType, -} - -impl Display for ImageOptions { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if let Some((x, y)) = self.size { - write!(f, "{} ({}x{})", self.ty, x, y)?; - } else { - write!(f, "{}", self.ty)?; - } - if self.compress { - write!(f, " (compressed)")?; - } - if self.preload { - write!(f, " (preload)")?; - } - Ok(()) - } -} - -impl ImageOptions { - /// Creates a new image options struct - pub fn new(ty: ImageType, size: Option<(u32, u32)>) -> Self { - Self { - compress: true, - size, - ty, - preload: false, - } - } - - /// Returns whether the image should be preloaded - pub fn preload(&self) -> bool { - self.preload - } - - /// Sets whether the image should be preloaded - pub fn set_preload(&mut self, preload: bool) { - self.preload = preload; - } - - /// Returns the image type - pub fn ty(&self) -> &ImageType { - &self.ty - } - - /// Sets the image type - pub fn set_ty(&mut self, ty: ImageType) { - self.ty = ty; - } - - /// Returns the size of the image - pub fn size(&self) -> Option<(u32, u32)> { - self.size - } - - /// Sets the size of the image - pub fn set_size(&mut self, size: Option<(u32, u32)>) { - self.size = size; - } - - /// Returns whether the image should be compressed - pub fn compress(&self) -> bool { - self.compress - } - - /// Sets whether the image should be compressed - pub fn set_compress(&mut self, compress: bool) { - self.compress = compress; - } -} - -/// The type of an image -#[derive(Serialize, Deserialize, Debug, PartialEq, PartialOrd, Clone, Copy, Hash, Eq)] -pub enum ImageType { - /// A png image - Png, - /// A jpg image - Jpg, - /// An avif image - Avif, - /// A webp image - Webp, -} - -impl ImageType { - /// Returns the extension for this image type - pub fn extension(&self) -> &'static str { - match self { - Self::Png => "png", - Self::Jpg => "jpg", - Self::Avif => "avif", - Self::Webp => "webp", - } - } -} - -impl Display for ImageType { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.extension()) - } -} - -impl FromStr for ImageType { - type Err = (); - - fn from_str(s: &str) -> Result { - match s { - "png" => Ok(Self::Png), - "jpg" | "jpeg" => Ok(Self::Jpg), - "avif" => Ok(Self::Avif), - "webp" => Ok(Self::Webp), - _ => Err(()), - } - } -} - -/// The options for a video asset -#[derive(Serialize, Deserialize, Debug, PartialEq, PartialOrd, Clone, Hash, Eq)] -pub struct VideoOptions { - /// Whether the video should be compressed - compress: bool, - /// Whether the video should be preloaded - preload: bool, - /// The type of the video - ty: VideoType, -} - -impl Display for VideoOptions { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.ty)?; - if self.compress { - write!(f, " (compressed)")?; - } - if self.preload { - write!(f, " (preload)")?; - } - Ok(()) - } -} - -impl VideoOptions { - /// Creates a new video options struct - pub fn new(ty: VideoType) -> Self { - Self { - compress: true, - ty, - preload: false, - } - } - - /// Returns the type of the video - pub fn ty(&self) -> &VideoType { - &self.ty - } - - /// Sets the type of the video - pub fn set_ty(&mut self, ty: VideoType) { - self.ty = ty; - } - - /// Returns whether the video should be compressed - pub fn compress(&self) -> bool { - self.compress - } - - /// Sets whether the video should be compressed - pub fn set_compress(&mut self, compress: bool) { - self.compress = compress; - } - - /// Returns whether the video should be preloaded - pub fn preload(&self) -> bool { - self.preload - } - - /// Sets whether the video should be preloaded - pub fn set_preload(&mut self, preload: bool) { - self.preload = preload; - } -} - -/// The type of a video -#[derive(Serialize, Deserialize, Debug, PartialEq, PartialOrd, Clone, Hash, Eq)] -pub enum VideoType { - /// An mp4 video - MP4, - /// A webm video - Webm, - /// A gif video - GIF, -} - -impl VideoType { - /// Returns the extension for this video type - pub fn extension(&self) -> &'static str { - match self { - Self::MP4 => "mp4", - Self::Webm => "webm", - Self::GIF => "gif", - } - } -} - -impl Display for VideoType { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.extension()) - } -} - -impl FromStr for VideoType { - type Err = (); - - fn from_str(s: &str) -> Result { - match s { - "mp4" => Ok(Self::MP4), - "webm" => Ok(Self::Webm), - "gif" => Ok(Self::GIF), - _ => Err(()), - } - } -} - -/// The options for a font asset -#[derive(Serialize, Deserialize, Debug, PartialEq, PartialOrd, Clone, Hash, Eq)] -pub struct FontOptions { - ty: FontType, -} - -impl Display for FontOptions { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.ty) - } -} - -impl FontOptions { - /// Creates a new font options struct - pub fn new(ty: FontType) -> Self { - Self { ty } - } - - /// Returns the type of the font - pub fn ty(&self) -> &FontType { - &self.ty - } -} - -/// The type of a font -#[derive(Serialize, Deserialize, Debug, PartialEq, PartialOrd, Clone, Hash, Eq)] -pub enum FontType { - /// A ttf (TrueType) font - TTF, - /// A woff (Web Open Font Format) font - WOFF, - /// A woff2 (Web Open Font Format 2) font - WOFF2, -} - -impl FontType { - /// Returns the extension for this font type - pub fn extension(&self) -> &'static str { - match self { - Self::TTF => "ttf", - Self::WOFF => "woff", - Self::WOFF2 => "woff2", - } - } -} - -impl FromStr for FontType { - type Err = (); - - fn from_str(s: &str) -> Result { - match s { - "ttf" => Ok(Self::TTF), - "woff" => Ok(Self::WOFF), - "woff2" => Ok(Self::WOFF2), - _ => Err(()), - } - } -} - -impl Display for FontType { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::TTF => write!(f, "ttf"), - Self::WOFF => write!(f, "woff"), - Self::WOFF2 => write!(f, "woff2"), - } - } -} - -/// The options for a css asset -#[derive(Serialize, Deserialize, Debug, PartialEq, PartialOrd, Clone, Hash, Eq)] -pub struct CssOptions { - minify: bool, - preload: bool, -} - -impl Default for CssOptions { - fn default() -> Self { - Self::new() - } -} - -impl Display for CssOptions { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if self.minify { - write!(f, "minified")?; - } - if self.preload { - write!(f, " (preload)")?; - } - Ok(()) - } -} - -impl CssOptions { - const EXTENSION: &'static str = "css"; - - /// Creates a new css options struct - pub const fn new() -> Self { - Self { - minify: true, - preload: false, - } - } - - /// Returns whether the css should be minified - pub fn minify(&self) -> bool { - self.minify - } - - /// Sets whether the css should be minified - pub fn set_minify(&mut self, minify: bool) { - self.minify = minify; - } - - /// Returns whether the css should be preloaded - pub fn preload(&self) -> bool { - self.preload - } - - /// Sets whether the css should be preloaded - pub fn set_preload(&mut self, preload: bool) { - self.preload = preload; - } -} - -/// The type of a Javascript asset -#[derive(Serialize, Deserialize, Debug, PartialEq, PartialOrd, Clone, Copy, Hash, Default, Eq)] -pub enum JsType { - /// A js asset - #[default] - Js, - // TODO: support ts files -} - -impl JsType { - /// Returns the extension for this js type - pub fn extension(&self) -> &'static str { - match self { - Self::Js => "js", - } - } -} - -impl FromStr for JsType { - type Err = (); - - fn from_str(s: &str) -> Result { - match s { - "js" => Ok(Self::Js), - _ => Err(()), - } - } -} - -impl Display for JsType { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.extension()) - } -} - -/// The options for a Javascript asset -#[derive(Serialize, Deserialize, Debug, PartialEq, PartialOrd, Clone, Hash, Default, Eq)] -pub struct JsOptions { - ty: JsType, - minify: bool, - preload: bool, -} - -impl Display for JsOptions { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "js")?; - Ok(()) - } -} - -impl JsOptions { - /// Creates a new js options struct - pub fn new(ty: JsType) -> Self { - Self { - ty, - preload: false, - minify: true, - } - } - - /// Returns whether the js should be preloaded - pub fn preload(&self) -> bool { - self.preload - } - - /// Sets whether the js should be preloaded - pub fn set_preload(&mut self, preload: bool) { - self.preload = preload; - } - - /// Returns if the js should be minified - pub fn minify(&self) -> bool { - self.minify - } - - /// Sets if the js should be minified - pub fn set_minify(&mut self, minify: bool) { - self.minify = minify; - } -} - -/// The options for a Json asset -#[derive(Serialize, Deserialize, Debug, PartialEq, PartialOrd, Clone, Hash, Default, Eq)] -pub struct JsonOptions { - preload: bool, -} - -impl Display for JsonOptions { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "json")?; - Ok(()) - } -} - -impl JsonOptions { - /// The extension of the json asset - pub const EXTENSION: &'static str = "json"; - - /// Creates a new json options struct - pub fn new() -> Self { - Self { preload: false } - } - - /// Returns whether the json should be preloaded - pub fn preload(&self) -> bool { - self.preload - } - - /// Sets whether the json should be preloaded - pub fn set_preload(&mut self, preload: bool) { - self.preload = preload; - } -} - -/// The options for an unknown file asset -#[derive(Serialize, Deserialize, Debug, PartialEq, PartialOrd, Clone, Hash, Eq)] -pub struct UnknownFileOptions { - extension: Option, -} - -impl Display for UnknownFileOptions { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if let Some(extension) = &self.extension { - write!(f, "{}", extension)?; - } - Ok(()) - } -} - -impl UnknownFileOptions { - /// Creates a new unknown file options struct - pub fn new(extension: Option) -> Self { - Self { extension } - } - - /// Returns the extension of the file - pub fn extension(&self) -> Option<&str> { - self.extension.as_deref() - } -} diff --git a/packages/manganis/src/common/lib.rs b/packages/manganis/src/common/lib.rs deleted file mode 100644 index c4cc9f9088..0000000000 --- a/packages/manganis/src/common/lib.rs +++ /dev/null @@ -1,12 +0,0 @@ -#![deny(missing_docs)] -//! Common types and methods for the manganis asset system - -mod asset; -// mod built; -mod config; -mod file; - -pub mod linker; -pub use asset::*; -pub use config::*; -pub use file::*; diff --git a/packages/manganis/src/common/linker.rs b/packages/manganis/src/common/linker.rs deleted file mode 100644 index 82100b8314..0000000000 --- a/packages/manganis/src/common/linker.rs +++ /dev/null @@ -1,5 +0,0 @@ -//! name conventions used by the linker on different platforms. -//! This is used to make the "link_section" magic working -//! -// code taken from https://github.com/dtolnay/linkme/, -// MIT license diff --git a/packages/manganis/src/csss.rs b/packages/manganis/src/csss.rs deleted file mode 100644 index 670a54ffff..0000000000 --- a/packages/manganis/src/csss.rs +++ /dev/null @@ -1,65 +0,0 @@ -/// A builder for a css asset. This must be used in the [`asset!`] macro. -/// -/// > **Note**: This will do nothing outside of the `asset!` macro -pub struct CssAssetBuilder; - -impl CssAssetBuilder { - /// Sets whether the css should be minified (default: true) - /// - /// > **Note**: This will do nothing outside of the `asset!` macro - /// - /// Minifying the css can make your site load faster by loading less data - /// - /// ```rust - /// const _: &str = manganis::asset!(css("https://sindresorhus.com/github-markdown-css/github-markdown.css").minify(false)); - /// ``` - #[allow(unused)] - pub const fn minify(self, minify: bool) -> Self { - Self - } - - /// Make the css preloaded - /// - /// > **Note**: This will do nothing outside of the `asset!` macro - /// - /// Preloading css will make the css start to load as soon as possible. This is useful for css that will be displayed soon after the page loads or css that may not be visible immediately, but should start loading sooner - /// - /// ```rust - /// const _: &str = manganis::asset!(css("https://sindresorhus.com/github-markdown-css/github-markdown.css").preload()); - /// ``` - #[allow(unused)] - pub const fn preload(self) -> Self { - Self - } - - /// Make the css URL encoded - /// - /// > **Note**: This will do nothing outside of the `asset!` macro - /// - /// URL encoding an image inlines the data of the css into the URL. This is useful for small css files that should load as soon as the html is parsed - /// - /// ```rust - /// const _: &str = manganis::asset!(css("https://sindresorhus.com/github-markdown-css/github-markdown.css").url_encoded()); - /// ``` - #[allow(unused)] - pub const fn url_encoded(self) -> Self { - Self - } -} - -/// Create an css asset from the local path or url to the css -/// -/// > **Note**: This will do nothing outside of the `asset!` macro -/// -/// You can collect css which will be automatically minified with the css builder: -/// ```rust -/// const _: &str = manganis::asset!(css("https://sindresorhus.com/github-markdown-css/github-markdown.css")); -/// ``` -/// You can mark css as preloaded to make them load faster in your app: -/// ```rust -/// const _: &str = manganis::asset!(css("https://sindresorhus.com/github-markdown-css/github-markdown.css").preload()); -/// ``` -#[allow(unused)] -pub const fn css(path: &'static str) -> CssAssetBuilder { - CssAssetBuilder -} diff --git a/packages/manganis/src/files.rs b/packages/manganis/src/files.rs deleted file mode 100644 index 0501e984d2..0000000000 --- a/packages/manganis/src/files.rs +++ /dev/null @@ -1,16 +0,0 @@ -/// Create an file asset from the local path or url to the file -/// -/// > **Note**: This will do nothing outside of the `asset!` macro -/// -/// The file builder collects an arbitrary file. Relative paths are resolved relative to the package root -/// ```rust -/// const _: &str = manganis::asset!("/assets/asset.txt"); -/// ``` -/// Or you can use URLs to read the asset at build time from a remote location -/// ```rust -/// const _: &str = manganis::asset!("https://rustacean.net/assets/rustacean-flat-happy.png"); -/// ``` -#[allow(unused)] -pub const fn file(path: &'static str) -> &'static str { - path -} diff --git a/packages/manganis/src/folder.rs b/packages/manganis/src/folder.rs deleted file mode 100644 index 8731c5d511..0000000000 --- a/packages/manganis/src/folder.rs +++ /dev/null @@ -1,38 +0,0 @@ -use crate::Asset; - -/// This is basically a compile-time version of ResourceAsset -/// A struct that contains the relative and absolute paths of an asset -#[derive(Debug, PartialEq, PartialOrd, Clone, Hash)] -pub struct FolderAsset { - src: Asset, -} - -impl Asset { - /// - pub const fn folder(self) -> FolderAsset { - FolderAsset::new(self) - } -} - -impl FolderAsset { - /// - pub const fn new(src: Asset) -> Self { - Self { src } - } -} - -/// -pub struct FolderAssetBuilder; - -/// Create an folder asset from the local path -/// -/// > **Note**: This will do nothing outside of the `asset!` macro -/// -/// The folder builder collects an arbitrary local folder. Relative paths are resolved relative to the package root -/// ```rust -/// const _: &str = manganis::asset!("/assets"); -/// ``` -#[allow(unused)] -pub const fn folder(src: &'static str) -> FolderAssetBuilder { - FolderAssetBuilder {} -} diff --git a/packages/manganis/src/fonts.rs b/packages/manganis/src/fonts.rs deleted file mode 100644 index 1f69e56010..0000000000 --- a/packages/manganis/src/fonts.rs +++ /dev/null @@ -1,75 +0,0 @@ -/// A builder for a font asset. This must be used in the `asset!` macro. -/// -/// > **Note**: This will do nothing outside of the `asset!` macro -pub struct FontAssetBuilder; - -impl FontAssetBuilder { - /// Sets the font family of the font - /// - /// > **Note**: This will do nothing outside of the `asset!` macro - /// - /// ```rust - /// const _: &str = manganis::asset!(font().families(["Roboto"])); - /// ``` - #[allow(unused)] - pub const fn families(self, families: [&'static str; N]) -> Self { - Self - } - - /// Sets the font weight of the font - /// - /// > **Note**: This will do nothing outside of the `asset!` macro - /// - /// ```rust - /// const _: &str = manganis::asset!(font().families(["Roboto"]).weights([200])); - /// ``` - #[allow(unused)] - pub const fn weights(self, weights: [u32; N]) -> Self { - Self - } - - /// Sets the subset of text that the font needs to support. The font will only include the characters in the text which can make the font file size significantly smaller - /// - /// > **Note**: This will do nothing outside of the `asset!` macro - /// - /// ```rust - /// const _: &str = manganis::asset!(font().families(["Roboto"]).weights([200]).text("Hello, world!")); - /// ``` - #[allow(unused)] - pub const fn text(self, text: &'static str) -> Self { - Self - } - - /// Sets the [display](https://www.w3.org/TR/css-fonts-4/#font-display-desc) of the font. The display control what happens when the font is unavailable - /// - /// > **Note**: This will do nothing outside of the `asset!` macro - /// - /// ```rust - /// const _: &str = manganis::asset!(font().families(["Roboto"]).weights([200]).text("Hello, world!").display("swap")); - /// ``` - #[allow(unused)] - pub const fn display(self, display: &'static str) -> Self { - Self - } -} - -/// Create a font asset -/// -/// > **Note**: This will do nothing outside of the `asset!` macro -/// -/// You can use the font builder to collect fonts that will be included in the final binary from google fonts -/// ```rust -/// const _: &str = manganis::asset!(font().families(["Roboto"])); -/// ``` -/// You can specify weights for the fonts -/// ```rust -/// const _: &str = manganis::asset!(font().families(["Roboto"]).weights([200])); -/// ``` -/// Or set the text to only include the characters you need -/// ```rust -/// const _: &str = manganis::asset!(font().families(["Roboto"]).weights([200]).text("Hello, world!")); -/// ``` -#[allow(unused)] -pub const fn font() -> FontAssetBuilder { - FontAssetBuilder -} diff --git a/packages/manganis/src/images.rs b/packages/manganis/src/images.rs deleted file mode 100644 index 0e251efe88..0000000000 --- a/packages/manganis/src/images.rs +++ /dev/null @@ -1,197 +0,0 @@ -use dioxus_core_types::DioxusFormattable; - -use crate::Asset; - -/// An image asset that is built by the [`asset!`] macro -#[derive(Debug, PartialEq, PartialOrd, Clone, Hash, Copy)] -pub struct ImageAsset { - /// The path to the image - asset: Asset, - /// A low quality preview of the image that is URL encoded - preview: Option<&'static str>, - /// A caption for the image - caption: Option<&'static str>, -} - -impl Asset { - /// Convert this asset into an image asset - pub const fn image(self) -> ImageAsset { - ImageAsset::new(self) - } -} - -impl ImageAsset { - /// Creates a new image asset - pub const fn new(path: Asset) -> Self { - Self { - asset: path, - preview: None, - caption: None, - } - } - - /// Returns the path to the image - pub const fn path(&self) -> &'static str { - self.asset.bundled - } - - /// Returns the preview of the image - pub const fn preview(&self) -> Option<&'static str> { - self.preview - } - - /// Sets the preview of the image - pub const fn with_preview(self, preview: Option<&'static str>) -> Self { - Self { preview, ..self } - } - - /// Returns the caption of the image - pub const fn caption(&self) -> Option<&'static str> { - self.caption - } - - /// Sets the caption of the image - pub const fn with_caption(self, caption: Option<&'static str>) -> Self { - Self { caption, ..self } - } - - /// Sets the format of the image - pub const fn format(self, format: ImageType) -> Self { - Self { ..self } - } -} - -impl std::ops::Deref for ImageAsset { - type Target = Asset; - - fn deref(&self) -> &Self::Target { - &self.asset - } -} - -impl std::fmt::Display for ImageAsset { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.asset.fmt(f) - } -} - -impl DioxusFormattable for ImageAsset { - fn format(&self) -> std::borrow::Cow<'static, str> { - std::borrow::Cow::Owned(self.to_string()) - } -} - -/// The type of an image. You can read more about the tradeoffs between image formats [here](https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types) -#[derive(Debug, PartialEq, PartialOrd, Clone, Copy, Hash)] -pub enum ImageType { - /// A png image. Png images cannot contain transparency and tend to compress worse than other formats - Png, - /// A jpg image. Jpg images can contain transparency and tend to compress better than png images - Jpg, - /// A webp image. Webp images can contain transparency and tend to compress better than jpg images - Webp, - /// An avif image. Avif images can compress slightly better than webp images but are not supported by all browsers - Avif, -} - -/// A builder for an image asset. This must be used in the [`asset!`] macro. -/// -/// > **Note**: This will do nothing outside of the `asset!` macro -pub struct ImageAssetBuilder; - -impl ImageAssetBuilder { - /// Sets the format of the image - /// - /// > **Note**: This will do nothing outside of the `asset!` macro - /// - /// Choosing the right format can make your site load much faster. Webp and avif images tend to be a good default for most images - /// - /// ```rust - /// const _: manganis::ImageAsset = manganis::asset!(image("https://avatars.githubusercontent.com/u/79236386?s=48&v=4").format(ImageType::Webp)); - /// ``` - #[allow(unused)] - pub const fn format(self, format: ImageType) -> Self { - Self - } - - /// Sets the size of the image - /// - /// > **Note**: This will do nothing outside of the `asset!` macro - /// - /// If you only use the image in one place, you can set the size of the image to the size it will be displayed at. This will make the image load faster - /// - /// ```rust - /// const _: manganis::ImageAsset = manganis::asset!(image("https://avatars.githubusercontent.com/u/79236386?s=48&v=4").size(512, 512)); - /// ``` - #[allow(unused)] - pub const fn size(self, x: u32, y: u32) -> Self { - Self - } - - /// Make the image use a low quality preview - /// - /// > **Note**: This will do nothing outside of the `asset!` macro - /// - /// A low quality preview is a small version of the image that will load faster. This is useful for large images on mobile devices that may take longer to load - /// - /// ```rust - /// const _: manganis::ImageAsset = manganis::asset!(image("https://avatars.githubusercontent.com/u/79236386?s=48&v=4").low_quality_preview()); - /// ``` - #[allow(unused)] - pub const fn low_quality_preview(self) -> Self { - Self - } - - /// Make the image preloaded - /// - /// > **Note**: This will do nothing outside of the `asset!` macro - /// - /// Preloading an image will make the image start to load as soon as possible. This is useful for images that will be displayed soon after the page loads or images that may not be visible immediately, but should start loading sooner - /// - /// ```rust - /// const _: manganis::ImageAsset = manganis::asset!(image("https://avatars.githubusercontent.com/u/79236386?s=48&v=4").preload()); - /// ``` - #[allow(unused)] - pub const fn preload(self) -> Self { - Self - } - - /// Make the image URL encoded - /// - /// > **Note**: This will do nothing outside of the `asset!` macro - /// - /// URL encoding an image inlines the data of the image into the URL. This is useful for small images that should load as soon as the html is parsed - /// - /// ```rust - /// const _: manganis::ImageAsset = manganis::asset!(image("https://avatars.githubusercontent.com/u/79236386?s=48&v=4").url_encoded()); - /// ``` - #[allow(unused)] - pub const fn url_encoded(self) -> Self { - Self - } -} - -/// Create an image asset from the local path or url to the image -/// -/// > **Note**: This will do nothing outside of the `asset!` macro -/// -/// You can collect images which will be automatically optimized with the image builder: -/// ```rust -/// const _: manganis::ImageAsset = manganis::asset!(image("rustacean-flat-gesture.png")); -/// ``` -/// Resize the image at compile time to make the assets file size smaller: -/// ```rust -/// const _: manganis::ImageAsset = manganis::asset!(image("rustacean-flat-gesture.png").size(52, 52)); -/// ``` -/// Or convert the image at compile time to a web friendly format: -/// ```rust -/// const _: manganis::ImageAsset = manganis::asset!(image("rustacean-flat-gesture.png").format(ImageType::Avif).size(52, 52)); -/// ``` -/// You can mark images as preloaded to make them load faster in your app -/// ```rust -/// const _: manganis::ImageAsset = manganis::asset!(image("rustacean-flat-gesture.png").preload()); -/// ``` -#[allow(unused)] -pub const fn image(path: &'static str) -> ImageAssetBuilder { - ImageAssetBuilder -} diff --git a/packages/manganis/src/jsons.rs b/packages/manganis/src/jsons.rs deleted file mode 100644 index 429a0ab3f3..0000000000 --- a/packages/manganis/src/jsons.rs +++ /dev/null @@ -1,36 +0,0 @@ -use crate::Asset; - -/// A builder for a json asset. This must be used in the [`asset!`] macro. -/// -/// > **Note**: This will do nothing outside of the `asset!` macro -pub struct JsonAssetBuilder; - -impl JsonAssetBuilder { - /// Make the json preloaded - /// - /// > **Note**: This will do nothing outside of the `asset!` macro - /// - /// Preloading json will make the json start to load as soon as possible. This is useful for json that will be run soon after the page loads or json that may not be used immediately, but should start loading sooner - /// - /// ```rust - /// const _: &str = manganis::asset!(json("assets/data.json").preload()); - /// ``` - #[allow(unused)] - pub const fn preload(self) -> Self { - Self - } - - /// Make the json URL encoded - /// - /// > **Note**: This will do nothing outside of the `asset!` macro - /// - /// URL encoding an image inlines the data of the json into the URL. This is useful for small json files that should load as soon as the html is parsed - /// - /// ```rust - /// const _: &str = manganis::asset!(json("assets/data.json").url_encoded()); - /// ``` - #[allow(unused)] - pub const fn url_encoded(self) -> Self { - Self - } -} diff --git a/packages/manganis/src/jss.rs b/packages/manganis/src/jss.rs deleted file mode 100644 index 6b57483a56..0000000000 --- a/packages/manganis/src/jss.rs +++ /dev/null @@ -1,48 +0,0 @@ -/// A builder for a javascript asset. This must be used in the [`asset!`] macro. -/// -/// > **Note**: This will do nothing outside of the `asset!` macro -pub struct JsAssetBuilder; - -impl JsAssetBuilder { - /// Sets whether the js should be minified (default: true) - /// - /// > **Note**: This will do nothing outside of the `asset!` macro - /// - /// Minifying the js can make your site load faster by loading less data - /// - /// ```rust - /// const _: &str = manganis::asset!(js("assets/script.js").minify(false)); - /// ``` - #[allow(unused)] - pub const fn minify(self, minify: bool) -> Self { - Self - } - - /// Make the js preloaded - /// - /// > **Note**: This will do nothing outside of the `asset!` macro - /// - /// Preloading js will make the js start to load as soon as possible. This is useful for js that will be run soon after the page loads or js that may not be used immediately, but should start loading sooner - /// - /// ```rust - /// const _: &str = manganis::asset!(js("assets/script.js").preload()); - /// ``` - #[allow(unused)] - pub const fn preload(self) -> Self { - Self - } - - /// Make the js URL encoded - /// - /// > **Note**: This will do nothing outside of the `asset!` macro - /// - /// URL encoding an image inlines the data of the js into the URL. This is useful for small js files that should load as soon as the html is parsed - /// - /// ```rust - /// const _: &str = manganis::asset!(js("assets/script.js").url_encoded()); - /// ``` - #[allow(unused)] - pub const fn url_encoded(self) -> Self { - Self - } -} diff --git a/packages/manganis/src/lib.rs b/packages/manganis/src/lib.rs deleted file mode 100644 index 9680e40c97..0000000000 --- a/packages/manganis/src/lib.rs +++ /dev/null @@ -1,14 +0,0 @@ -#![doc = include_str!("../../../README.md")] -#![deny(missing_docs)] - -#[cfg(feature = "macro")] -pub use manganis_macro::*; - -mod folder; -pub use folder::*; - -mod images; -pub use images::*; - -mod builder; -pub use builder::*; diff --git a/packages/manganis/src/new.rs b/packages/manganis/src/new.rs deleted file mode 100644 index 61c88a281a..0000000000 --- a/packages/manganis/src/new.rs +++ /dev/null @@ -1,141 +0,0 @@ -/// -pub const fn video(self) -> VideoAsset { - VideoAsset::new(self.src) -} - -/// -pub const fn json(self) -> JsonAsset { - JsonAsset::new(self.src) -} - -/// -pub const fn css(self) -> CssAsset { - CssAsset::new(self.src) -} - -/// -pub const fn javascript(self) -> JavaScriptAsset { - JavaScriptAsset::new(self.src) -} - -/// -pub const fn typescript(self) -> TypeScriptAsset { - TypeScriptAsset::new(self.src) -} - -/// -pub struct CssAsset { - src: AssetSource, -} - -impl CssAsset { - /// - pub const fn new(src: AssetSource) -> Self { - Self { src } - } - - /// - pub const fn minify(self, minify: bool) -> Self { - todo!() - } -} - -/// -pub struct VideoAsset { - src: AssetSource, -} - -impl VideoAsset { - /// - pub const fn new(src: AssetSource) -> Self { - Self { src } - } -} - -/// -/// -pub struct JsonAsset { - src: AssetSource, -} -impl JsonAsset { - /// - pub const fn new(src: AssetSource) -> Self { - Self { src } - } - - /// Make the json preloaded - /// - /// > **Note**: This will do nothing outside of the `asset!` macro - /// - /// Preloading json will make the json start to load as soon as possible. This is useful for json that will be run soon after the page loads or json that may not be used immediately, but should start loading sooner - /// - /// ```rust - /// const _: &str = manganis::asset!(json("assets/data.json").preload()); - /// ``` - #[allow(unused)] - pub const fn preload(self) -> Self { - self - } - - /// Make the json URL encoded - /// - /// > **Note**: This will do nothing outside of the `asset!` macro - /// - /// URL encoding an image inlines the data of the json into the URL. This is useful for small json files that should load as soon as the html is parsed - /// - /// ```rust - /// const _: &str = manganis::asset!(json("assets/data.json").url_encoded()); - /// ``` - #[allow(unused)] - pub const fn url_encoded(self) -> Self { - self - } -} - -/// -/// -/// -pub struct JavaScriptAsset { - src: AssetSource, -} -impl JavaScriptAsset { - /// - pub const fn new(src: AssetSource) -> Self { - Self { src } - } -} - -/// -/// -pub struct TypeScriptAsset { - src: AssetSource, -} - -impl TypeScriptAsset { - /// - pub const fn new(src: AssetSource) -> Self { - Self { src } - } -} -/// -pub struct FolderAsset { - src: AssetSource, -} - -impl FolderAsset { - /// - pub const fn new(src: AssetSource) -> Self { - Self { src } - } - - /// - pub const fn build(self) -> FolderAsset { - FolderAsset { src: self.src } - } -} - -/// Asset -#[derive(Debug, PartialEq, Clone, Copy, Hash)] -pub struct ImageAsset { - src: AssetSource, -} diff --git a/packages/manganis/src/video.rs b/packages/manganis/src/video.rs deleted file mode 100644 index 0f7bbcf538..0000000000 --- a/packages/manganis/src/video.rs +++ /dev/null @@ -1,16 +0,0 @@ -/// Create a video asset from the local path or url to the video -/// -/// > **Note**: This will do nothing outside of the `mg!` macro -/// -/// The video builder collects an arbitrary file. Relative paths are resolved relative to the package root -/// ```rust -/// const _: &str = manganis::mg!(video("/assets/video.mp4")); -/// ``` -/// Or you can use URLs to read the asset at build time from a remote location -/// ```rust -/// const _: &str = manganis::mg!(video("https://private-user-images.githubusercontent.com/66571940/355646745-10781eef-de07-491d-aaa3-f75949b32190.mov?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjMxMzI5NTcsIm5iZiI6MTcyMzEzMjY1NywicGF0aCI6Ii82NjU3MTk0MC8zNTU2NDY3NDUtMTA3ODFlZWYtZGUwNy00OTFkLWFhYTMtZjc1OTQ5YjMyMTkwLm1vdj9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA4MDglMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwODA4VDE1NTczN1omWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTVkODEwZjI4ODE2ZmM4MjE3MWQ2ZDk3MjQ0NjQxYmZlMDI2OTAyMzhjNGU4MzlkYTdmZWM1MjI4ZWQ5NDg3M2QmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.jlX5E6WGjZeqZind6UCRLFrJ9NHcsV8xXy-Ls30tKPQ")); -/// ``` -#[allow(unused)] -pub const fn video(path: &'static str) -> &'static str { - path -} From da7d93107e3390d2479bf066f4d348d3b3e2f31c Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 26 Sep 2024 16:07:45 -0500 Subject: [PATCH 120/139] remove native and web features from dioxus-html --- Cargo.lock | 6 +- packages/desktop/Cargo.toml | 5 +- packages/desktop/build.rs | 9 + packages/desktop/src/app.rs | 4 +- packages/desktop/src/document.rs | 3 + packages/desktop/src/file_upload.rs | 54 +- packages/desktop/src/js/hash.txt | 1 + .../{html => desktop}/src/js/native_eval.js | 0 packages/desktop/src/protocol.rs | 2 +- .../{html => desktop}/src/ts/native_eval.ts | 6 +- packages/desktop/src/webview.rs | 15 +- packages/desktop/tsconfig.json | 18 + packages/html/Cargo.toml | 43 +- packages/html/build.rs | 2 - packages/html/src/document/bindings.rs | 50 -- packages/html/src/document/mod.rs | 2 - packages/html/src/js/hash.txt | 2 +- packages/html/src/lib.rs | 6 - packages/html/src/native_bind/mod.rs | 3 - .../src/native_bind/native_file_engine.rs | 56 -- packages/html/src/ts/eval.ts | 38 - packages/html/src/web_sys_bind/events.rs | 675 ------------------ packages/html/src/web_sys_bind/mod.rs | 5 - packages/web/Cargo.toml | 51 +- packages/web/build.rs | 7 + packages/web/src/document.rs | 48 +- packages/web/src/event.rs | 591 --------------- packages/web/src/events/animation.rs | 32 + packages/web/src/events/clipboard.rs | 26 + packages/web/src/events/composition.rs | 24 + packages/web/src/events/drag.rs | 108 +++ packages/web/src/events/file.rs | 24 + packages/web/src/events/focus.rs | 19 + packages/web/src/events/form.rs | 160 +++++ packages/web/src/events/keyboard.rs | 67 ++ packages/web/src/events/load.rs | 37 + packages/web/src/events/media.rs | 19 + packages/web/src/events/mod.rs | 278 ++++++++ packages/web/src/events/mounted.rs | 126 ++++ packages/web/src/events/mouse.rs | 87 +++ packages/web/src/events/pointer.rs | 119 +++ packages/web/src/events/resize.rs | 57 ++ packages/web/src/events/selection.rs | 19 + packages/web/src/events/toggle.rs | 19 + packages/web/src/events/touch.rs | 111 +++ packages/web/src/events/transition.rs | 22 + packages/web/src/events/wheel.rs | 90 +++ .../web_sys_bind => web/src}/file_engine.rs | 2 +- packages/{html => web}/src/js/eval.js | 2 +- packages/web/src/js/hash.txt | 1 + packages/web/src/lib.rs | 8 +- packages/web/src/ts/eval.ts | 43 ++ 52 files changed, 1701 insertions(+), 1501 deletions(-) create mode 100644 packages/desktop/src/js/hash.txt rename packages/{html => desktop}/src/js/native_eval.js (100%) rename packages/{html => desktop}/src/ts/native_eval.ts (96%) create mode 100644 packages/desktop/tsconfig.json delete mode 100644 packages/html/src/document/bindings.rs delete mode 100644 packages/html/src/native_bind/mod.rs delete mode 100644 packages/html/src/native_bind/native_file_engine.rs delete mode 100644 packages/html/src/web_sys_bind/events.rs delete mode 100644 packages/html/src/web_sys_bind/mod.rs create mode 100644 packages/web/build.rs delete mode 100644 packages/web/src/event.rs create mode 100644 packages/web/src/events/animation.rs create mode 100644 packages/web/src/events/clipboard.rs create mode 100644 packages/web/src/events/composition.rs create mode 100644 packages/web/src/events/drag.rs create mode 100644 packages/web/src/events/file.rs create mode 100644 packages/web/src/events/focus.rs create mode 100644 packages/web/src/events/form.rs create mode 100644 packages/web/src/events/keyboard.rs create mode 100644 packages/web/src/events/load.rs create mode 100644 packages/web/src/events/media.rs create mode 100644 packages/web/src/events/mod.rs create mode 100644 packages/web/src/events/mounted.rs create mode 100644 packages/web/src/events/mouse.rs create mode 100644 packages/web/src/events/pointer.rs create mode 100644 packages/web/src/events/resize.rs create mode 100644 packages/web/src/events/selection.rs create mode 100644 packages/web/src/events/toggle.rs create mode 100644 packages/web/src/events/touch.rs create mode 100644 packages/web/src/events/transition.rs create mode 100644 packages/web/src/events/wheel.rs rename packages/{html/src/web_sys_bind => web/src}/file_engine.rs (99%) rename packages/{html => web}/src/js/eval.js (93%) create mode 100644 packages/web/src/js/hash.txt create mode 100644 packages/web/src/ts/eval.ts diff --git a/Cargo.lock b/Cargo.lock index ea3c8f33a8..22c4eb7859 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2580,6 +2580,7 @@ dependencies = [ "global-hotkey", "http-range", "infer 0.11.0", + "lazy-js-bundle", "muda", "objc", "objc_id", @@ -2751,9 +2752,6 @@ dependencies = [ "serde_repr", "tokio", "tracing", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", ] [[package]] @@ -3056,6 +3054,7 @@ dependencies = [ name = "dioxus-web" version = "0.6.0-alpha.2" dependencies = [ + "async-trait", "ciborium", "console_error_panic_hook", "dioxus", @@ -3073,6 +3072,7 @@ dependencies = [ "gloo-dialogs", "gloo-timers 0.2.6", "js-sys", + "lazy-js-bundle", "rustc-hash 1.1.0", "serde", "serde-wasm-bindgen", diff --git a/packages/desktop/Cargo.toml b/packages/desktop/Cargo.toml index 4bccbe18dc..08f281caf1 100644 --- a/packages/desktop/Cargo.toml +++ b/packages/desktop/Cargo.toml @@ -13,8 +13,8 @@ keywords = ["dom", "ui", "gui", "react"] dioxus-core = { workspace = true, features = ["serialize"] } dioxus-html = { workspace = true, features = [ "serialize", - "native-bind", "mounted", + "file_engine", "document", ] } dioxus-signals = { workspace = true, optional = true } @@ -88,6 +88,9 @@ cocoa = "0.25" core-foundation = "0.9.3" objc = "0.2.7" +[build-dependencies] +lazy-js-bundle = { workspace = true } + [features] default = ["tokio_runtime", "wry/objc-exception", "devtools"] tokio_runtime = ["dep:tokio"] diff --git a/packages/desktop/build.rs b/packages/desktop/build.rs index b08ad66cf5..db08a8f551 100644 --- a/packages/desktop/build.rs +++ b/packages/desktop/build.rs @@ -36,8 +36,17 @@ fn check_gnu() { } } +fn compile_ts() { + // If any TS files change, re-run the build script + lazy_js_bundle::LazyTypeScriptBindings::new() + .with_watching("./src/ts") + .with_binding("./src/ts/native_eval.ts", "./src/js/native_eval.js") + .run(); +} + fn main() { check_gnu(); + compile_ts(); } const EXAMPLES_TOML: &str = r#" diff --git a/packages/desktop/src/app.rs b/packages/desktop/src/app.rs index c35936cf99..830891cda8 100644 --- a/packages/desktop/src/app.rs +++ b/packages/desktop/src/app.rs @@ -1,14 +1,14 @@ use crate::{ config::{Config, WindowCloseBehaviour}, event_handlers::WindowEventHandlers, - file_upload::{DesktopFileUploadForm, FileDialogRequest}, + file_upload::{DesktopFileUploadForm, FileDialogRequest, NativeFileEngine}, ipc::{IpcMessage, UserWindowEvent}, query::QueryResult, shortcut::ShortcutRegistry, webview::WebviewInstance, }; use dioxus_core::{ElementId, VirtualDom}; -use dioxus_html::{native_bind::NativeFileEngine, PlatformEventData}; +use dioxus_html::PlatformEventData; use std::{ any::Any, cell::{Cell, RefCell}, diff --git a/packages/desktop/src/document.rs b/packages/desktop/src/document.rs index 707b9d8322..26c724ff4b 100644 --- a/packages/desktop/src/document.rs +++ b/packages/desktop/src/document.rs @@ -3,6 +3,9 @@ use generational_box::{AnyStorage, GenerationalBox, UnsyncStorage}; use crate::{query::Query, DesktopContext}; +/// Code for the Dioxus channel used to communicate between the dioxus and javascript code +pub const NATIVE_EVAL_JS: &str = include_str!("./js/native_eval.js"); + /// Represents the desktop-target's provider of evaluators. pub struct DesktopDocument { pub(crate) desktop_ctx: DesktopContext, diff --git a/packages/desktop/src/file_upload.rs b/packages/desktop/src/file_upload.rs index dba431ef4d..dab4fcac47 100644 --- a/packages/desktop/src/file_upload.rs +++ b/packages/desktop/src/file_upload.rs @@ -1,9 +1,13 @@ #![allow(unused)] +use std::any::Any; + +use tokio::fs::File; +use tokio::io::AsyncReadExt; + use dioxus_html::{ geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint}, input_data::{MouseButton, MouseButtonSet}, - native_bind::NativeFileEngine, point_interaction::{ InteractionElementOffset, InteractionLocation, ModifiersInteraction, PointerInteraction, }, @@ -237,3 +241,51 @@ impl PointerInteraction for DesktopFileDragEvent { self.mouse.trigger_button() } } + +pub struct NativeFileEngine { + files: Vec, +} + +impl NativeFileEngine { + pub fn new(files: Vec) -> Self { + Self { files } + } +} + +#[async_trait::async_trait(?Send)] +impl FileEngine for NativeFileEngine { + fn files(&self) -> Vec { + self.files + .iter() + .filter_map(|f| Some(f.to_str()?.to_string())) + .collect() + } + + async fn file_size(&self, file: &str) -> Option { + let file = File::open(file).await.ok()?; + Some(file.metadata().await.ok()?.len()) + } + + async fn read_file(&self, file: &str) -> Option> { + let mut file = File::open(file).await.ok()?; + + let mut contents = Vec::new(); + file.read_to_end(&mut contents).await.ok()?; + + Some(contents) + } + + async fn read_file_to_string(&self, file: &str) -> Option { + let mut file = File::open(file).await.ok()?; + + let mut contents = String::new(); + file.read_to_string(&mut contents).await.ok()?; + + Some(contents) + } + + async fn get_native_file(&self, file: &str) -> Option> { + let file = File::open(file).await.ok()?; + Some(Box::new(file)) + } +} diff --git a/packages/desktop/src/js/hash.txt b/packages/desktop/src/js/hash.txt new file mode 100644 index 0000000000..6287f751bc --- /dev/null +++ b/packages/desktop/src/js/hash.txt @@ -0,0 +1 @@ +[11927251734412729446] \ No newline at end of file diff --git a/packages/html/src/js/native_eval.js b/packages/desktop/src/js/native_eval.js similarity index 100% rename from packages/html/src/js/native_eval.js rename to packages/desktop/src/js/native_eval.js diff --git a/packages/desktop/src/protocol.rs b/packages/desktop/src/protocol.rs index 295a00e550..2c159ef8a7 100644 --- a/packages/desktop/src/protocol.rs +++ b/packages/desktop/src/protocol.rs @@ -1,5 +1,5 @@ +use crate::document::NATIVE_EVAL_JS; use crate::{assets::*, webview::WebviewEdits}; -use dioxus_html::document::NATIVE_EVAL_JS; use dioxus_interpreter_js::unified_bindings::SLEDGEHAMMER_JS; use dioxus_interpreter_js::NATIVE_JS; use serde::Deserialize; diff --git a/packages/html/src/ts/native_eval.ts b/packages/desktop/src/ts/native_eval.ts similarity index 96% rename from packages/html/src/ts/native_eval.ts rename to packages/desktop/src/ts/native_eval.ts index b4152074b8..10c4d6aab0 100644 --- a/packages/html/src/ts/native_eval.ts +++ b/packages/desktop/src/ts/native_eval.ts @@ -1,4 +1,8 @@ -import { Channel, DioxusChannel, WeakDioxusChannel } from "./eval"; +import { + Channel, + DioxusChannel, + WeakDioxusChannel, +} from "../../../html/src/ts/eval"; // In dioxus desktop, eval needs to use the window object to store global state because we evaluate separate snippets of javascript in the browser declare global { diff --git a/packages/desktop/src/webview.rs b/packages/desktop/src/webview.rs index d09cae6ced..875ee9eff2 100644 --- a/packages/desktop/src/webview.rs +++ b/packages/desktop/src/webview.rs @@ -3,15 +3,18 @@ use crate::element::DesktopElement; use crate::file_upload::DesktopFileDragEvent; use crate::menubar::DioxusMenu; use crate::{ - app::SharedContext, assets::AssetHandlerRegistry, edits::WryQueue, - file_upload::NativeFileHover, ipc::UserWindowEvent, protocol, waker::tao_waker, Config, - DesktopContext, DesktopService, + app::SharedContext, + assets::AssetHandlerRegistry, + edits::WryQueue, + file_upload::{NativeFileEngine, NativeFileHover}, + ipc::UserWindowEvent, + protocol, + waker::tao_waker, + Config, DesktopContext, DesktopService, }; use dioxus_core::{Runtime, ScopeId, VirtualDom}; use dioxus_hooks::to_owned; -use dioxus_html::{ - native_bind::NativeFileEngine, prelude::Document, HasFileData, HtmlEvent, PlatformEventData, -}; +use dioxus_html::{prelude::Document, HasFileData, HtmlEvent, PlatformEventData}; use futures_util::{pin_mut, FutureExt}; use std::cell::OnceCell; use std::sync::Arc; diff --git a/packages/desktop/tsconfig.json b/packages/desktop/tsconfig.json new file mode 100644 index 0000000000..11cda68e3f --- /dev/null +++ b/packages/desktop/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "module": "CommonJS", + "lib": [ + "ES2015", + "DOM", + "dom", + "dom.iterable", + "ESNext" + ], + "noImplicitAny": true, + "removeComments": true, + "preserveConstEnums": true, + }, + "exclude": [ + "**/*.spec.ts" + ] +} diff --git a/packages/html/Cargo.toml b/packages/html/Cargo.toml index 9bcdff30a5..a123602542 100644 --- a/packages/html/Cargo.toml +++ b/packages/html/Cargo.toml @@ -19,8 +19,6 @@ dioxus-hooks = { workspace = true } generational-box = { workspace = true } serde = { version = "1", features = ["derive"], optional = true } serde_repr = { version = "0.1", optional = true } -wasm-bindgen = { workspace = true, optional = true } -wasm-bindgen-futures = { workspace = true, optional = true } js-sys = { version = "0.3.56", optional = true } euclid = "0.22.7" enumset = "1.1.2" @@ -32,30 +30,6 @@ serde_json = { version = "1", optional = true } tracing.workspace = true rustversion = "1.0.17" -[dependencies.web-sys] -optional = true -version = "0.3.56" -features = [ - "Touch", - "TouchList", - "TouchEvent", - "MouseEvent", - "DragEvent", - "InputEvent", - "HtmlInputElement", - "ClipboardEvent", - "KeyboardEvent", - "WheelEvent", - "AnimationEvent", - "TransitionEvent", - "PointerEvent", - "FocusEvent", - "CompositionEvent", - "CustomEvent", - "ResizeObserverEntry", - "ResizeObserverSize" -] - [build-dependencies] lazy-js-bundle = { workspace = true } @@ -76,30 +50,17 @@ serialize = [ "keyboard-types/serde", "dioxus-core/serialize" ] -mounted = [ - "web-sys?/Element", - "web-sys?/DomRect", - "web-sys?/ScrollIntoViewOptions", - "web-sys?/ScrollLogicalPosition", - "web-sys?/ScrollBehavior", - "web-sys?/HtmlElement", -] +mounted = [] document = [ "dep:serde", "dep:serde_json" ] file_engine = [ "dep:async-trait", - "dep:js-sys", - "web-sys?/File", - "web-sys?/FileList", - "web-sys?/FileReader" ] -wasm-bind = ["dep:web-sys", "dep:wasm-bindgen", "dep:wasm-bindgen-futures"] -native-bind = ["dep:tokio", "file_engine"] hot-reload-context = ["dep:dioxus-rsx"] html-to-rsx = [] [package.metadata.docs.rs] cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"] -feature = ["html-to-rsx", "hot-reload-context", "html-to-rsx", "native-bind", "wasm-bind"] +feature = ["html-to-rsx", "hot-reload-context", "html-to-rsx"] diff --git a/packages/html/build.rs b/packages/html/build.rs index 88da265a46..8d5edfa581 100644 --- a/packages/html/build.rs +++ b/packages/html/build.rs @@ -2,8 +2,6 @@ fn main() { // If any TS files change, re-run the build script lazy_js_bundle::LazyTypeScriptBindings::new() .with_watching("./src/ts") - .with_binding("./src/ts/eval.ts", "./src/js/eval.js") - .with_binding("./src/ts/native_eval.ts", "./src/js/native_eval.js") .with_binding("./src/ts/head.ts", "./src/js/head.js") .run(); } diff --git a/packages/html/src/document/bindings.rs b/packages/html/src/document/bindings.rs deleted file mode 100644 index ad8ef16066..0000000000 --- a/packages/html/src/document/bindings.rs +++ /dev/null @@ -1,50 +0,0 @@ -/// Code for the Dioxus channel used to communicate between the dioxus and javascript code -#[cfg(feature = "native-bind")] -pub const NATIVE_EVAL_JS: &str = include_str!("../js/native_eval.js"); - -#[cfg(feature = "wasm-bind")] -#[wasm_bindgen::prelude::wasm_bindgen] -pub struct JSOwner { - _owner: Box, -} - -#[cfg(feature = "wasm-bind")] -impl JSOwner { - pub fn new(owner: impl std::any::Any) -> Self { - Self { - _owner: Box::new(owner), - } - } -} - -#[cfg(feature = "wasm-bind")] -#[wasm_bindgen::prelude::wasm_bindgen(module = "/src/js/eval.js")] -extern "C" { - pub type WebDioxusChannel; - - #[wasm_bindgen(constructor)] - pub fn new(owner: JSOwner) -> WebDioxusChannel; - - #[wasm_bindgen(method, js_name = "rustSend")] - pub fn rust_send(this: &WebDioxusChannel, value: wasm_bindgen::JsValue); - - #[wasm_bindgen(method, js_name = "rustRecv")] - pub async fn rust_recv(this: &WebDioxusChannel) -> wasm_bindgen::JsValue; - - #[wasm_bindgen(method)] - pub fn send(this: &WebDioxusChannel, value: wasm_bindgen::JsValue); - - #[wasm_bindgen(method)] - pub async fn recv(this: &WebDioxusChannel) -> wasm_bindgen::JsValue; - - #[wasm_bindgen(method)] - pub fn weak(this: &WebDioxusChannel) -> WeakDioxusChannel; - - pub type WeakDioxusChannel; - - #[wasm_bindgen(method, js_name = "rustSend")] - pub fn rust_send(this: &WeakDioxusChannel, value: wasm_bindgen::JsValue); - - #[wasm_bindgen(method, js_name = "rustRecv")] - pub async fn rust_recv(this: &WeakDioxusChannel) -> wasm_bindgen::JsValue; -} diff --git a/packages/html/src/document/mod.rs b/packages/html/src/document/mod.rs index 27c05c5509..f142005966 100644 --- a/packages/html/src/document/mod.rs +++ b/packages/html/src/document/mod.rs @@ -7,9 +7,7 @@ use std::{ use generational_box::{AnyStorage, GenerationalBox, UnsyncStorage}; -mod bindings; #[allow(unused)] -pub use bindings::*; mod eval; pub use eval::*; diff --git a/packages/html/src/js/hash.txt b/packages/html/src/js/hash.txt index 051bb006a9..faaeb89f2e 100644 --- a/packages/html/src/js/hash.txt +++ b/packages/html/src/js/hash.txt @@ -1 +1 @@ -[10372071913661173523, 8375185156499858125, 4813754958077120784] \ No newline at end of file +[206827801705263822, 8375185156499858125] \ No newline at end of file diff --git a/packages/html/src/lib.rs b/packages/html/src/lib.rs index 3f1646912f..0067a90683 100644 --- a/packages/html/src/lib.rs +++ b/packages/html/src/lib.rs @@ -27,14 +27,8 @@ pub use file_data::*; mod attribute_groups; pub mod geometry; pub mod input_data; -#[cfg(feature = "native-bind")] -pub mod native_bind; pub mod point_interaction; mod render_template; -#[cfg(feature = "wasm-bind")] -mod web_sys_bind; -#[cfg(feature = "wasm-bind")] -pub use web_sys_bind::*; #[cfg(feature = "serialize")] mod transit; diff --git a/packages/html/src/native_bind/mod.rs b/packages/html/src/native_bind/mod.rs deleted file mode 100644 index 4d84a4ab31..0000000000 --- a/packages/html/src/native_bind/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod native_file_engine; - -pub use native_file_engine::*; diff --git a/packages/html/src/native_bind/native_file_engine.rs b/packages/html/src/native_bind/native_file_engine.rs deleted file mode 100644 index 98893ced6c..0000000000 --- a/packages/html/src/native_bind/native_file_engine.rs +++ /dev/null @@ -1,56 +0,0 @@ -use std::any::Any; -use std::path::PathBuf; - -use tokio::fs::File; -use tokio::io::AsyncReadExt; - -use crate::file_data::FileEngine; - -pub struct NativeFileEngine { - files: Vec, -} - -impl NativeFileEngine { - pub fn new(files: Vec) -> Self { - Self { files } - } -} - -#[cfg(feature = "file_engine")] -#[async_trait::async_trait(?Send)] -impl FileEngine for NativeFileEngine { - fn files(&self) -> Vec { - self.files - .iter() - .filter_map(|f| Some(f.to_str()?.to_string())) - .collect() - } - - async fn file_size(&self, file: &str) -> Option { - let file = File::open(file).await.ok()?; - Some(file.metadata().await.ok()?.len()) - } - - async fn read_file(&self, file: &str) -> Option> { - let mut file = File::open(file).await.ok()?; - - let mut contents = Vec::new(); - file.read_to_end(&mut contents).await.ok()?; - - Some(contents) - } - - async fn read_file_to_string(&self, file: &str) -> Option { - let mut file = File::open(file).await.ok()?; - - let mut contents = String::new(); - file.read_to_string(&mut contents).await.ok()?; - - Some(contents) - } - - async fn get_native_file(&self, file: &str) -> Option> { - let file = File::open(file).await.ok()?; - Some(Box::new(file)) - } -} diff --git a/packages/html/src/ts/eval.ts b/packages/html/src/ts/eval.ts index 44ce99d674..4440fe9dfd 100644 --- a/packages/html/src/ts/eval.ts +++ b/packages/html/src/ts/eval.ts @@ -68,41 +68,3 @@ export abstract class DioxusChannel { // Receive data sent from javascript in rust abstract rustRecv(): Promise; } - -export class WebDioxusChannel extends DioxusChannel { - js_to_rust: Channel; - rust_to_js: Channel; - owner: any; - - constructor(owner: any) { - super(); - this.owner = owner; - this.js_to_rust = new Channel(); - this.rust_to_js = new Channel(); - } - - // Return a weak reference to this channel - weak(): WeakDioxusChannel { - return new WeakDioxusChannel(this); - } - - // Receive message from Rust - async recv() { - return await this.rust_to_js.recv(); - } - - // Send message to rust. - send(data: any) { - this.js_to_rust.send(data); - } - - // Send data from rust to javascript - rustSend(data: any) { - this.rust_to_js.send(data); - } - - // Receive data sent from javascript in rust - async rustRecv(): Promise { - return await this.js_to_rust.recv(); - } -} diff --git a/packages/html/src/web_sys_bind/events.rs b/packages/html/src/web_sys_bind/events.rs deleted file mode 100644 index 2be499fdd4..0000000000 --- a/packages/html/src/web_sys_bind/events.rs +++ /dev/null @@ -1,675 +0,0 @@ -use crate::events::HasKeyboardData; -use crate::events::{ - AnimationData, CompositionData, KeyboardData, MouseData, PointerData, TouchData, - TransitionData, WheelData, -}; -use crate::file_data::HasFileData; -use crate::geometry::PixelsSize; -use crate::geometry::{ClientPoint, ElementPoint, PagePoint, ScreenPoint}; -use crate::input_data::{decode_key_location, decode_mouse_button_set, MouseButton}; -use crate::prelude::*; -use keyboard_types::{Code, Key, Modifiers}; -use std::str::FromStr; -use wasm_bindgen::JsCast; -use web_sys::{js_sys, ResizeObserverEntry}; -use web_sys::{ - AnimationEvent, CompositionEvent, CustomEvent, Event, KeyboardEvent, MouseEvent, PointerEvent, - Touch, TouchEvent, TransitionEvent, WheelEvent, -}; - -macro_rules! uncheck_convert { - ($t:ty, $d:ty) => { - impl From for $d { - #[inline] - fn from(e: Event) -> Self { - let e: $t = e.unchecked_into(); - Self::from(e) - } - } - - impl From<&Event> for $d { - #[inline] - fn from(e: &Event) -> Self { - let e: &$t = e.unchecked_ref(); - Self::from(e.clone()) - } - } - }; - ($($t:ty => $d:ty),+ $(,)?) => { - $(uncheck_convert!($t, $d);)+ - }; -} - -uncheck_convert![ - web_sys::CompositionEvent => CompositionData, - web_sys::KeyboardEvent => KeyboardData, - web_sys::MouseEvent => MouseData, - web_sys::TouchEvent => TouchData, - web_sys::PointerEvent => PointerData, - web_sys::WheelEvent => WheelData, - web_sys::AnimationEvent => AnimationData, - web_sys::TransitionEvent => TransitionData, - web_sys::MouseEvent => DragData, - web_sys::FocusEvent => FocusData, -]; - -impl From for ResizeData { - #[inline] - fn from(e: Event) -> Self { - >::from(&e) - } -} - -impl From<&Event> for ResizeData { - #[inline] - fn from(e: &Event) -> Self { - let e: &CustomEvent = e.unchecked_ref(); - let value = e.detail(); - Self::from(value.unchecked_into::()) - } -} - -impl HasCompositionData for CompositionEvent { - fn data(&self) -> std::string::String { - self.data().unwrap_or_default() - } - - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl HasKeyboardData for KeyboardEvent { - fn key(&self) -> Key { - Key::from_str(self.key().as_str()).unwrap_or(Key::Unidentified) - } - - fn code(&self) -> Code { - Code::from_str(self.code().as_str()).unwrap_or(Code::Unidentified) - } - - fn location(&self) -> keyboard_types::Location { - decode_key_location(self.location() as usize) - } - - fn is_auto_repeating(&self) -> bool { - self.repeat() - } - - fn is_composing(&self) -> bool { - self.is_composing() - } - - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl ModifiersInteraction for KeyboardEvent { - fn modifiers(&self) -> Modifiers { - let mut modifiers = Modifiers::empty(); - - if self.alt_key() { - modifiers.insert(Modifiers::ALT); - } - if self.ctrl_key() { - modifiers.insert(Modifiers::CONTROL); - } - if self.meta_key() { - modifiers.insert(Modifiers::META); - } - if self.shift_key() { - modifiers.insert(Modifiers::SHIFT); - } - - modifiers - } -} - -impl InteractionLocation for MouseEvent { - fn client_coordinates(&self) -> ClientPoint { - ClientPoint::new(self.client_x().into(), self.client_y().into()) - } - - fn page_coordinates(&self) -> PagePoint { - PagePoint::new(self.page_x().into(), self.page_y().into()) - } - - fn screen_coordinates(&self) -> ScreenPoint { - ScreenPoint::new(self.screen_x().into(), self.screen_y().into()) - } -} - -impl InteractionElementOffset for MouseEvent { - fn element_coordinates(&self) -> ElementPoint { - ElementPoint::new(self.offset_x().into(), self.offset_y().into()) - } -} - -impl ModifiersInteraction for MouseEvent { - fn modifiers(&self) -> Modifiers { - let mut modifiers = Modifiers::empty(); - - if self.alt_key() { - modifiers.insert(Modifiers::ALT); - } - if self.ctrl_key() { - modifiers.insert(Modifiers::CONTROL); - } - if self.meta_key() { - modifiers.insert(Modifiers::META); - } - if self.shift_key() { - modifiers.insert(Modifiers::SHIFT); - } - - modifiers - } -} - -impl PointerInteraction for MouseEvent { - fn held_buttons(&self) -> crate::input_data::MouseButtonSet { - decode_mouse_button_set(self.buttons()) - } - - fn trigger_button(&self) -> Option { - Some(MouseButton::from_web_code(self.button())) - } -} - -impl HasMouseData for MouseEvent { - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl HasFileData for MouseEvent {} - -impl HasDragData for MouseEvent { - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl ModifiersInteraction for TouchEvent { - fn modifiers(&self) -> Modifiers { - let mut modifiers = Modifiers::empty(); - - if self.alt_key() { - modifiers.insert(Modifiers::ALT); - } - if self.ctrl_key() { - modifiers.insert(Modifiers::CONTROL); - } - if self.meta_key() { - modifiers.insert(Modifiers::META); - } - if self.shift_key() { - modifiers.insert(Modifiers::SHIFT); - } - - modifiers - } -} - -impl crate::events::HasTouchData for TouchEvent { - fn touches(&self) -> Vec { - let touches = TouchEvent::touches(self); - let mut result = Vec::with_capacity(touches.length() as usize); - for i in 0..touches.length() { - let touch = touches.get(i).unwrap(); - result.push(TouchPoint::new(touch)); - } - result - } - - fn touches_changed(&self) -> Vec { - let touches = self.changed_touches(); - let mut result = Vec::with_capacity(touches.length() as usize); - for i in 0..touches.length() { - let touch = touches.get(i).unwrap(); - result.push(TouchPoint::new(touch)); - } - result - } - - fn target_touches(&self) -> Vec { - let touches = self.target_touches(); - let mut result = Vec::with_capacity(touches.length() as usize); - for i in 0..touches.length() { - let touch = touches.get(i).unwrap(); - result.push(TouchPoint::new(touch)); - } - result - } - - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl HasTouchPointData for Touch { - fn identifier(&self) -> i32 { - self.identifier() - } - - fn radius(&self) -> ScreenPoint { - ScreenPoint::new(self.radius_x().into(), self.radius_y().into()) - } - - fn rotation(&self) -> f64 { - self.rotation_angle() as f64 - } - - fn force(&self) -> f64 { - self.force() as f64 - } - - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl InteractionLocation for Touch { - fn client_coordinates(&self) -> ClientPoint { - ClientPoint::new(self.client_x().into(), self.client_y().into()) - } - - fn screen_coordinates(&self) -> ScreenPoint { - ScreenPoint::new(self.screen_x().into(), self.screen_y().into()) - } - - fn page_coordinates(&self) -> PagePoint { - PagePoint::new(self.page_x().into(), self.page_y().into()) - } -} - -impl HasPointerData for PointerEvent { - fn pointer_id(&self) -> i32 { - self.pointer_id() - } - - fn width(&self) -> i32 { - self.width() - } - - fn height(&self) -> i32 { - self.height() - } - - fn pressure(&self) -> f32 { - self.pressure() - } - - fn tangential_pressure(&self) -> f32 { - self.tangential_pressure() - } - - fn tilt_x(&self) -> i32 { - self.tilt_x() - } - - fn tilt_y(&self) -> i32 { - self.tilt_y() - } - - fn twist(&self) -> i32 { - self.twist() - } - - fn pointer_type(&self) -> String { - self.pointer_type() - } - - fn is_primary(&self) -> bool { - self.is_primary() - } - - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl InteractionLocation for PointerEvent { - fn client_coordinates(&self) -> ClientPoint { - ClientPoint::new(self.client_x().into(), self.client_y().into()) - } - - fn screen_coordinates(&self) -> ScreenPoint { - ScreenPoint::new(self.screen_x().into(), self.screen_y().into()) - } - - fn page_coordinates(&self) -> PagePoint { - PagePoint::new(self.page_x().into(), self.page_y().into()) - } -} - -impl InteractionElementOffset for PointerEvent { - fn element_coordinates(&self) -> ElementPoint { - ElementPoint::new(self.offset_x().into(), self.offset_y().into()) - } -} - -impl ModifiersInteraction for PointerEvent { - fn modifiers(&self) -> Modifiers { - let mut modifiers = Modifiers::empty(); - - if self.alt_key() { - modifiers.insert(Modifiers::ALT); - } - if self.ctrl_key() { - modifiers.insert(Modifiers::CONTROL); - } - if self.meta_key() { - modifiers.insert(Modifiers::META); - } - if self.shift_key() { - modifiers.insert(Modifiers::SHIFT); - } - - modifiers - } -} - -impl PointerInteraction for PointerEvent { - fn held_buttons(&self) -> crate::input_data::MouseButtonSet { - decode_mouse_button_set(self.buttons()) - } - - fn trigger_button(&self) -> Option { - Some(MouseButton::from_web_code(self.button())) - } -} - -impl HasWheelData for WheelEvent { - fn delta(&self) -> crate::geometry::WheelDelta { - crate::geometry::WheelDelta::from_web_attributes( - self.delta_mode(), - self.delta_x(), - self.delta_y(), - self.delta_z(), - ) - } - - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl HasMouseData for WheelEvent { - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl InteractionLocation for WheelEvent { - fn client_coordinates(&self) -> ClientPoint { - ClientPoint::new(self.client_x().into(), self.client_y().into()) - } - - fn screen_coordinates(&self) -> ScreenPoint { - ScreenPoint::new(self.screen_x().into(), self.screen_y().into()) - } - - fn page_coordinates(&self) -> PagePoint { - PagePoint::new(self.page_x().into(), self.page_y().into()) - } -} - -impl InteractionElementOffset for WheelEvent { - fn element_coordinates(&self) -> ElementPoint { - ElementPoint::new(self.offset_x().into(), self.offset_y().into()) - } -} - -impl ModifiersInteraction for WheelEvent { - fn modifiers(&self) -> Modifiers { - let mut modifiers = Modifiers::empty(); - - if self.alt_key() { - modifiers.insert(Modifiers::ALT); - } - if self.ctrl_key() { - modifiers.insert(Modifiers::CONTROL); - } - if self.meta_key() { - modifiers.insert(Modifiers::META); - } - if self.shift_key() { - modifiers.insert(Modifiers::SHIFT); - } - - modifiers - } -} - -impl PointerInteraction for WheelEvent { - fn held_buttons(&self) -> crate::input_data::MouseButtonSet { - decode_mouse_button_set(self.buttons()) - } - - fn trigger_button(&self) -> Option { - Some(MouseButton::from_web_code(self.button())) - } -} - -impl HasAnimationData for AnimationEvent { - fn animation_name(&self) -> String { - self.animation_name() - } - - fn pseudo_element(&self) -> String { - self.pseudo_element() - } - - fn elapsed_time(&self) -> f32 { - self.elapsed_time() - } - - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl HasTransitionData for TransitionEvent { - fn elapsed_time(&self) -> f32 { - self.elapsed_time() - } - - fn property_name(&self) -> String { - self.property_name() - } - - fn pseudo_element(&self) -> String { - self.pseudo_element() - } - - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -#[cfg(feature = "mounted")] -impl From<&web_sys::Element> for MountedData { - fn from(e: &web_sys::Element) -> Self { - MountedData::new(e.clone()) - } -} - -#[cfg(feature = "mounted")] -impl crate::RenderedElementBacking for web_sys::Element { - fn get_scroll_offset( - &self, - ) -> std::pin::Pin< - Box< - dyn std::future::Future>, - >, - > { - let left = self.scroll_left(); - let top = self.scroll_top(); - let result = Ok(crate::geometry::PixelsVector2D::new( - left as f64, - top as f64, - )); - Box::pin(async { result }) - } - - fn get_scroll_size( - &self, - ) -> std::pin::Pin< - Box>>, - > { - let width = self.scroll_width(); - let height = self.scroll_height(); - let result = Ok(crate::geometry::PixelsSize::new( - width as f64, - height as f64, - )); - Box::pin(async { result }) - } - - fn get_client_rect( - &self, - ) -> std::pin::Pin< - Box>>, - > { - let rect = self.get_bounding_client_rect(); - let result = Ok(crate::geometry::PixelsRect::new( - euclid::Point2D::new(rect.left(), rect.top()), - euclid::Size2D::new(rect.width(), rect.height()), - )); - Box::pin(async { result }) - } - - fn as_any(&self) -> &dyn std::any::Any { - self - } - - fn scroll_to( - &self, - behavior: crate::ScrollBehavior, - ) -> std::pin::Pin>>> { - let options = web_sys::ScrollIntoViewOptions::new(); - match behavior { - crate::ScrollBehavior::Instant => { - options.set_behavior(web_sys::ScrollBehavior::Instant); - } - crate::ScrollBehavior::Smooth => { - options.set_behavior(web_sys::ScrollBehavior::Smooth); - } - } - self.scroll_into_view_with_scroll_into_view_options(&options); - - Box::pin(async { Ok(()) }) - } - - fn set_focus( - &self, - focus: bool, - ) -> std::pin::Pin>>> { - #[derive(Debug)] - struct FocusError(wasm_bindgen::JsValue); - - impl std::fmt::Display for FocusError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "failed to focus element {:?}", self.0) - } - } - - impl std::error::Error for FocusError {} - - let result = self - .dyn_ref::() - .ok_or_else(|| crate::MountedError::OperationFailed(Box::new(FocusError(self.into())))) - .and_then(|e| { - (if focus { e.focus() } else { e.blur() }) - .map_err(|err| crate::MountedError::OperationFailed(Box::new(FocusError(err)))) - }); - Box::pin(async { result }) - } -} - -fn extract_first_size(resize_observer_output: js_sys::Array) -> ResizeResult { - let first = resize_observer_output.get(0); - let size = first.unchecked_into::(); - - // inline_size matches the width of the element if its writing-mode is horizontal, the height otherwise - let inline_size = size.inline_size(); - // block_size matches the height of the element if its writing-mode is horizontal, the width otherwise - let block_size = size.block_size(); - - Ok(PixelsSize::new(inline_size, block_size)) -} - -impl HasResizeData for ResizeObserverEntry { - fn as_any(&self) -> &dyn std::any::Any { - self - } - - fn get_border_box_size(&self) -> ResizeResult { - extract_first_size(self.border_box_size()) - } - - fn get_content_box_size(&self) -> ResizeResult { - extract_first_size(self.content_box_size()) - } -} - -impl HasScrollData for Event { - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl HasClipboardData for Event { - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl From<&Event> for ClipboardData { - fn from(e: &Event) -> Self { - ClipboardData::new(e.clone()) - } -} - -impl HasFocusData for web_sys::FocusEvent { - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl HasToggleData for web_sys::Event { - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl HasSelectionData for web_sys::Event { - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl HasMediaData for web_sys::Event { - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl HasFileData for web_sys::Event { - #[cfg(feature = "file_engine")] - fn files(&self) -> Option> { - let files = self - .dyn_ref() - .and_then(|input: &web_sys::HtmlInputElement| { - input.files().and_then(|files| { - #[allow(clippy::arc_with_non_send_sync)] - crate::web_sys_bind::file_engine::WebFileEngine::new(files) - .map(|f| std::sync::Arc::new(f) as std::sync::Arc) - }) - }); - - files - } -} diff --git a/packages/html/src/web_sys_bind/mod.rs b/packages/html/src/web_sys_bind/mod.rs deleted file mode 100644 index 4ef737d430..0000000000 --- a/packages/html/src/web_sys_bind/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod events; -#[cfg(feature = "file_engine")] -mod file_engine; -#[cfg(feature = "file_engine")] -pub use file_engine::*; diff --git a/packages/web/Cargo.toml b/packages/web/Cargo.toml index c1abecc358..f79b2692a8 100644 --- a/packages/web/Cargo.toml +++ b/packages/web/Cargo.toml @@ -12,7 +12,7 @@ keywords = ["dom", "ui", "gui", "react", "wasm"] [dependencies] dioxus-core = { workspace = true } dioxus-core-types = { workspace = true } -dioxus-html = { workspace = true, features = ["wasm-bind"] } +dioxus-html = { workspace = true } dioxus-devtools = { workspace = true } dioxus-signals = { workspace = true } dioxus-interpreter-js = { workspace = true, features = [ @@ -38,33 +38,66 @@ serde = { version = "1.0", optional = true } serde-wasm-bindgen = { version = "0.5.0", optional = true } ciborium = { workspace = true, optional = true } +async-trait = { version = "0.1.58", optional = true } [dependencies.web-sys] version = "0.3.56" features = [ + "AnimationEvent", + "ClipboardEvent", + "CloseEvent", + "Comment", + "CompositionEvent", + "console", + "CustomEvent", + "DataTransfer", "Document", + "DragEvent", + "FocusEvent", "HtmlElement", + "HtmlFormElement", "HtmlInputElement", "HtmlSelectElement", "HtmlTextAreaElement", - "HtmlFormElement", + "InputEvent", + "KeyboardEvent", + "MouseEvent", + "NodeList", + "PointerEvent", + "ResizeObserverEntry", + "ResizeObserverSize", "Text", - "Comment", + "Touch", + "TouchEvent", + "TouchList", + "TransitionEvent", + "WheelEvent", "Window", - "DataTransfer", - "console", - "NodeList", - "CloseEvent", - "CustomEvent", ] +[build-dependencies] +lazy-js-bundle = { workspace = true } + [features] default = ["panic_hook", "mounted", "file_engine", "devtools", "document"] panic_hook = ["dep:console_error_panic_hook"] hydrate = ["web-sys/Comment", "ciborium", "dep:serde"] -mounted = ["web-sys/Element", "dioxus-html/mounted"] +mounted = [ + "web-sys/Element", + "dioxus-html/mounted", + "web-sys/Element", + "web-sys/DomRect", + "web-sys/ScrollIntoViewOptions", + "web-sys/ScrollLogicalPosition", + "web-sys/ScrollBehavior", + "web-sys/HtmlElement", +] file_engine = [ "dioxus-html/file_engine", + "dep:async-trait", + "web-sys/File", + "web-sys/FileList", + "web-sys/FileReader" ] devtools = ["web-sys/MessageEvent", "web-sys/WebSocket", "web-sys/Location", "dep:serde_json", "dep:serde", "dioxus-core/serialize"] document = ["dioxus-html/document", "dep:serde-wasm-bindgen", "dep:serde_json", "dep:serde"] diff --git a/packages/web/build.rs b/packages/web/build.rs new file mode 100644 index 0000000000..860c568758 --- /dev/null +++ b/packages/web/build.rs @@ -0,0 +1,7 @@ +fn main() { + // If any TS files change, re-run the build script + lazy_js_bundle::LazyTypeScriptBindings::new() + .with_watching("./src/ts") + .with_binding("./src/ts/eval.ts", "./src/js/eval.js") + .run(); +} diff --git a/packages/web/src/document.rs b/packages/web/src/document.rs index ce8828a5e8..62127c7c7a 100644 --- a/packages/web/src/document.rs +++ b/packages/web/src/document.rs @@ -1,7 +1,5 @@ use dioxus_core::ScopeId; -use dioxus_html::document::{ - Document, EvalError, Evaluator, JSOwner, WeakDioxusChannel, WebDioxusChannel, -}; +use dioxus_html::document::{Document, EvalError, Evaluator}; use generational_box::{AnyStorage, GenerationalBox, UnsyncStorage}; use js_sys::Function; use serde::Serialize; @@ -11,6 +9,50 @@ use std::pin::Pin; use std::{rc::Rc, str::FromStr}; use wasm_bindgen::prelude::*; +#[wasm_bindgen::prelude::wasm_bindgen] +pub struct JSOwner { + _owner: Box, +} + +impl JSOwner { + pub fn new(owner: impl std::any::Any) -> Self { + Self { + _owner: Box::new(owner), + } + } +} + +#[wasm_bindgen::prelude::wasm_bindgen(module = "/src/js/eval.js")] +extern "C" { + pub type WebDioxusChannel; + + #[wasm_bindgen(constructor)] + pub fn new(owner: JSOwner) -> WebDioxusChannel; + + #[wasm_bindgen(method, js_name = "rustSend")] + pub fn rust_send(this: &WebDioxusChannel, value: wasm_bindgen::JsValue); + + #[wasm_bindgen(method, js_name = "rustRecv")] + pub async fn rust_recv(this: &WebDioxusChannel) -> wasm_bindgen::JsValue; + + #[wasm_bindgen(method)] + pub fn send(this: &WebDioxusChannel, value: wasm_bindgen::JsValue); + + #[wasm_bindgen(method)] + pub async fn recv(this: &WebDioxusChannel) -> wasm_bindgen::JsValue; + + #[wasm_bindgen(method)] + pub fn weak(this: &WebDioxusChannel) -> WeakDioxusChannel; + + pub type WeakDioxusChannel; + + #[wasm_bindgen(method, js_name = "rustSend")] + pub fn rust_send(this: &WeakDioxusChannel, value: wasm_bindgen::JsValue); + + #[wasm_bindgen(method, js_name = "rustRecv")] + pub async fn rust_recv(this: &WeakDioxusChannel) -> wasm_bindgen::JsValue; +} + /// Provides the WebEvalProvider through [`ScopeId::provide_context`]. pub fn init_document() { let provider: Rc = Rc::new(WebDocument); diff --git a/packages/web/src/event.rs b/packages/web/src/event.rs deleted file mode 100644 index 24f58f0ebe..0000000000 --- a/packages/web/src/event.rs +++ /dev/null @@ -1,591 +0,0 @@ -use std::{any::Any, collections::HashMap}; - -use dioxus_html::{ - point_interaction::{ - InteractionElementOffset, InteractionLocation, ModifiersInteraction, PointerInteraction, - }, - DragData, FormData, FormValue, HasDragData, HasFileData, HasFormData, HasImageData, - HasMouseData, HtmlEventConverter, ImageData, MountedData, PlatformEventData, ScrollData, -}; -use js_sys::Array; -use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue}; -use web_sys::{Document, Element, Event, MouseEvent}; - -pub(crate) struct WebEventConverter; - -#[inline(always)] -fn downcast_event(event: &dioxus_html::PlatformEventData) -> &GenericWebSysEvent { - event - .downcast::() - .expect("event should be a GenericWebSysEvent") -} - -impl HtmlEventConverter for WebEventConverter { - #[inline(always)] - fn convert_animation_data( - &self, - event: &dioxus_html::PlatformEventData, - ) -> dioxus_html::AnimationData { - downcast_event(event).raw.clone().into() - } - - #[inline(always)] - fn convert_clipboard_data( - &self, - event: &dioxus_html::PlatformEventData, - ) -> dioxus_html::ClipboardData { - downcast_event(event).raw.clone().into() - } - - #[inline(always)] - fn convert_composition_data( - &self, - event: &dioxus_html::PlatformEventData, - ) -> dioxus_html::CompositionData { - downcast_event(event).raw.clone().into() - } - - #[inline(always)] - fn convert_drag_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::DragData { - let event = downcast_event(event); - DragData::new(WebDragData::new(event.raw.clone().unchecked_into())) - } - - #[inline(always)] - fn convert_focus_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::FocusData { - downcast_event(event).raw.clone().into() - } - - #[inline(always)] - fn convert_form_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::FormData { - let event = downcast_event(event); - FormData::new(WebFormData::new(event.element.clone(), event.raw.clone())) - } - - #[inline(always)] - fn convert_image_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::ImageData { - let event = downcast_event(event); - let error = event.raw.type_() == "error"; - ImageData::new(WebImageEvent::new(event.raw.clone(), error)) - } - - #[inline(always)] - fn convert_keyboard_data( - &self, - event: &dioxus_html::PlatformEventData, - ) -> dioxus_html::KeyboardData { - downcast_event(event).raw.clone().into() - } - - #[inline(always)] - fn convert_media_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::MediaData { - downcast_event(event).raw.clone().into() - } - - #[allow(unused_variables)] - #[inline(always)] - fn convert_mounted_data(&self, event: &dioxus_html::PlatformEventData) -> MountedData { - #[cfg(feature = "mounted")] - { - MountedData::from( - event - .downcast::() - .expect("event should be a web_sys::Element"), - ) - } - #[cfg(not(feature = "mounted"))] - { - panic!("mounted events are not supported without the mounted feature on the dioxus-web crate enabled") - } - } - - #[inline(always)] - fn convert_mouse_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::MouseData { - downcast_event(event).raw.clone().into() - } - - #[inline(always)] - fn convert_pointer_data( - &self, - event: &dioxus_html::PlatformEventData, - ) -> dioxus_html::PointerData { - downcast_event(event).raw.clone().into() - } - - #[inline(always)] - fn convert_resize_data( - &self, - event: &dioxus_html::PlatformEventData, - ) -> dioxus_html::ResizeData { - downcast_event(event).raw.clone().into() - } - - #[inline(always)] - fn convert_scroll_data( - &self, - event: &dioxus_html::PlatformEventData, - ) -> dioxus_html::ScrollData { - ScrollData::from(downcast_event(event).raw.clone()) - } - - #[inline(always)] - fn convert_selection_data( - &self, - event: &dioxus_html::PlatformEventData, - ) -> dioxus_html::SelectionData { - downcast_event(event).raw.clone().into() - } - - #[inline(always)] - fn convert_toggle_data( - &self, - event: &dioxus_html::PlatformEventData, - ) -> dioxus_html::ToggleData { - downcast_event(event).raw.clone().into() - } - - #[inline(always)] - fn convert_touch_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::TouchData { - downcast_event(event).raw.clone().into() - } - - #[inline(always)] - fn convert_transition_data( - &self, - event: &dioxus_html::PlatformEventData, - ) -> dioxus_html::TransitionData { - downcast_event(event).raw.clone().into() - } - - #[inline(always)] - fn convert_wheel_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::WheelData { - downcast_event(event).raw.clone().into() - } -} - -/// A extension trait for web-sys events that provides a way to get the event as a web-sys event. -pub trait WebEventExt { - /// Try to downcast this event as a `web-sys` event. - fn try_as_web_event(&self) -> Option; - - /// Downcast this event as a `web-sys` event. - #[inline(always)] - fn as_web_event(&self) -> E - where - E: 'static, - { - self.try_as_web_event().unwrap_or_else(|| { - panic!( - "Error downcasting to `web-sys`, event should be a {}.", - std::any::type_name::() - ) - }) - } -} - -impl WebEventExt for dioxus_html::AnimationData { - #[inline(always)] - fn try_as_web_event(&self) -> Option { - self.downcast::().cloned() - } -} - -impl WebEventExt for dioxus_html::ClipboardData { - #[inline(always)] - fn try_as_web_event(&self) -> Option { - self.downcast::().cloned() - } -} - -impl WebEventExt for dioxus_html::CompositionData { - #[inline(always)] - fn try_as_web_event(&self) -> Option { - self.downcast::().cloned() - } -} - -impl WebEventExt for dioxus_html::DragData { - #[inline(always)] - fn try_as_web_event(&self) -> Option { - self.downcast::() - .map(|data| &data.raw) - .cloned() - } -} - -impl WebEventExt for dioxus_html::FocusData { - #[inline(always)] - fn try_as_web_event(&self) -> Option { - self.downcast::().cloned() - } -} - -impl WebEventExt for dioxus_html::FormData { - #[inline(always)] - fn try_as_web_event(&self) -> Option { - self.downcast::().cloned() - } -} - -impl WebEventExt for dioxus_html::ImageData { - #[inline(always)] - fn try_as_web_event(&self) -> Option { - self.downcast::().cloned() - } -} - -impl WebEventExt for dioxus_html::KeyboardData { - #[inline(always)] - fn try_as_web_event(&self) -> Option { - self.downcast::().cloned() - } -} - -impl WebEventExt for dioxus_html::MediaData { - #[inline(always)] - fn try_as_web_event(&self) -> Option { - self.downcast::().cloned() - } -} - -impl WebEventExt for MountedData { - #[inline(always)] - fn try_as_web_event(&self) -> Option { - self.downcast::().cloned() - } -} - -impl WebEventExt for dioxus_html::MouseData { - #[inline(always)] - fn try_as_web_event(&self) -> Option { - self.downcast::().cloned() - } -} - -impl WebEventExt for dioxus_html::PointerData { - #[inline(always)] - fn try_as_web_event(&self) -> Option { - self.downcast::().cloned() - } -} - -impl WebEventExt for ScrollData { - #[inline(always)] - fn try_as_web_event(&self) -> Option { - self.downcast::().cloned() - } -} - -impl WebEventExt for dioxus_html::SelectionData { - #[inline(always)] - fn try_as_web_event(&self) -> Option { - self.downcast::().cloned() - } -} - -impl WebEventExt for dioxus_html::ToggleData { - #[inline(always)] - fn try_as_web_event(&self) -> Option { - self.downcast::().cloned() - } -} - -impl WebEventExt for dioxus_html::TouchData { - #[inline(always)] - fn try_as_web_event(&self) -> Option { - self.downcast::().cloned() - } -} - -impl WebEventExt for dioxus_html::TransitionData { - #[inline(always)] - fn try_as_web_event(&self) -> Option { - self.downcast::().cloned() - } -} - -impl WebEventExt for dioxus_html::WheelData { - #[inline(always)] - fn try_as_web_event(&self) -> Option { - self.downcast::().cloned() - } -} - -impl WebEventExt for dioxus_html::ResizeData { - #[inline(always)] - fn try_as_web_event(&self) -> Option { - self.downcast::() - .and_then(|e| e.detail().dyn_into::().ok()) - } -} - -struct GenericWebSysEvent { - raw: Event, - element: Element, -} - -// todo: some of these events are being casted to the wrong event type. -// We need tests that simulate clicks/etc and make sure every event type works. -pub(crate) fn virtual_event_from_websys_event( - event: web_sys::Event, - target: Element, -) -> PlatformEventData { - PlatformEventData::new(Box::new(GenericWebSysEvent { - raw: event, - element: target, - })) -} - -pub(crate) fn load_document() -> Document { - web_sys::window() - .expect("should have access to the Window") - .document() - .expect("should have access to the Document") -} - -#[derive(Clone)] -struct WebImageEvent { - raw: Event, - error: bool, -} - -impl WebImageEvent { - fn new(raw: Event, error: bool) -> Self { - Self { raw, error } - } -} - -impl HasImageData for WebImageEvent { - fn load_error(&self) -> bool { - self.error - } - - fn as_any(&self) -> &dyn Any { - &self.raw as &dyn Any - } -} - -struct WebFormData { - element: Element, - raw: Event, -} - -impl WebFormData { - fn new(element: Element, raw: Event) -> Self { - Self { element, raw } - } -} - -impl HasFormData for WebFormData { - fn value(&self) -> String { - let target = &self.element; - target - .dyn_ref() - .map(|input: &web_sys::HtmlInputElement| { - // todo: special case more input types - match input.type_().as_str() { - "checkbox" => { - match input.checked() { - true => "true".to_string(), - false => "false".to_string(), - } - }, - _ => { - input.value() - } - } - }) - .or_else(|| { - target - .dyn_ref() - .map(|input: &web_sys::HtmlTextAreaElement| input.value()) - }) - // select elements are NOT input events - because - why woudn't they be?? - .or_else(|| { - target - .dyn_ref() - .map(|input: &web_sys::HtmlSelectElement| input.value()) - }) - .or_else(|| { - target - .dyn_ref::() - .unwrap() - .text_content() - }) - .expect("only an InputElement or TextAreaElement or an element with contenteditable=true can have an oninput event listener") - } - - fn values(&self) -> HashMap { - let mut values = HashMap::new(); - - fn insert_value(map: &mut HashMap, key: String, new_value: String) { - map.entry(key.clone()).or_default().0.push(new_value); - } - - // try to fill in form values - if let Some(form) = self.element.dyn_ref::() { - let form_data = get_form_data(form); - for value in form_data.entries().into_iter().flatten() { - if let Ok(array) = value.dyn_into::() { - if let Some(name) = array.get(0).as_string() { - if let Ok(item_values) = array.get(1).dyn_into::() { - item_values - .iter() - .filter_map(|v| v.as_string()) - .for_each(|v| insert_value(&mut values, name.clone(), v)); - } else if let Ok(item_value) = array.get(1).dyn_into::() { - insert_value(&mut values, name, item_value.as_string().unwrap()); - } - } - } - } - } else if let Some(select) = self.element.dyn_ref::() { - // try to fill in select element values - let options = get_select_data(select); - values.insert("options".to_string(), FormValue(options)); - } - - values - } - - fn as_any(&self) -> &dyn Any { - &self.raw as &dyn Any - } -} - -impl HasFileData for WebFormData { - #[cfg(feature = "file_engine")] - fn files(&self) -> Option> { - let files = self - .element - .dyn_ref() - .and_then(|input: &web_sys::HtmlInputElement| { - input.files().and_then(|files| { - #[allow(clippy::arc_with_non_send_sync)] - dioxus_html::WebFileEngine::new(files).map(|f| { - std::sync::Arc::new(f) as std::sync::Arc - }) - }) - }); - - files - } -} - -struct WebDragData { - raw: MouseEvent, -} - -impl WebDragData { - fn new(raw: MouseEvent) -> Self { - Self { raw } - } -} - -impl HasDragData for WebDragData { - fn as_any(&self) -> &dyn std::any::Any { - &self.raw as &dyn std::any::Any - } -} - -impl HasMouseData for WebDragData { - fn as_any(&self) -> &dyn std::any::Any { - &self.raw as &dyn std::any::Any - } -} - -impl PointerInteraction for WebDragData { - fn trigger_button(&self) -> Option { - self.raw.trigger_button() - } - - fn held_buttons(&self) -> dioxus_html::input_data::MouseButtonSet { - self.raw.held_buttons() - } -} - -impl ModifiersInteraction for WebDragData { - fn modifiers(&self) -> dioxus_html::prelude::Modifiers { - self.raw.modifiers() - } -} - -impl InteractionElementOffset for WebDragData { - fn coordinates(&self) -> dioxus_html::geometry::Coordinates { - self.raw.coordinates() - } - - fn element_coordinates(&self) -> dioxus_html::geometry::ElementPoint { - self.raw.element_coordinates() - } -} - -impl InteractionLocation for WebDragData { - fn client_coordinates(&self) -> dioxus_html::geometry::ClientPoint { - self.raw.client_coordinates() - } - - fn screen_coordinates(&self) -> dioxus_html::geometry::ScreenPoint { - self.raw.screen_coordinates() - } - - fn page_coordinates(&self) -> dioxus_html::geometry::PagePoint { - self.raw.page_coordinates() - } -} - -impl HasFileData for WebDragData { - #[cfg(feature = "file_engine")] - fn files(&self) -> Option> { - let files = self - .raw - .dyn_ref::() - .and_then(|drag_event| { - drag_event.data_transfer().and_then(|dt| { - dt.files().and_then(|files| { - #[allow(clippy::arc_with_non_send_sync)] - dioxus_html::WebFileEngine::new(files).map(|f| { - std::sync::Arc::new(f) as std::sync::Arc - }) - }) - }) - }); - - files - } -} - -// web-sys does not expose the keys api for form data, so we need to manually bind to it -#[wasm_bindgen(inline_js = r#" -export function get_form_data(form) { - let values = new Map(); - const formData = new FormData(form); - - for (let name of formData.keys()) { - values.set(name, formData.getAll(name)); - } - - return values; -} -"#)] -extern "C" { - fn get_form_data(form: &web_sys::HtmlFormElement) -> js_sys::Map; -} - -// web-sys does not expose the keys api for select data, so we need to manually bind to it -#[wasm_bindgen(inline_js = r#" -export function get_select_data(select) { - let values = []; - for (let i = 0; i < select.options.length; i++) { - let option = select.options[i]; - if (option.selected) { - values.push(option.value.toString()); - } - } - - return values; -} -"#)] -extern "C" { - fn get_select_data(select: &web_sys::HtmlSelectElement) -> Vec; -} diff --git a/packages/web/src/events/animation.rs b/packages/web/src/events/animation.rs new file mode 100644 index 0000000000..a1ed2c71c4 --- /dev/null +++ b/packages/web/src/events/animation.rs @@ -0,0 +1,32 @@ +use dioxus_html::HasAnimationData; +use web_sys::AnimationEvent; + +use super::{Synthetic, WebEventExt}; + +impl HasAnimationData for Synthetic { + fn animation_name(&self) -> String { + self.event.animation_name() + } + + fn pseudo_element(&self) -> String { + self.event.pseudo_element() + } + + fn elapsed_time(&self) -> f32 { + self.event.elapsed_time() + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +impl WebEventExt for dioxus_html::AnimationData { + type WebEvent = web_sys::AnimationEvent; + + #[inline(always)] + fn try_as_web_event(&self) -> Option { + self.downcast::>() + .map(|e| e.event.clone()) + } +} diff --git a/packages/web/src/events/clipboard.rs b/packages/web/src/events/clipboard.rs new file mode 100644 index 0000000000..9eeb7791a0 --- /dev/null +++ b/packages/web/src/events/clipboard.rs @@ -0,0 +1,26 @@ +use dioxus_html::HasClipboardData; +use web_sys::Event; + +use super::{Synthetic, WebEventExt}; + +impl From<&Event> for Synthetic { + fn from(e: &Event) -> Self { + Synthetic::new(e.clone()) + } +} + +impl HasClipboardData for Synthetic { + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +impl WebEventExt for dioxus_html::ClipboardData { + type WebEvent = web_sys::Event; + + #[inline(always)] + fn try_as_web_event(&self) -> Option { + self.downcast::>() + .map(|e| e.event.clone()) + } +} diff --git a/packages/web/src/events/composition.rs b/packages/web/src/events/composition.rs new file mode 100644 index 0000000000..221caf9ae9 --- /dev/null +++ b/packages/web/src/events/composition.rs @@ -0,0 +1,24 @@ +use dioxus_html::HasCompositionData; +use web_sys::CompositionEvent; + +use super::{Synthetic, WebEventExt}; + +impl HasCompositionData for Synthetic { + fn data(&self) -> std::string::String { + self.event.data().unwrap_or_default() + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +impl WebEventExt for dioxus_html::CompositionData { + type WebEvent = web_sys::CompositionEvent; + + #[inline(always)] + fn try_as_web_event(&self) -> Option { + self.downcast::>() + .map(|e| e.event.clone()) + } +} diff --git a/packages/web/src/events/drag.rs b/packages/web/src/events/drag.rs new file mode 100644 index 0000000000..6379dbd29e --- /dev/null +++ b/packages/web/src/events/drag.rs @@ -0,0 +1,108 @@ +use dioxus_html::{ + prelude::{ + InteractionElementOffset, InteractionLocation, ModifiersInteraction, PointerInteraction, + }, + HasDragData, HasFileData, HasMouseData, +}; +use web_sys::MouseEvent; + +use super::{Synthetic, WebEventExt}; + +pub(crate) struct WebDragData { + raw: Synthetic, +} + +impl WebDragData { + pub fn new(raw: MouseEvent) -> Self { + Self { + raw: Synthetic::new(raw), + } + } +} + +impl HasDragData for WebDragData { + fn as_any(&self) -> &dyn std::any::Any { + &self.raw as &dyn std::any::Any + } +} + +impl HasMouseData for WebDragData { + fn as_any(&self) -> &dyn std::any::Any { + &self.raw as &dyn std::any::Any + } +} + +impl PointerInteraction for WebDragData { + fn trigger_button(&self) -> Option { + self.raw.trigger_button() + } + + fn held_buttons(&self) -> dioxus_html::input_data::MouseButtonSet { + self.raw.held_buttons() + } +} + +impl ModifiersInteraction for WebDragData { + fn modifiers(&self) -> dioxus_html::prelude::Modifiers { + self.raw.modifiers() + } +} + +impl InteractionElementOffset for WebDragData { + fn coordinates(&self) -> dioxus_html::geometry::Coordinates { + self.raw.coordinates() + } + + fn element_coordinates(&self) -> dioxus_html::geometry::ElementPoint { + self.raw.element_coordinates() + } +} + +impl InteractionLocation for WebDragData { + fn client_coordinates(&self) -> dioxus_html::geometry::ClientPoint { + self.raw.client_coordinates() + } + + fn screen_coordinates(&self) -> dioxus_html::geometry::ScreenPoint { + self.raw.screen_coordinates() + } + + fn page_coordinates(&self) -> dioxus_html::geometry::PagePoint { + self.raw.page_coordinates() + } +} + +impl HasFileData for WebDragData { + #[cfg(feature = "file_engine")] + fn files(&self) -> Option> { + use wasm_bindgen::JsCast; + + let files = self + .raw + .event + .dyn_ref::() + .and_then(|drag_event| { + drag_event.data_transfer().and_then(|dt| { + dt.files().and_then(|files| { + #[allow(clippy::arc_with_non_send_sync)] + crate::file_engine::WebFileEngine::new(files).map(|f| { + std::sync::Arc::new(f) as std::sync::Arc + }) + }) + }) + }); + + files + } +} + +impl WebEventExt for dioxus_html::DragData { + type WebEvent = web_sys::MouseEvent; + + #[inline(always)] + fn try_as_web_event(&self) -> Option { + self.downcast::() + .map(|data| &data.raw.event) + .cloned() + } +} diff --git a/packages/web/src/events/file.rs b/packages/web/src/events/file.rs new file mode 100644 index 0000000000..981a5288b6 --- /dev/null +++ b/packages/web/src/events/file.rs @@ -0,0 +1,24 @@ +use dioxus_html::HasFileData; + +use super::Synthetic; + +impl HasFileData for Synthetic { + #[cfg(feature = "file_engine")] + fn files(&self) -> Option> { + use wasm_bindgen::JsCast; + + let files = self + .event + .dyn_ref() + .and_then(|input: &web_sys::HtmlInputElement| { + input.files().and_then(|files| { + #[allow(clippy::arc_with_non_send_sync)] + crate::file_engine::WebFileEngine::new(files).map(|f| { + std::sync::Arc::new(f) as std::sync::Arc + }) + }) + }); + + files + } +} diff --git a/packages/web/src/events/focus.rs b/packages/web/src/events/focus.rs new file mode 100644 index 0000000000..befaadfab9 --- /dev/null +++ b/packages/web/src/events/focus.rs @@ -0,0 +1,19 @@ +use dioxus_html::HasFocusData; + +use super::{Synthetic, WebEventExt}; + +impl HasFocusData for Synthetic { + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +impl WebEventExt for dioxus_html::FocusData { + type WebEvent = web_sys::FocusEvent; + + #[inline(always)] + fn try_as_web_event(&self) -> Option { + self.downcast::>() + .map(|e| e.event.clone()) + } +} diff --git a/packages/web/src/events/form.rs b/packages/web/src/events/form.rs new file mode 100644 index 0000000000..d92330f64c --- /dev/null +++ b/packages/web/src/events/form.rs @@ -0,0 +1,160 @@ +use std::{any::Any, collections::HashMap}; + +use dioxus_html::{FormValue, HasFileData, HasFormData}; +use js_sys::Array; +use wasm_bindgen::JsValue; +use wasm_bindgen::{prelude::wasm_bindgen, JsCast}; +use web_sys::{Element, Event}; + +use super::WebEventExt; + +pub(crate) struct WebFormData { + element: Element, + raw: Event, +} + +impl WebEventExt for dioxus_html::FormData { + type WebEvent = Event; + + #[inline(always)] + fn try_as_web_event(&self) -> Option { + self.downcast::().map(|e| e.raw.clone()) + } +} + +impl WebFormData { + pub fn new(element: Element, raw: Event) -> Self { + Self { element, raw } + } +} + +impl HasFormData for WebFormData { + fn value(&self) -> String { + let target = &self.element; + target + .dyn_ref() + .map(|input: &web_sys::HtmlInputElement| { + // todo: special case more input types + match input.type_().as_str() { + "checkbox" => { + match input.checked() { + true => "true".to_string(), + false => "false".to_string(), + } + }, + _ => { + input.value() + } + } + }) + .or_else(|| { + target + .dyn_ref() + .map(|input: &web_sys::HtmlTextAreaElement| input.value()) + }) + // select elements are NOT input events - because - why woudn't they be?? + .or_else(|| { + target + .dyn_ref() + .map(|input: &web_sys::HtmlSelectElement| input.value()) + }) + .or_else(|| { + target + .dyn_ref::() + .unwrap() + .text_content() + }) + .expect("only an InputElement or TextAreaElement or an element with contenteditable=true can have an oninput event listener") + } + + fn values(&self) -> HashMap { + let mut values = HashMap::new(); + + fn insert_value(map: &mut HashMap, key: String, new_value: String) { + map.entry(key.clone()).or_default().0.push(new_value); + } + + // try to fill in form values + if let Some(form) = self.element.dyn_ref::() { + let form_data = get_form_data(form); + for value in form_data.entries().into_iter().flatten() { + if let Ok(array) = value.dyn_into::() { + if let Some(name) = array.get(0).as_string() { + if let Ok(item_values) = array.get(1).dyn_into::() { + item_values + .iter() + .filter_map(|v| v.as_string()) + .for_each(|v| insert_value(&mut values, name.clone(), v)); + } else if let Ok(item_value) = array.get(1).dyn_into::() { + insert_value(&mut values, name, item_value.as_string().unwrap()); + } + } + } + } + } else if let Some(select) = self.element.dyn_ref::() { + // try to fill in select element values + let options = get_select_data(select); + values.insert("options".to_string(), FormValue(options)); + } + + values + } + + fn as_any(&self) -> &dyn Any { + &self.raw as &dyn Any + } +} + +impl HasFileData for WebFormData { + #[cfg(feature = "file_engine")] + fn files(&self) -> Option> { + let files = self + .element + .dyn_ref() + .and_then(|input: &web_sys::HtmlInputElement| { + input.files().and_then(|files| { + #[allow(clippy::arc_with_non_send_sync)] + crate::file_engine::WebFileEngine::new(files).map(|f| { + std::sync::Arc::new(f) as std::sync::Arc + }) + }) + }); + + files + } +} + +// web-sys does not expose the keys api for select data, so we need to manually bind to it +#[wasm_bindgen(inline_js = r#" +export function get_select_data(select) { + let values = []; + for (let i = 0; i < select.options.length; i++) { + let option = select.options[i]; + if (option.selected) { + values.push(option.value.toString()); + } + } + + return values; +} +"#)] +extern "C" { + fn get_select_data(select: &web_sys::HtmlSelectElement) -> Vec; +} + +// web-sys does not expose the keys api for form data, so we need to manually bind to it +#[wasm_bindgen(inline_js = r#" +export function get_form_data(form) { + let values = new Map(); + const formData = new FormData(form); + + for (let name of formData.keys()) { + values.set(name, formData.getAll(name)); + } + + return values; +} +"#)] +extern "C" { + fn get_form_data(form: &web_sys::HtmlFormElement) -> js_sys::Map; +} diff --git a/packages/web/src/events/keyboard.rs b/packages/web/src/events/keyboard.rs new file mode 100644 index 0000000000..03a8bbded9 --- /dev/null +++ b/packages/web/src/events/keyboard.rs @@ -0,0 +1,67 @@ +use std::str::FromStr; + +use dioxus_html::{ + input_data::decode_key_location, + prelude::{Code, Key, Location, Modifiers, ModifiersInteraction}, + HasKeyboardData, +}; +use web_sys::KeyboardEvent; + +use super::{Synthetic, WebEventExt}; + +impl HasKeyboardData for Synthetic { + fn key(&self) -> Key { + Key::from_str(self.event.key().as_str()).unwrap_or(Key::Unidentified) + } + + fn code(&self) -> Code { + Code::from_str(self.event.code().as_str()).unwrap_or(Code::Unidentified) + } + + fn location(&self) -> Location { + decode_key_location(self.event.location() as usize) + } + + fn is_auto_repeating(&self) -> bool { + self.event.repeat() + } + + fn is_composing(&self) -> bool { + self.event.is_composing() + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +impl ModifiersInteraction for Synthetic { + fn modifiers(&self) -> Modifiers { + let mut modifiers = Modifiers::empty(); + + if self.event.alt_key() { + modifiers.insert(Modifiers::ALT); + } + if self.event.ctrl_key() { + modifiers.insert(Modifiers::CONTROL); + } + if self.event.meta_key() { + modifiers.insert(Modifiers::META); + } + if self.event.shift_key() { + modifiers.insert(Modifiers::SHIFT); + } + + modifiers + } +} + +impl WebEventExt for dioxus_html::KeyboardData { + type WebEvent = web_sys::KeyboardEvent; + + #[inline(always)] + fn try_as_web_event(&self) -> Option { + self.downcast::>() + .map(|e| e.event.clone()) + } +} diff --git a/packages/web/src/events/load.rs b/packages/web/src/events/load.rs new file mode 100644 index 0000000000..a797941458 --- /dev/null +++ b/packages/web/src/events/load.rs @@ -0,0 +1,37 @@ +use std::any::Any; + +use dioxus_html::HasImageData; +use web_sys::Event; + +use super::WebEventExt; + +#[derive(Clone)] +pub(crate) struct WebImageEvent { + raw: Event, + error: bool, +} + +impl WebImageEvent { + pub fn new(raw: Event, error: bool) -> Self { + Self { raw, error } + } +} + +impl HasImageData for WebImageEvent { + fn load_error(&self) -> bool { + self.error + } + + fn as_any(&self) -> &dyn Any { + &self.raw as &dyn Any + } +} + +impl WebEventExt for dioxus_html::ImageData { + type WebEvent = Event; + + #[inline(always)] + fn try_as_web_event(&self) -> Option { + self.downcast::().map(|e| e.raw.clone()) + } +} diff --git a/packages/web/src/events/media.rs b/packages/web/src/events/media.rs new file mode 100644 index 0000000000..bfd3d33a35 --- /dev/null +++ b/packages/web/src/events/media.rs @@ -0,0 +1,19 @@ +use dioxus_html::HasMediaData; + +use super::{Synthetic, WebEventExt}; + +impl HasMediaData for Synthetic { + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +impl WebEventExt for dioxus_html::MediaData { + type WebEvent = web_sys::Event; + + #[inline(always)] + fn try_as_web_event(&self) -> Option { + self.downcast::>() + .map(|e| e.event.clone()) + } +} diff --git a/packages/web/src/events/mod.rs b/packages/web/src/events/mod.rs new file mode 100644 index 0000000000..98d3e3bbed --- /dev/null +++ b/packages/web/src/events/mod.rs @@ -0,0 +1,278 @@ +use dioxus_html::{ + DragData, FormData, HtmlEventConverter, ImageData, MountedData, PlatformEventData, +}; +use drag::WebDragData; +use form::WebFormData; +use load::WebImageEvent; +use wasm_bindgen::JsCast; +use web_sys::{Document, Element, Event}; + +mod animation; +mod clipboard; +mod composition; +mod drag; +mod file; +mod focus; +mod form; +mod keyboard; +mod load; +mod media; +#[cfg(feature = "mounted")] +mod mounted; +mod mouse; +mod pointer; +mod resize; +mod selection; +mod toggle; +mod touch; +mod transition; +mod wheel; + +/// A wrapper for the websys event that allows us to give it the impls from dioxus-html +pub(crate) struct Synthetic { + /// The inner web sys event that the synthetic event wraps + pub event: T, +} + +impl Synthetic { + /// Create a new synthetic event from a web sys event + pub fn new(event: T) -> Self { + Self { event } + } +} + +pub(crate) struct WebEventConverter; + +#[inline(always)] +fn downcast_event(event: &dioxus_html::PlatformEventData) -> &GenericWebSysEvent { + event + .downcast::() + .expect("event should be a GenericWebSysEvent") +} + +impl HtmlEventConverter for WebEventConverter { + #[inline(always)] + fn convert_animation_data( + &self, + event: &dioxus_html::PlatformEventData, + ) -> dioxus_html::AnimationData { + Synthetic::::from(downcast_event(event).raw.clone()).into() + } + + #[inline(always)] + fn convert_clipboard_data( + &self, + event: &dioxus_html::PlatformEventData, + ) -> dioxus_html::ClipboardData { + Synthetic::new(downcast_event(event).raw.clone()).into() + } + + #[inline(always)] + fn convert_composition_data( + &self, + event: &dioxus_html::PlatformEventData, + ) -> dioxus_html::CompositionData { + Synthetic::::from(downcast_event(event).raw.clone()).into() + } + + #[inline(always)] + fn convert_drag_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::DragData { + let event = downcast_event(event); + DragData::new(WebDragData::new(event.raw.clone().unchecked_into())) + } + + #[inline(always)] + fn convert_focus_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::FocusData { + Synthetic::::from(downcast_event(event).raw.clone()).into() + } + + #[inline(always)] + fn convert_form_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::FormData { + let event = downcast_event(event); + FormData::new(WebFormData::new(event.element.clone(), event.raw.clone())) + } + + #[inline(always)] + fn convert_image_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::ImageData { + let event = downcast_event(event); + let error = event.raw.type_() == "error"; + ImageData::new(WebImageEvent::new(event.raw.clone(), error)) + } + + #[inline(always)] + fn convert_keyboard_data( + &self, + event: &dioxus_html::PlatformEventData, + ) -> dioxus_html::KeyboardData { + Synthetic::::from(downcast_event(event).raw.clone()).into() + } + + #[inline(always)] + fn convert_media_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::MediaData { + Synthetic::new(downcast_event(event).raw.clone()).into() + } + + #[allow(unused_variables)] + #[inline(always)] + fn convert_mounted_data(&self, event: &dioxus_html::PlatformEventData) -> MountedData { + #[cfg(feature = "mounted")] + { + Synthetic::new( + event + .downcast::() + .expect("event should be a web_sys::Element") + .clone(), + ) + .into() + } + #[cfg(not(feature = "mounted"))] + { + panic!("mounted events are not supported without the mounted feature on the dioxus-web crate enabled") + } + } + + #[inline(always)] + fn convert_mouse_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::MouseData { + Synthetic::::from(downcast_event(event).raw.clone()).into() + } + + #[inline(always)] + fn convert_pointer_data( + &self, + event: &dioxus_html::PlatformEventData, + ) -> dioxus_html::PointerData { + Synthetic::::from(downcast_event(event).raw.clone()).into() + } + + #[inline(always)] + fn convert_resize_data( + &self, + event: &dioxus_html::PlatformEventData, + ) -> dioxus_html::ResizeData { + Synthetic::::from(downcast_event(event).raw.clone()).into() + } + + #[inline(always)] + fn convert_scroll_data( + &self, + event: &dioxus_html::PlatformEventData, + ) -> dioxus_html::ScrollData { + Synthetic::new(downcast_event(event).raw.clone()).into() + } + + #[inline(always)] + fn convert_selection_data( + &self, + event: &dioxus_html::PlatformEventData, + ) -> dioxus_html::SelectionData { + Synthetic::new(downcast_event(event).raw.clone()).into() + } + + #[inline(always)] + fn convert_toggle_data( + &self, + event: &dioxus_html::PlatformEventData, + ) -> dioxus_html::ToggleData { + Synthetic::new(downcast_event(event).raw.clone()).into() + } + + #[inline(always)] + fn convert_touch_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::TouchData { + Synthetic::::from(downcast_event(event).raw.clone()).into() + } + + #[inline(always)] + fn convert_transition_data( + &self, + event: &dioxus_html::PlatformEventData, + ) -> dioxus_html::TransitionData { + Synthetic::::from(downcast_event(event).raw.clone()).into() + } + + #[inline(always)] + fn convert_wheel_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::WheelData { + Synthetic::::from(downcast_event(event).raw.clone()).into() + } +} + +/// A extension trait for web-sys events that provides a way to get the event as a web-sys event. +pub trait WebEventExt { + /// The web specific event type + type WebEvent; + + /// Try to downcast this event as a `web-sys` event. + fn try_as_web_event(&self) -> Option; + + /// Downcast this event as a `web-sys` event. + #[inline(always)] + fn as_web_event(&self) -> Self::WebEvent + where + Self::WebEvent: 'static, + { + self.try_as_web_event().unwrap_or_else(|| { + panic!( + "Error downcasting to `web-sys`, event should be a {}.", + std::any::type_name::() + ) + }) + } +} + +struct GenericWebSysEvent { + raw: Event, + element: Element, +} + +// todo: some of these events are being casted to the wrong event type. +// We need tests that simulate clicks/etc and make sure every event type works. +pub(crate) fn virtual_event_from_websys_event( + event: web_sys::Event, + target: Element, +) -> PlatformEventData { + PlatformEventData::new(Box::new(GenericWebSysEvent { + raw: event, + element: target, + })) +} + +pub(crate) fn load_document() -> Document { + web_sys::window() + .expect("should have access to the Window") + .document() + .expect("should have access to the Document") +} + +macro_rules! uncheck_convert { + ($t:ty) => { + impl From for Synthetic<$t> { + #[inline] + fn from(e: Event) -> Self { + let e: $t = e.unchecked_into(); + Self::new(e) + } + } + + impl From<&Event> for Synthetic<$t> { + #[inline] + fn from(e: &Event) -> Self { + let e: &$t = e.unchecked_ref(); + Self::new(e.clone()) + } + } + }; + ($($t:ty),+ $(,)?) => { + $(uncheck_convert!($t);)+ + }; +} + +uncheck_convert![ + web_sys::CompositionEvent, + web_sys::KeyboardEvent, + web_sys::TouchEvent, + web_sys::PointerEvent, + web_sys::WheelEvent, + web_sys::AnimationEvent, + web_sys::TransitionEvent, + web_sys::MouseEvent, + web_sys::FocusEvent, +]; diff --git a/packages/web/src/events/mounted.rs b/packages/web/src/events/mounted.rs new file mode 100644 index 0000000000..bf0dafc1a6 --- /dev/null +++ b/packages/web/src/events/mounted.rs @@ -0,0 +1,126 @@ +use dioxus_html::{ + geometry::euclid::{Point2D, Size2D}, + MountedData, +}; +use wasm_bindgen::JsCast; + +use super::{Synthetic, WebEventExt}; + +impl dioxus_html::RenderedElementBacking for Synthetic { + fn get_scroll_offset( + &self, + ) -> std::pin::Pin< + Box< + dyn std::future::Future< + Output = dioxus_html::MountedResult, + >, + >, + > { + let left = self.event.scroll_left(); + let top = self.event.scroll_top(); + let result = Ok(dioxus_html::geometry::PixelsVector2D::new( + left as f64, + top as f64, + )); + Box::pin(async { result }) + } + + fn get_scroll_size( + &self, + ) -> std::pin::Pin< + Box< + dyn std::future::Future< + Output = dioxus_html::MountedResult, + >, + >, + > { + let width = self.event.scroll_width(); + let height = self.event.scroll_height(); + let result = Ok(dioxus_html::geometry::PixelsSize::new( + width as f64, + height as f64, + )); + Box::pin(async { result }) + } + + fn get_client_rect( + &self, + ) -> std::pin::Pin< + Box< + dyn std::future::Future< + Output = dioxus_html::MountedResult, + >, + >, + > { + let rect = self.event.get_bounding_client_rect(); + let result = Ok(dioxus_html::geometry::PixelsRect::new( + Point2D::new(rect.left(), rect.top()), + Size2D::new(rect.width(), rect.height()), + )); + Box::pin(async { result }) + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } + + fn scroll_to( + &self, + behavior: dioxus_html::ScrollBehavior, + ) -> std::pin::Pin>>> { + let options = web_sys::ScrollIntoViewOptions::new(); + match behavior { + dioxus_html::ScrollBehavior::Instant => { + options.set_behavior(web_sys::ScrollBehavior::Instant); + } + dioxus_html::ScrollBehavior::Smooth => { + options.set_behavior(web_sys::ScrollBehavior::Smooth); + } + } + self.event + .scroll_into_view_with_scroll_into_view_options(&options); + + Box::pin(async { Ok(()) }) + } + + fn set_focus( + &self, + focus: bool, + ) -> std::pin::Pin>>> { + #[derive(Debug)] + struct FocusError(wasm_bindgen::JsValue); + + impl std::fmt::Display for FocusError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "failed to focus element {:?}", self.0) + } + } + + impl std::error::Error for FocusError {} + + let result = self + .event + .dyn_ref::() + .ok_or_else(|| { + dioxus_html::MountedError::OperationFailed(Box::new(FocusError( + self.event.clone().into(), + ))) + }) + .and_then(|e| { + (if focus { e.focus() } else { e.blur() }).map_err(|err| { + dioxus_html::MountedError::OperationFailed(Box::new(FocusError(err))) + }) + }); + Box::pin(async { result }) + } +} + +impl WebEventExt for MountedData { + type WebEvent = web_sys::Element; + + #[inline(always)] + fn try_as_web_event(&self) -> Option { + self.downcast::>() + .map(|e| e.event.clone()) + } +} diff --git a/packages/web/src/events/mouse.rs b/packages/web/src/events/mouse.rs new file mode 100644 index 0000000000..22b181174c --- /dev/null +++ b/packages/web/src/events/mouse.rs @@ -0,0 +1,87 @@ +use dioxus_html::{ + geometry::{ClientPoint, ElementPoint, PagePoint, ScreenPoint}, + input_data::{decode_mouse_button_set, MouseButton}, + prelude::{ + InteractionElementOffset, InteractionLocation, Modifiers, ModifiersInteraction, + PointerInteraction, + }, + HasDragData, HasFileData, HasMouseData, +}; +use web_sys::MouseEvent; + +use super::{Synthetic, WebEventExt}; + +impl InteractionLocation for Synthetic { + fn client_coordinates(&self) -> ClientPoint { + ClientPoint::new(self.event.client_x().into(), self.event.client_y().into()) + } + + fn page_coordinates(&self) -> PagePoint { + PagePoint::new(self.event.page_x().into(), self.event.page_y().into()) + } + + fn screen_coordinates(&self) -> ScreenPoint { + ScreenPoint::new(self.event.screen_x().into(), self.event.screen_y().into()) + } +} + +impl InteractionElementOffset for Synthetic { + fn element_coordinates(&self) -> ElementPoint { + ElementPoint::new(self.event.offset_x().into(), self.event.offset_y().into()) + } +} + +impl ModifiersInteraction for Synthetic { + fn modifiers(&self) -> Modifiers { + let mut modifiers = Modifiers::empty(); + + if self.event.alt_key() { + modifiers.insert(Modifiers::ALT); + } + if self.event.ctrl_key() { + modifiers.insert(Modifiers::CONTROL); + } + if self.event.meta_key() { + modifiers.insert(Modifiers::META); + } + if self.event.shift_key() { + modifiers.insert(Modifiers::SHIFT); + } + + modifiers + } +} + +impl PointerInteraction for Synthetic { + fn held_buttons(&self) -> dioxus_html::input_data::MouseButtonSet { + decode_mouse_button_set(self.event.buttons()) + } + + fn trigger_button(&self) -> Option { + Some(MouseButton::from_web_code(self.event.button())) + } +} + +impl HasMouseData for Synthetic { + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +impl HasFileData for Synthetic {} + +impl HasDragData for Synthetic { + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +impl WebEventExt for dioxus_html::MouseData { + type WebEvent = web_sys::MouseEvent; + + #[inline(always)] + fn try_as_web_event(&self) -> Option { + self.downcast::>() + .map(|e| e.event.clone()) + } +} diff --git a/packages/web/src/events/pointer.rs b/packages/web/src/events/pointer.rs new file mode 100644 index 0000000000..abc9d7d85a --- /dev/null +++ b/packages/web/src/events/pointer.rs @@ -0,0 +1,119 @@ +use dioxus_html::{ + geometry::{ClientPoint, ElementPoint, PagePoint, ScreenPoint}, + input_data::{decode_mouse_button_set, MouseButton}, + prelude::{ + InteractionElementOffset, InteractionLocation, Modifiers, ModifiersInteraction, + PointerInteraction, + }, + HasPointerData, +}; +use web_sys::PointerEvent; + +use super::{Synthetic, WebEventExt}; + +impl HasPointerData for Synthetic { + fn pointer_id(&self) -> i32 { + self.event.pointer_id() + } + + fn width(&self) -> i32 { + self.event.width() + } + + fn height(&self) -> i32 { + self.event.height() + } + + fn pressure(&self) -> f32 { + self.event.pressure() + } + + fn tangential_pressure(&self) -> f32 { + self.event.tangential_pressure() + } + + fn tilt_x(&self) -> i32 { + self.event.tilt_x() + } + + fn tilt_y(&self) -> i32 { + self.event.tilt_y() + } + + fn twist(&self) -> i32 { + self.event.twist() + } + + fn pointer_type(&self) -> String { + self.event.pointer_type() + } + + fn is_primary(&self) -> bool { + self.event.is_primary() + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +impl InteractionLocation for Synthetic { + fn client_coordinates(&self) -> ClientPoint { + ClientPoint::new(self.event.client_x().into(), self.event.client_y().into()) + } + + fn screen_coordinates(&self) -> ScreenPoint { + ScreenPoint::new(self.event.screen_x().into(), self.event.screen_y().into()) + } + + fn page_coordinates(&self) -> PagePoint { + PagePoint::new(self.event.page_x().into(), self.event.page_y().into()) + } +} + +impl InteractionElementOffset for Synthetic { + fn element_coordinates(&self) -> ElementPoint { + ElementPoint::new(self.event.offset_x().into(), self.event.offset_y().into()) + } +} + +impl ModifiersInteraction for Synthetic { + fn modifiers(&self) -> Modifiers { + let mut modifiers = Modifiers::empty(); + + if self.event.alt_key() { + modifiers.insert(Modifiers::ALT); + } + if self.event.ctrl_key() { + modifiers.insert(Modifiers::CONTROL); + } + if self.event.meta_key() { + modifiers.insert(Modifiers::META); + } + if self.event.shift_key() { + modifiers.insert(Modifiers::SHIFT); + } + + modifiers + } +} + +impl PointerInteraction for Synthetic { + fn held_buttons(&self) -> dioxus_html::input_data::MouseButtonSet { + decode_mouse_button_set(self.event.buttons()) + } + + fn trigger_button(&self) -> Option { + Some(MouseButton::from_web_code(self.event.button())) + } +} + +impl WebEventExt for dioxus_html::PointerData { + type WebEvent = web_sys::PointerEvent; + + #[inline(always)] + fn try_as_web_event(&self) -> Option { + self.downcast::>() + .map(|e| e.event.clone()) + } +} diff --git a/packages/web/src/events/resize.rs b/packages/web/src/events/resize.rs new file mode 100644 index 0000000000..028abe69d1 --- /dev/null +++ b/packages/web/src/events/resize.rs @@ -0,0 +1,57 @@ +use dioxus_html::{geometry::PixelsSize, HasResizeData, ResizeResult}; +use wasm_bindgen::JsCast; +use web_sys::{CustomEvent, Event, ResizeObserverEntry}; + +use super::{Synthetic, WebEventExt}; + +impl From for Synthetic { + #[inline] + fn from(e: Event) -> Self { + as From<&Event>>::from(&e) + } +} + +impl From<&Event> for Synthetic { + #[inline] + fn from(e: &Event) -> Self { + let e: &CustomEvent = e.unchecked_ref(); + let value = e.detail(); + Self::new(value.unchecked_into::()) + } +} + +impl HasResizeData for Synthetic { + fn as_any(&self) -> &dyn std::any::Any { + self + } + + fn get_border_box_size(&self) -> ResizeResult { + extract_first_size(self.event.border_box_size()) + } + + fn get_content_box_size(&self) -> ResizeResult { + extract_first_size(self.event.content_box_size()) + } +} + +impl WebEventExt for dioxus_html::ResizeData { + type WebEvent = web_sys::ResizeObserverEntry; + + #[inline(always)] + fn try_as_web_event(&self) -> Option { + self.downcast::>() + .map(|e| e.event.clone()) + } +} + +fn extract_first_size(resize_observer_output: js_sys::Array) -> ResizeResult { + let first = resize_observer_output.get(0); + let size = first.unchecked_into::(); + + // inline_size matches the width of the element if its writing-mode is horizontal, the height otherwise + let inline_size = size.inline_size(); + // block_size matches the height of the element if its writing-mode is horizontal, the width otherwise + let block_size = size.block_size(); + + Ok(PixelsSize::new(inline_size, block_size)) +} diff --git a/packages/web/src/events/selection.rs b/packages/web/src/events/selection.rs new file mode 100644 index 0000000000..d68a6ec96e --- /dev/null +++ b/packages/web/src/events/selection.rs @@ -0,0 +1,19 @@ +use dioxus_html::HasSelectionData; + +use super::{Synthetic, WebEventExt}; + +impl HasSelectionData for Synthetic { + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +impl WebEventExt for dioxus_html::SelectionData { + type WebEvent = web_sys::Event; + + #[inline(always)] + fn try_as_web_event(&self) -> Option { + self.downcast::>() + .map(|e| e.event.clone()) + } +} diff --git a/packages/web/src/events/toggle.rs b/packages/web/src/events/toggle.rs new file mode 100644 index 0000000000..67d2f76b3c --- /dev/null +++ b/packages/web/src/events/toggle.rs @@ -0,0 +1,19 @@ +use dioxus_html::HasToggleData; + +use super::{Synthetic, WebEventExt}; + +impl HasToggleData for Synthetic { + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +impl WebEventExt for dioxus_html::ToggleData { + type WebEvent = web_sys::Event; + + #[inline(always)] + fn try_as_web_event(&self) -> Option { + self.downcast::>() + .map(|e| e.event.clone()) + } +} diff --git a/packages/web/src/events/touch.rs b/packages/web/src/events/touch.rs new file mode 100644 index 0000000000..4a108e4f7a --- /dev/null +++ b/packages/web/src/events/touch.rs @@ -0,0 +1,111 @@ +use dioxus_html::{ + geometry::{ClientPoint, PagePoint, ScreenPoint}, + prelude::{InteractionLocation, Modifiers, ModifiersInteraction}, + HasTouchPointData, TouchPoint, +}; +use web_sys::{Touch, TouchEvent}; + +use super::{Synthetic, WebEventExt}; + +impl ModifiersInteraction for Synthetic { + fn modifiers(&self) -> Modifiers { + let mut modifiers = Modifiers::empty(); + + if self.event.alt_key() { + modifiers.insert(Modifiers::ALT); + } + if self.event.ctrl_key() { + modifiers.insert(Modifiers::CONTROL); + } + if self.event.meta_key() { + modifiers.insert(Modifiers::META); + } + if self.event.shift_key() { + modifiers.insert(Modifiers::SHIFT); + } + + modifiers + } +} + +impl dioxus_html::events::HasTouchData for Synthetic { + fn touches(&self) -> Vec { + let touches = TouchEvent::touches(&self.event); + let mut result = Vec::with_capacity(touches.length() as usize); + for i in 0..touches.length() { + let touch = touches.get(i).unwrap(); + result.push(TouchPoint::new(Synthetic::new(touch))); + } + result + } + + fn touches_changed(&self) -> Vec { + let touches = self.event.changed_touches(); + let mut result = Vec::with_capacity(touches.length() as usize); + for i in 0..touches.length() { + let touch = touches.get(i).unwrap(); + result.push(TouchPoint::new(Synthetic::new(touch))); + } + result + } + + fn target_touches(&self) -> Vec { + let touches = self.event.target_touches(); + let mut result = Vec::with_capacity(touches.length() as usize); + for i in 0..touches.length() { + let touch = touches.get(i).unwrap(); + result.push(TouchPoint::new(Synthetic::new(touch))); + } + result + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +impl HasTouchPointData for Synthetic { + fn identifier(&self) -> i32 { + self.event.identifier() + } + + fn radius(&self) -> ScreenPoint { + ScreenPoint::new(self.event.radius_x().into(), self.event.radius_y().into()) + } + + fn rotation(&self) -> f64 { + self.event.rotation_angle() as f64 + } + + fn force(&self) -> f64 { + self.event.force() as f64 + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +impl InteractionLocation for Synthetic { + fn client_coordinates(&self) -> ClientPoint { + ClientPoint::new(self.event.client_x().into(), self.event.client_y().into()) + } + + fn screen_coordinates(&self) -> ScreenPoint { + ScreenPoint::new(self.event.screen_x().into(), self.event.screen_y().into()) + } + + fn page_coordinates(&self) -> PagePoint { + PagePoint::new(self.event.page_x().into(), self.event.page_y().into()) + } +} + +impl WebEventExt for dioxus_html::TouchData { + type WebEvent = web_sys::TouchEvent; + + #[inline(always)] + fn try_as_web_event(&self) -> Option { + self.downcast::>() + .map(|e| e.event.clone()) + } +} diff --git a/packages/web/src/events/transition.rs b/packages/web/src/events/transition.rs new file mode 100644 index 0000000000..ee0638cbc2 --- /dev/null +++ b/packages/web/src/events/transition.rs @@ -0,0 +1,22 @@ +use dioxus_html::HasTransitionData; +use web_sys::TransitionEvent; + +use super::Synthetic; + +impl HasTransitionData for Synthetic { + fn elapsed_time(&self) -> f32 { + self.event.elapsed_time() + } + + fn property_name(&self) -> String { + self.event.property_name() + } + + fn pseudo_element(&self) -> String { + self.event.pseudo_element() + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} diff --git a/packages/web/src/events/wheel.rs b/packages/web/src/events/wheel.rs new file mode 100644 index 0000000000..ffc9c7183c --- /dev/null +++ b/packages/web/src/events/wheel.rs @@ -0,0 +1,90 @@ +use dioxus_html::{ + geometry::{ClientPoint, ElementPoint, PagePoint, ScreenPoint}, + input_data::{decode_mouse_button_set, MouseButton}, + prelude::{ + InteractionElementOffset, InteractionLocation, Modifiers, ModifiersInteraction, + PointerInteraction, + }, + HasMouseData, HasScrollData, HasWheelData, +}; +use web_sys::{Event, WheelEvent}; + +use super::Synthetic; + +impl HasWheelData for Synthetic { + fn delta(&self) -> dioxus_html::geometry::WheelDelta { + dioxus_html::geometry::WheelDelta::from_web_attributes( + self.event.delta_mode(), + self.event.delta_x(), + self.event.delta_y(), + self.event.delta_z(), + ) + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +impl HasMouseData for Synthetic { + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +impl InteractionLocation for Synthetic { + fn client_coordinates(&self) -> ClientPoint { + ClientPoint::new(self.event.client_x().into(), self.event.client_y().into()) + } + + fn screen_coordinates(&self) -> ScreenPoint { + ScreenPoint::new(self.event.screen_x().into(), self.event.screen_y().into()) + } + + fn page_coordinates(&self) -> PagePoint { + PagePoint::new(self.event.page_x().into(), self.event.page_y().into()) + } +} + +impl InteractionElementOffset for Synthetic { + fn element_coordinates(&self) -> ElementPoint { + ElementPoint::new(self.event.offset_x().into(), self.event.offset_y().into()) + } +} + +impl ModifiersInteraction for Synthetic { + fn modifiers(&self) -> Modifiers { + let mut modifiers = Modifiers::empty(); + + if self.event.alt_key() { + modifiers.insert(Modifiers::ALT); + } + if self.event.ctrl_key() { + modifiers.insert(Modifiers::CONTROL); + } + if self.event.meta_key() { + modifiers.insert(Modifiers::META); + } + if self.event.shift_key() { + modifiers.insert(Modifiers::SHIFT); + } + + modifiers + } +} + +impl PointerInteraction for Synthetic { + fn held_buttons(&self) -> dioxus_html::input_data::MouseButtonSet { + decode_mouse_button_set(self.event.buttons()) + } + + fn trigger_button(&self) -> Option { + Some(MouseButton::from_web_code(self.event.button())) + } +} + +impl HasScrollData for Synthetic { + fn as_any(&self) -> &dyn std::any::Any { + self + } +} diff --git a/packages/html/src/web_sys_bind/file_engine.rs b/packages/web/src/file_engine.rs similarity index 99% rename from packages/html/src/web_sys_bind/file_engine.rs rename to packages/web/src/file_engine.rs index ce138c96b6..f0a0ff6988 100644 --- a/packages/html/src/web_sys_bind/file_engine.rs +++ b/packages/web/src/file_engine.rs @@ -1,6 +1,6 @@ use std::any::Any; -use crate::FileEngine; +use dioxus_html::FileEngine; use futures_channel::oneshot; use js_sys::Uint8Array; use wasm_bindgen::{prelude::Closure, JsCast}; diff --git a/packages/html/src/js/eval.js b/packages/web/src/js/eval.js similarity index 93% rename from packages/html/src/js/eval.js rename to packages/web/src/js/eval.js index 216f11c9d3..111158c3ea 100644 --- a/packages/html/src/js/eval.js +++ b/packages/web/src/js/eval.js @@ -1 +1 @@ -class Channel{pending;waiting;constructor(){this.pending=[],this.waiting=[]}send(data){if(this.waiting.length>0){this.waiting.shift()(data);return}this.pending.push(data)}async recv(){return new Promise((resolve,_reject)=>{if(this.pending.length>0){resolve(this.pending.shift());return}this.waiting.push(resolve)})}}class WeakDioxusChannel{inner;constructor(channel){this.inner=new WeakRef(channel)}rustSend(data){let channel=this.inner.deref();if(channel)channel.rustSend(data)}async rustRecv(){let channel=this.inner.deref();if(channel)return await channel.rustRecv()}}class DioxusChannel{weak(){return new WeakDioxusChannel(this)}}class WebDioxusChannel extends DioxusChannel{js_to_rust;rust_to_js;owner;constructor(owner){super();this.owner=owner,this.js_to_rust=new Channel,this.rust_to_js=new Channel}weak(){return new WeakDioxusChannel(this)}async recv(){return await this.rust_to_js.recv()}send(data){this.js_to_rust.send(data)}rustSend(data){this.rust_to_js.send(data)}async rustRecv(){return await this.js_to_rust.recv()}}export{WebDioxusChannel,WeakDioxusChannel,DioxusChannel,Channel}; +class Channel{pending;waiting;constructor(){this.pending=[],this.waiting=[]}send(data){if(this.waiting.length>0){this.waiting.shift()(data);return}this.pending.push(data)}async recv(){return new Promise((resolve,_reject)=>{if(this.pending.length>0){resolve(this.pending.shift());return}this.waiting.push(resolve)})}}class WeakDioxusChannel{inner;constructor(channel){this.inner=new WeakRef(channel)}rustSend(data){let channel=this.inner.deref();if(channel)channel.rustSend(data)}async rustRecv(){let channel=this.inner.deref();if(channel)return await channel.rustRecv()}}class DioxusChannel{weak(){return new WeakDioxusChannel(this)}}class WebDioxusChannel extends DioxusChannel{js_to_rust;rust_to_js;owner;constructor(owner){super();this.owner=owner,this.js_to_rust=new Channel,this.rust_to_js=new Channel}weak(){return new WeakDioxusChannel(this)}async recv(){return await this.rust_to_js.recv()}send(data){this.js_to_rust.send(data)}rustSend(data){this.rust_to_js.send(data)}async rustRecv(){return await this.js_to_rust.recv()}}export{WebDioxusChannel}; diff --git a/packages/web/src/js/hash.txt b/packages/web/src/js/hash.txt new file mode 100644 index 0000000000..f397e27fa1 --- /dev/null +++ b/packages/web/src/js/hash.txt @@ -0,0 +1 @@ +[3479327739946104450] \ No newline at end of file diff --git a/packages/web/src/lib.rs b/packages/web/src/lib.rs index 4600ddb6c0..8e772cef70 100644 --- a/packages/web/src/lib.rs +++ b/packages/web/src/lib.rs @@ -29,15 +29,19 @@ use futures_util::{pin_mut, select, FutureExt, StreamExt}; mod cfg; mod dom; -mod event; +mod events; pub mod launch; mod mutations; -pub use event::*; +pub use events::*; #[cfg(feature = "document")] mod document; +#[cfg(feature = "file_engine")] +mod file_engine; #[cfg(feature = "document")] pub use document::WebDocument; +#[cfg(feature = "file_engine")] +pub use file_engine::*; #[cfg(all(feature = "devtools", debug_assertions))] mod devtools; diff --git a/packages/web/src/ts/eval.ts b/packages/web/src/ts/eval.ts new file mode 100644 index 0000000000..3c87d0d252 --- /dev/null +++ b/packages/web/src/ts/eval.ts @@ -0,0 +1,43 @@ +import { + DioxusChannel, + Channel, + WeakDioxusChannel, +} from "../../../html/src/ts/eval"; + +export class WebDioxusChannel extends DioxusChannel { + js_to_rust: Channel; + rust_to_js: Channel; + owner: any; + + constructor(owner: any) { + super(); + this.owner = owner; + this.js_to_rust = new Channel(); + this.rust_to_js = new Channel(); + } + + // Return a weak reference to this channel + weak(): WeakDioxusChannel { + return new WeakDioxusChannel(this); + } + + // Receive message from Rust + async recv() { + return await this.rust_to_js.recv(); + } + + // Send message to rust. + send(data: any) { + this.js_to_rust.send(data); + } + + // Send data from rust to javascript + rustSend(data: any) { + this.rust_to_js.send(data); + } + + // Receive data sent from javascript in rust + async rustRecv(): Promise { + return await this.js_to_rust.recv(); + } +} From fe31b6e7e9a4ac3090d5f4c6dfc7c772ede50b2c Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 26 Sep 2024 16:21:28 -0500 Subject: [PATCH 121/139] Fix downcasting web events --- packages/html/src/events/mounted.rs | 8 +++++++- packages/web/src/events/wheel.rs | 22 +++++++++++++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/packages/html/src/events/mounted.rs b/packages/html/src/events/mounted.rs index cccf53c7a6..ed021b6aed 100644 --- a/packages/html/src/events/mounted.rs +++ b/packages/html/src/events/mounted.rs @@ -1,7 +1,7 @@ //! Handles querying data from the renderer use std::{ - fmt::{Display, Formatter}, + fmt::{Debug, Display, Formatter}, future::Future, pin::Pin, }; @@ -70,6 +70,12 @@ pub struct MountedData { inner: Box, } +impl Debug for MountedData { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("MountedData").finish() + } +} + impl From for MountedData { fn from(e: E) -> Self { Self { inner: Box::new(e) } diff --git a/packages/web/src/events/wheel.rs b/packages/web/src/events/wheel.rs index ffc9c7183c..c32889cd85 100644 --- a/packages/web/src/events/wheel.rs +++ b/packages/web/src/events/wheel.rs @@ -9,7 +9,7 @@ use dioxus_html::{ }; use web_sys::{Event, WheelEvent}; -use super::Synthetic; +use super::{Synthetic, WebEventExt}; impl HasWheelData for Synthetic { fn delta(&self) -> dioxus_html::geometry::WheelDelta { @@ -88,3 +88,23 @@ impl HasScrollData for Synthetic { self } } + +impl WebEventExt for dioxus_html::ScrollData { + type WebEvent = web_sys::Event; + + #[inline(always)] + fn try_as_web_event(&self) -> Option { + self.downcast::>() + .map(|e| e.event.clone()) + } +} + +impl WebEventExt for dioxus_html::WheelData { + type WebEvent = web_sys::WheelEvent; + + #[inline(always)] + fn try_as_web_event(&self) -> Option { + self.downcast::>() + .map(|e| e.event.clone()) + } +} From 1d5ec2a49eed17b661b4f188550b91768b278149 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 26 Sep 2024 16:30:59 -0500 Subject: [PATCH 122/139] Fix desktop with tokio disabled --- packages/desktop/src/file_upload.rs | 56 +++++++++++++++++++++-------- packages/desktop/src/launch.rs | 20 ++++++----- 2 files changed, 53 insertions(+), 23 deletions(-) diff --git a/packages/desktop/src/file_upload.rs b/packages/desktop/src/file_upload.rs index dab4fcac47..5448586d35 100644 --- a/packages/desktop/src/file_upload.rs +++ b/packages/desktop/src/file_upload.rs @@ -2,8 +2,8 @@ use std::any::Any; -use tokio::fs::File; -use tokio::io::AsyncReadExt; +#[cfg(feature = "tokio_runtime")] +use tokio::{fs::File, io::AsyncReadExt}; use dioxus_html::{ geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint}, @@ -262,30 +262,58 @@ impl FileEngine for NativeFileEngine { } async fn file_size(&self, file: &str) -> Option { - let file = File::open(file).await.ok()?; - Some(file.metadata().await.ok()?.len()) + #[cfg(feature = "tokio_runtime")] + { + let file = File::open(file).await.ok()?; + Some(file.metadata().await.ok()?.len()) + } + #[cfg(not(feature = "tokio_runtime"))] + { + None + } } async fn read_file(&self, file: &str) -> Option> { - let mut file = File::open(file).await.ok()?; + #[cfg(feature = "tokio_runtime")] + { + let mut file = File::open(file).await.ok()?; - let mut contents = Vec::new(); - file.read_to_end(&mut contents).await.ok()?; + let mut contents = Vec::new(); + file.read_to_end(&mut contents).await.ok()?; - Some(contents) + Some(contents) + } + #[cfg(not(feature = "tokio_runtime"))] + { + None + } } async fn read_file_to_string(&self, file: &str) -> Option { - let mut file = File::open(file).await.ok()?; + #[cfg(feature = "tokio_runtime")] + { + let mut file = File::open(file).await.ok()?; - let mut contents = String::new(); - file.read_to_string(&mut contents).await.ok()?; + let mut contents = String::new(); + file.read_to_string(&mut contents).await.ok()?; - Some(contents) + Some(contents) + } + #[cfg(not(feature = "tokio_runtime"))] + { + None + } } async fn get_native_file(&self, file: &str) -> Option> { - let file = File::open(file).await.ok()?; - Some(Box::new(file)) + #[cfg(feature = "tokio_runtime")] + { + let file = File::open(file).await.ok()?; + Some(Box::new(file)) + } + #[cfg(not(feature = "tokio_runtime"))] + { + None + } } } diff --git a/packages/desktop/src/launch.rs b/packages/desktop/src/launch.rs index 0bef0fc113..1a66d169b7 100644 --- a/packages/desktop/src/launch.rs +++ b/packages/desktop/src/launch.rs @@ -63,18 +63,20 @@ pub fn launch_virtual_dom_blocking(virtual_dom: VirtualDom, desktop_config: Conf /// Launches the WebView and runs the event loop, with configuration and root props. pub fn launch_virtual_dom(virtual_dom: VirtualDom, desktop_config: Config) -> ! { #[cfg(feature = "tokio_runtime")] - tokio::runtime::Builder::new_multi_thread() - .enable_all() - .build() - .unwrap() - .block_on(tokio::task::unconstrained(async move { - launch_virtual_dom_blocking(virtual_dom, desktop_config) - })); + { + tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .unwrap() + .block_on(tokio::task::unconstrained(async move { + launch_virtual_dom_blocking(virtual_dom, desktop_config) + })); + + unreachable!("The desktop launch function will never exit") + } #[cfg(not(feature = "tokio_runtime"))] launch_virtual_dom_blocking(virtual_dom, desktop_config); - - unreachable!("The desktop launch function will never exit") } /// Launches the WebView and runs the event loop, with configuration and root props. From 90e2c8c8bc8de69db1e708a873ec6f899aea5ed1 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 26 Sep 2024 18:11:01 -0500 Subject: [PATCH 123/139] clean up the git diff a bit --- packages/core-types/src/formatter.rs | 31 ++++++++++++++++++--- packages/core/src/hotreload_utils.rs | 34 +++++++++++------------ packages/core/src/nodes.rs | 40 ---------------------------- 3 files changed, 45 insertions(+), 60 deletions(-) diff --git a/packages/core-types/src/formatter.rs b/packages/core-types/src/formatter.rs index a4df243729..4048dadfec 100644 --- a/packages/core-types/src/formatter.rs +++ b/packages/core-types/src/formatter.rs @@ -2,13 +2,38 @@ use std::borrow::Cow; /// Take this type and format it into a Cow<'static, str> /// -/// This trait exists so libraries like manganis can implement this type for asssets without depending +/// This trait exists so libraries like manganis can implement this type for assets without depending /// on dioxus-core, which can be heavy in proc macros and build scripts. /// /// We don't want a blanket impl for T: Display because that might conflict for the other integral data /// types of AttributeValue -/// -/// Todo: we might be able to specialize without this just with Display. pub trait DioxusFormattable { fn format(&self) -> Cow<'static, str>; } + +impl DioxusFormattable for &'static str { + fn format(&self) -> Cow<'static, str> { + self.into() + } +} + +impl DioxusFormattable for String { + fn format(&self) -> Cow<'static, str> { + self.into() + } +} + +impl DioxusFormattable for Arguments<'_> { + fn format(&self) -> Cow<'static, str> { + self.to_string().into() + } +} + +/// A marker trait that automatically implements [`DioxusFormattable`] through the display impl +pub trait DioxusFormattableThroughDisplay: Display {} + +impl DioxusFormattable for T { + fn format(&self) -> Cow<'static, str> { + self.to_string().into() + } +} diff --git a/packages/core/src/hotreload_utils.rs b/packages/core/src/hotreload_utils.rs index 8527c170e6..e94ce0e505 100644 --- a/packages/core/src/hotreload_utils.rs +++ b/packages/core/src/hotreload_utils.rs @@ -113,23 +113,23 @@ pub enum FmtSegment { }, } -/// let __pool = DynamicValuePool::new( -/// vec![...], -/// vec![...], -/// vec![...], -/// ); -/// VNode::new( -/// None, -/// Template { -/// name: "...", -/// roots: &[...], -/// node_paths: &[..], -/// attr_paths: &[...], -/// }, -/// Box::new([...]), -/// Box::new([...]), -/// ) -/// +// let __pool = DynamicValuePool::new( +// vec![...], +// vec![...], +// vec![...], +// ); +// VNode::new( +// None, +// Template { +// name: "...", +// roots: &[...], +// node_paths: &[..], +// attr_paths: &[...], +// }, +// Box::new([...]), +// Box::new([...]), +// ) + // Open questions: // - How do we handle type coercion for different sized component property integers? // - Should non-string hot literals go through the centralized pool? diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index 9e7f3e7639..ef8dcf25c7 100644 --- a/packages/core/src/nodes.rs +++ b/packages/core/src/nodes.rs @@ -889,18 +889,6 @@ where }) } } -pub struct OptionDisplayMarker; -impl IntoDynNode for Option -where - T: Display, -{ - fn into_dyn_node(self) -> DynamicNode { - todo!() - // DynamicNode::Text(VText { - // value: self.to_string(), - // }) - } -} impl IntoDynNode for Option { fn into_dyn_node(self) -> DynamicNode { @@ -911,34 +899,6 @@ impl IntoDynNode for Option { } } -// // struct DisplayMarker; -// // impl IntoDynNode for Option { -// // fn into_dyn_node(self) -> DynamicNode { -// // todo!() -// // } -// // } - -// impl IntoDynNode for &str { -// fn into_dyn_node(self) -> DynamicNode { -// DynamicNode::Text(VText { -// value: self.to_string(), -// }) -// } -// } -// impl IntoDynNode for String { -// fn into_dyn_node(self) -> DynamicNode { -// DynamicNode::Text(VText { value: self }) -// } -// } -// impl IntoDynNode for Arguments<'_> { -// fn into_dyn_node(self) -> DynamicNode { -// DynamicNode::Text(VText { -// value: self.to_string(), -// }) -// } -// } - -// Note that we're using the E as a generic but this is never crafted anyways. pub struct FromNodeIterator; impl IntoDynNode for T where From 132d1555ecd8f878b1ab4e78965bc9a1a16910d0 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 30 Sep 2024 10:11:55 -0500 Subject: [PATCH 124/139] Revert all changes outside the document crate --- .vscode/settings.json | 2 +- Cargo.lock | 3810 ++++++++++++----- Cargo.toml | 71 +- packages/cli-config/.gitignore | 4 + packages/config-macro/src/lib.rs | 2 +- packages/core-macro/src/props/mod.rs | 4 +- packages/core-types/src/event.rs | 203 - packages/core-types/src/formatter.rs | 31 +- packages/core-types/src/hotreload.rs | 480 --- packages/core-types/src/lib.rs | 10 - packages/core/Cargo.toml | 36 +- packages/core/src/events.rs | 191 + packages/core/src/hotreload_utils.rs | 34 +- packages/core/src/lib.rs | 1 - packages/core/src/nodes.rs | 93 +- packages/core/src/runtime.rs | 11 +- packages/core/tests/suspense.rs | 2 +- packages/desktop/Cargo.toml | 50 +- packages/desktop/build.rs | 9 - packages/desktop/headless_tests/eval.rs | 30 +- packages/desktop/headless_tests/rendering.rs | 2 +- packages/desktop/headless_tests/utils.rs | 5 +- packages/desktop/src/app.rs | 1 + packages/desktop/src/assets.rs | 26 +- packages/desktop/src/desktop_context.rs | 48 +- packages/desktop/src/document.rs | 106 +- packages/desktop/src/element.rs | 227 +- packages/desktop/src/file_upload.rs | 17 +- packages/desktop/src/hooks.rs | 15 +- packages/desktop/src/index.html | 2 - packages/desktop/src/ipc.rs | 4 + packages/desktop/src/launch.rs | 2 + packages/desktop/src/lib.rs | 1 + packages/desktop/src/protocol.rs | 331 +- packages/desktop/src/query.rs | 240 ++ packages/desktop/src/webview.rs | 27 +- packages/devtools/tests/roundtrip.rs | 36 - packages/dioxus-lib/Cargo.toml | 1 - packages/dioxus/Cargo.toml | 79 +- packages/dioxus/src/launch.rs | 720 ++-- packages/dioxus/src/lib.rs | 94 +- packages/fullstack/Cargo.toml | 34 +- packages/fullstack/src/config.rs | 7 - packages/fullstack/src/document/mod.rs | 2 - packages/fullstack/src/document/server.rs | 23 +- packages/fullstack/src/document/web.rs | 67 +- .../fullstack/src/html_storage/serialize.rs | 5 +- packages/fullstack/src/launch.rs | 6 +- packages/fullstack/src/render.rs | 3 +- packages/fullstack/src/serve_config.rs | 4 +- packages/html/Cargo.toml | 8 +- .../attributes.rs => html/assets/script.js} | 0 .../src/core.rs => html/assets/style.css} | 0 packages/{document => html}/docs/eval.md | 0 packages/{document => html}/docs/head.md | 0 packages/html/src/document/eval.rs | 134 + .../src => html/src/document}/head.rs | 39 +- packages/html/src/events/animation.rs | 2 +- packages/html/src/events/clipboard.rs | 2 +- packages/html/src/events/composition.rs | 2 +- packages/html/src/events/drag.rs | 2 +- packages/html/src/events/focus.rs | 2 +- packages/html/src/events/form.rs | 2 +- packages/html/src/events/image.rs | 2 +- packages/html/src/events/keyboard.rs | 2 +- packages/html/src/events/media.rs | 2 +- packages/html/src/events/mounted.rs | 63 +- packages/html/src/events/mouse.rs | 2 +- packages/html/src/events/pointer.rs | 2 +- packages/html/src/events/resize.rs | 2 +- packages/html/src/events/scroll.rs | 2 +- packages/html/src/events/selection.rs | 2 +- packages/html/src/events/toggle.rs | 2 +- packages/html/src/events/touch.rs | 2 +- packages/html/src/events/transition.rs | 2 +- packages/html/src/events/wheel.rs | 2 +- packages/html/src/js/head.js | 1 + packages/html/src/lib.rs | 12 +- packages/html/src/render_template.rs | 40 + packages/html/src/ts/.gitignore | 3 + packages/html/src/ts/head.ts | 19 + packages/{web => html}/tsconfig.json | 0 packages/interpreter/Cargo.toml | 16 +- packages/interpreter/src/js/common.js | 2 +- packages/interpreter/src/js/core.js | 2 +- packages/interpreter/src/js/hash.txt | 2 +- packages/interpreter/src/js/native.js | 2 +- packages/interpreter/src/ts/native.ts | 47 +- packages/liveview/Cargo.toml | 11 +- packages/liveview/build.rs | 7 - packages/liveview/examples/axum.rs | 2 +- packages/liveview/examples/axum_stress.rs | 2 +- .../liveview/src/adapters/axum_adapter.rs | 3 +- packages/liveview/src/config.rs | 1 + packages/liveview/src/document.rs | 112 - packages/liveview/src/element.rs | 134 +- packages/liveview/src/eval.rs | 81 + packages/liveview/src/js/hash.txt | 1 - packages/liveview/src/js/main.js | 1 - packages/liveview/src/launch.rs | 1 - packages/liveview/src/lib.rs | 21 +- packages/liveview/src/{ts/main.ts => main.js} | 33 +- packages/liveview/src/pool.rs | 4 +- packages/liveview/tsconfig.json | 19 - .../playwright-tests/fullstack/src/main.rs | 4 +- .../nested-suspense/src/main.rs | 4 +- .../static-generation/src/main.rs | 2 +- .../suspense-carousel/Cargo.toml | 7 +- .../suspense-carousel/src/main.rs | 12 +- packages/playwright-tests/web/src/main.rs | 25 +- packages/router-macro/src/lib.rs | 8 - packages/router/Cargo.toml | 33 +- packages/router/examples/manual.rs | 68 - packages/router/examples/simple_routes.rs | 204 + packages/router/src/components/link.rs | 108 +- packages/router/src/components/mod.rs | 15 - packages/router/src/components/outlet.rs | 2 +- packages/router/src/components/router.rs | 52 +- .../router/src/contexts/generic_router.rs | 102 - packages/router/src/contexts/outlet.rs | 2 +- packages/router/src/contexts/router.rs | 452 +- .../src/history/liveview.rs} | 7 +- packages/router/src/history/memory.rs | 54 +- packages/router/src/history/mod.rs | 388 ++ .../history.rs => router/src/history/web.rs} | 43 - .../{web => router}/src/history/web_hash.rs | 0 packages/router/src/history/web_history.rs | 42 + .../{web => router}/src/history/web_scroll.rs | 0 packages/router/src/hooks/use_route.rs | 2 +- packages/router/src/into_routable.rs | 76 - packages/router/src/lib.rs | 26 +- packages/router/src/navigation.rs | 72 +- packages/router/src/routable.rs | 727 +++- packages/router/src/routable/hash.rs | 89 - packages/router/src/routable/query.rs | 147 - packages/router/src/routable/segments.rs | 178 - packages/router/src/routable/sitemap.rs | 0 packages/router/src/router_cfg.rs | 101 +- packages/router/tests/site_map.rs | 3 - packages/router/tests/via_ssr/link.rs | 55 +- packages/router/tests/via_ssr/outlet.rs | 30 +- packages/router/tests/via_ssr/redirect.rs | 6 +- .../router/tests/via_ssr/without_index.rs | 2 +- packages/rsx-hotreload/src/context.rs | 1 - packages/rsx/src/component.rs | 2 +- packages/runtime-config/Cargo.toml | 6 - packages/runtime-config/README.md | 7 - packages/runtime-config/src/lib.rs | 24 - packages/server/Cargo.toml | 6 - packages/server/src/document.rs | 1 - packages/server/src/lib.rs | 1 - packages/ssr/src/renderer.rs | 1 + packages/static-generation/Cargo.toml | 5 +- packages/web/Cargo.toml | 29 +- packages/web/src/devtools.rs | 18 +- packages/web/src/document.rs | 169 +- packages/web/src/event/drag.rs | 95 - packages/web/src/event/ext.rs | 155 - packages/web/src/event/file.rs | 144 - packages/web/src/event/form.rs | 150 - packages/web/src/event/image.rs | 26 - packages/web/src/event/resize.rs | 1 - packages/web/src/event/synthetic.rs | 622 --- packages/web/src/hydration/deserialize.rs | 6 +- packages/web/src/hydration/hydrate.rs | 5 +- packages/web/src/hydration/mod.rs | 18 - packages/web/src/launch.rs | 10 +- packages/web/src/lib.rs | 60 +- packages/web/src/web_sys_bind/file_engine.rs | 133 - 169 files changed, 6769 insertions(+), 6142 deletions(-) create mode 100644 packages/cli-config/.gitignore delete mode 100644 packages/core-types/src/event.rs delete mode 100644 packages/core-types/src/hotreload.rs create mode 100644 packages/desktop/src/query.rs delete mode 100644 packages/devtools/tests/roundtrip.rs rename packages/{core-types/src/attributes.rs => html/assets/script.js} (100%) rename packages/{core-types/src/core.rs => html/assets/style.css} (100%) rename packages/{document => html}/docs/eval.md (100%) rename packages/{document => html}/docs/head.md (100%) create mode 100644 packages/html/src/document/eval.rs rename packages/{document/src => html/src/document}/head.rs (94%) create mode 100644 packages/html/src/js/head.js create mode 100644 packages/html/src/render_template.rs create mode 100644 packages/html/src/ts/.gitignore create mode 100644 packages/html/src/ts/head.ts rename packages/{web => html}/tsconfig.json (100%) delete mode 100644 packages/liveview/build.rs delete mode 100644 packages/liveview/src/document.rs create mode 100644 packages/liveview/src/eval.rs delete mode 100644 packages/liveview/src/js/hash.txt delete mode 100644 packages/liveview/src/js/main.js rename packages/liveview/src/{ts/main.ts => main.js} (78%) delete mode 100644 packages/liveview/tsconfig.json delete mode 100644 packages/router/examples/manual.rs create mode 100644 packages/router/examples/simple_routes.rs delete mode 100644 packages/router/src/components/mod.rs delete mode 100644 packages/router/src/contexts/generic_router.rs rename packages/{liveview/src/history.rs => router/src/history/liveview.rs} (99%) rename packages/{web/src/history.rs => router/src/history/web.rs} (88%) rename packages/{web => router}/src/history/web_hash.rs (100%) create mode 100644 packages/router/src/history/web_history.rs rename packages/{web => router}/src/history/web_scroll.rs (100%) delete mode 100644 packages/router/src/into_routable.rs delete mode 100644 packages/router/src/routable/hash.rs delete mode 100644 packages/router/src/routable/query.rs delete mode 100644 packages/router/src/routable/segments.rs delete mode 100644 packages/router/src/routable/sitemap.rs delete mode 100644 packages/rsx-hotreload/src/context.rs delete mode 100644 packages/runtime-config/Cargo.toml delete mode 100644 packages/runtime-config/README.md delete mode 100644 packages/runtime-config/src/lib.rs delete mode 100644 packages/server/Cargo.toml delete mode 100644 packages/server/src/document.rs delete mode 100644 packages/server/src/lib.rs delete mode 100644 packages/web/src/event/drag.rs delete mode 100644 packages/web/src/event/ext.rs delete mode 100644 packages/web/src/event/file.rs delete mode 100644 packages/web/src/event/form.rs delete mode 100644 packages/web/src/event/image.rs delete mode 100644 packages/web/src/event/resize.rs delete mode 100644 packages/web/src/event/synthetic.rs delete mode 100644 packages/web/src/web_sys_bind/file_engine.rs diff --git a/.vscode/settings.json b/.vscode/settings.json index 3b4ab3b48c..a1b7ed88b7 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,5 @@ { - // Disable toml formatting since everyone uses it differently + "editor.formatOnSave": true, "[toml]": { "editor.formatOnSave": false }, diff --git a/Cargo.lock b/Cargo.lock index ba8e9eb5e1..22c4eb7859 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,16 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +dependencies = [ + "lazy_static", + "regex", +] + [[package]] name = "addr2line" version = "0.24.1" @@ -23,6 +33,12 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + [[package]] name = "aead" version = "0.5.2" @@ -79,6 +95,7 @@ dependencies = [ "const-random", "getrandom 0.2.15", "once_cell", + "serde", "version_check", "zerocopy", ] @@ -101,6 +118,12 @@ dependencies = [ "as-slice", ] +[[package]] +name = "aligned-vec" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" + [[package]] name = "alloc-no-stdlib" version = "2.0.4" @@ -233,12 +256,29 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d67af77d68a931ecd5cbd8a3b5987d63a1d1d1278f7f6a60ae33db485cdebb69" +[[package]] +name = "arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" + [[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.77", +] + [[package]] name = "arrayvec" version = "0.7.6" @@ -277,6 +317,18 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341" +[[package]] +name = "ast_node" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9184f2b369b3e8625712493c89b785881f27eedc6cde480a81883cef78868b2" +dependencies = [ + "proc-macro2", + "quote", + "swc_macros_common", + "syn 2.0.77", +] + [[package]] name = "async-broadcast" version = "0.7.1" @@ -559,12 +611,46 @@ dependencies = [ "terminal-prompt", ] +[[package]] +name = "auto_impl" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + [[package]] name = "autocfg" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +[[package]] +name = "av1-grain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6678909d8c5d46a42abcf571271e15fdbc0a225e3646cf23762cd415046c78bf" +dependencies = [ + "anyhow", + "arrayvec", + "log", + "nom", + "num-rational", + "v_frame", +] + +[[package]] +name = "avif-serialize" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876c75a42f6364451a033496a14c44bffe41f5f4a8236f697391f11024e596d2" +dependencies = [ + "arrayvec", +] + [[package]] name = "aws-lc-rs" version = "1.9.0" @@ -653,7 +739,7 @@ dependencies = [ "sha1", "sync_wrapper 1.0.1", "tokio", - "tokio-tungstenite", + "tokio-tungstenite 0.21.0", "tower", "tower-layer", "tower-service", @@ -708,7 +794,7 @@ dependencies = [ "axum-core 0.4.3", "bytes", "futures-util", - "headers", + "headers 0.4.0", "http 1.1.0", "http-body 1.0.1", "http-body-util", @@ -828,12 +914,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "base16ct" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" - [[package]] name = "base64" version = "0.21.7" @@ -846,6 +926,15 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64-simd" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "781dd20c3aff0bd194fe7d2a977dd92f21c173891f3a03b677359e5fa457e5d5" +dependencies = [ + "simd-abstraction", +] + [[package]] name = "base64ct" version = "1.6.0" @@ -853,13 +942,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] -name = "bcder" -version = "0.7.4" +name = "better_scoped_tls" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c627747a6774aab38beb35990d88309481378558875a41da1a4b2e373c906ef0" +checksum = "794edcc9b3fb07bb4aecaa11f093fd45663b4feadb782d68303a2268bc2701de" dependencies = [ - "bytes", - "smallvec", + "scoped-tls", ] [[package]] @@ -917,12 +1005,6 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" -[[package]] -name = "bitfield" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d7e60934ceec538daadb9d8432424ed043a904d8e0243f3c6446bce549a46ac" - [[package]] name = "bitflags" version = "1.3.2" @@ -939,16 +1021,10 @@ dependencies = [ ] [[package]] -name = "bitness" -version = "0.4.0" +name = "bitstream-io" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57792b99d555ebf109c83169228076f7d997e2b37ba1a653850ccd703ac7bab0" -dependencies = [ - "sysctl", - "thiserror", - "uname", - "winapi", -] +checksum = "b81e1519b0d82120d2fd469d5bfb2919a9361c48b02d82d04befc1cdd2002452" [[package]] name = "bitvec" @@ -977,24 +1053,6 @@ dependencies = [ "generic-array 0.14.7", ] -[[package]] -name = "block-padding" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" -dependencies = [ - "generic-array 0.14.7", -] - -[[package]] -name = "block2" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" -dependencies = [ - "objc2", -] - [[package]] name = "blocking" version = "1.6.1" @@ -1008,16 +1066,6 @@ dependencies = [ "piper", ] -[[package]] -name = "blowfish" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7" -dependencies = [ - "byteorder", - "cipher", -] - [[package]] name = "borsh" version = "1.5.1" @@ -1064,23 +1112,32 @@ dependencies = [ ] [[package]] -name = "bstr" -version = "1.10.0" +name = "browserslist-rs" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" +checksum = "fdf0ca73de70c3da94e4194e4a01fe732378f55d47cf4c0588caab22a0dbfa14" dependencies = [ - "memchr", - "regex-automata 0.4.7", + "ahash 0.8.11", + "chrono", + "either", + "indexmap 2.5.0", + "itertools 0.13.0", + "nom", + "once_cell", "serde", + "serde_json", + "thiserror", ] [[package]] -name = "buffer-redux" -version = "1.0.2" +name = "bstr" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e8acf87c5b9f5897cd3ebb9a327f420e0cae9dd4e5c1d2e36f2c84c571a58f1" +checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" dependencies = [ "memchr", + "regex-automata 0.4.7", + "serde", ] [[package]] @@ -1097,6 +1154,9 @@ name = "bumpalo" version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +dependencies = [ + "allocator-api2", +] [[package]] name = "bytecheck" @@ -1132,6 +1192,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + [[package]] name = "bytes" version = "1.7.1" @@ -1184,16 +1250,6 @@ dependencies = [ "system-deps", ] -[[package]] -name = "camellia" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3264e2574e9ef2b53ce6f536dea83a69ac0bc600b762d1523ff83fe07230ce30" -dependencies = [ - "byteorder", - "cipher", -] - [[package]] name = "camino" version = "1.1.9" @@ -1203,18 +1259,6 @@ dependencies = [ "serde", ] -[[package]] -name = "cargo-config2" -version = "0.1.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1124054becb9262cc15c5e96e82f0d782f2aed3a3034d1f71a6385a6fa9e9595" -dependencies = [ - "home", - "serde", - "serde_derive", - "toml_edit 0.22.21", -] - [[package]] name = "cargo-generate" version = "0.21.3" @@ -1248,12 +1292,12 @@ dependencies = [ "remove_dir_all", "rhai", "sanitize-filename", - "semver", + "semver 1.0.23", "serde", "tempfile", "thiserror", "time", - "toml", + "toml 0.8.19", "walkdir", ] @@ -1274,7 +1318,7 @@ checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" dependencies = [ "camino", "cargo-platform", - "semver", + "semver 1.0.23", "serde", "serde_json", "thiserror", @@ -1287,7 +1331,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad639525b1c67b6a298f378417b060fbc04618bea559482a8484381cce27d965" dependencies = [ "serde", - "toml", + "toml 0.8.19", ] [[package]] @@ -1302,15 +1346,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" -[[package]] -name = "cast5" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b07d673db1ccf000e90f54b819db9e75a8348d6eb056e9b8ab53231b7a9911" -dependencies = [ - "cipher", -] - [[package]] name = "castaway" version = "0.2.3" @@ -1357,15 +1392,6 @@ dependencies = [ "uuid", ] -[[package]] -name = "cfb-mode" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "738b8d467867f80a71351933f70461f5b56f24d5c93e0cf216e59229c968d330" -dependencies = [ - "cipher", -] - [[package]] name = "cfg-expr" version = "0.15.8" @@ -1516,6 +1542,22 @@ dependencies = [ "cc", ] +[[package]] +name = "cocoa" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" +dependencies = [ + "bitflags 1.3.2", + "block", + "cocoa-foundation 0.1.2", + "core-foundation 0.9.4", + "core-graphics 0.23.2", + "foreign-types 0.5.0", + "libc", + "objc", +] + [[package]] name = "cocoa" version = "0.26.0" @@ -1524,14 +1566,28 @@ checksum = "f79398230a6e2c08f5c9760610eb6924b52aa9e7950a619602baba59dcbbdbb2" dependencies = [ "bitflags 2.6.0", "block", - "cocoa-foundation", + "cocoa-foundation 0.2.0", "core-foundation 0.10.0", - "core-graphics", + "core-graphics 0.24.0", "foreign-types 0.5.0", "libc", "objc", ] +[[package]] +name = "cocoa-foundation" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" +dependencies = [ + "bitflags 1.3.2", + "block", + "core-foundation 0.9.4", + "core-graphics-types 0.1.3", + "libc", + "objc", +] + [[package]] name = "cocoa-foundation" version = "0.2.0" @@ -1541,7 +1597,7 @@ dependencies = [ "bitflags 2.6.0", "block", "core-foundation 0.10.0", - "core-graphics-types", + "core-graphics-types 0.2.0", "libc", "objc", ] @@ -1568,6 +1624,17 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +[[package]] +name = "colored" +version = "1.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f741c91823341bebf717d4c71bda820630ce065443b58bd1b7451af008355" +dependencies = [ + "is-terminal", + "lazy_static", + "winapi", +] + [[package]] name = "colored" version = "2.1.0" @@ -1697,6 +1764,26 @@ dependencies = [ "tiny-keccak", ] +[[package]] +name = "const-str" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21077772762a1002bb421c3af42ac1725fa56066bfc53d9a55bb79905df2aaf3" +dependencies = [ + "const-str-proc-macro", +] + +[[package]] +name = "const-str-proc-macro" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e1e0fdd2e5d3041e530e1b21158aeeef8b5d0e306bc5c1e3d6cf0930d10e25a" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "const_format" version = "0.2.33" @@ -1779,6 +1866,19 @@ 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 0.1.3", + "foreign-types 0.5.0", + "libc", +] + [[package]] name = "core-graphics" version = "0.24.0" @@ -1787,11 +1887,22 @@ checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" dependencies = [ "bitflags 2.6.0", "core-foundation 0.10.0", - "core-graphics-types", + "core-graphics-types 0.2.0", "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 = "core-graphics-types" version = "0.2.0" @@ -1804,10 +1915,13 @@ dependencies = [ ] [[package]] -name = "cpio" -version = "0.2.2" +name = "core2" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27e77cfc4543efb4837662cb7cd53464ae66f0fd5c708d71e0f338b1c11d62d3" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] [[package]] name = "cpufeatures" @@ -1833,12 +1947,6 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" -[[package]] -name = "crc24" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd121741cf3eb82c08dd3023eb55bf2665e5f60ec20f89760cf836ae4562e6a0" - [[package]] name = "crc32fast" version = "1.4.2" @@ -1961,18 +2069,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" -[[package]] -name = "crypto-bigint" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" -dependencies = [ - "generic-array 0.14.7", - "rand_core 0.6.4", - "subtle", - "zeroize", -] - [[package]] name = "crypto-common" version = "0.1.6" @@ -2001,6 +2097,28 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "cssparser" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9be934d936a0fbed5bcdc01042b770de1398bf79d0e192f49fa7faea0e99281e" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa 1.0.11", + "phf 0.11.2", + "smallvec", +] + +[[package]] +name = "cssparser-color" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556c099a61d85989d7af52b692e35a8d68a57e7df8c6d07563dc0778b3960c9f" +dependencies = [ + "cssparser 0.33.0", +] + [[package]] name = "cssparser-macros" version = "0.6.1" @@ -2040,33 +2158,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "curve25519-dalek" -version = "4.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" -dependencies = [ - "cfg-if", - "cpufeatures", - "curve25519-dalek-derive", - "digest", - "fiat-crypto", - "rustc_version", - "subtle", - "zeroize", -] - -[[package]] -name = "curve25519-dalek-derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] - [[package]] name = "cvt" version = "0.1.2" @@ -2155,6 +2246,12 @@ dependencies = [ "syn 2.0.77", ] +[[package]] +name = "dary_heap" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7762d17f1241643615821a8455a0b2c3e803784b058693d990b11f2dce25a0ca" + [[package]] name = "dashmap" version = "5.5.3" @@ -2175,55 +2272,43 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" [[package]] -name = "der" -version = "0.7.9" +name = "data-url" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +checksum = "3a30bfce702bcfa94e906ef82421f2c0e61c076ad76030c16ee5d2e9a32fe193" dependencies = [ - "const-oid", - "pem-rfc7468", - "zeroize", + "matches", ] [[package]] -name = "deranged" -version = "0.3.11" +name = "debugid" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" dependencies = [ - "powerfmt", "serde", + "uuid", ] [[package]] -name = "derive_builder" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d67778784b508018359cbc8696edb3db78160bab2c2a28ba7f56ef6932997f8" -dependencies = [ - "derive_builder_macro", -] - -[[package]] -name = "derive_builder_core" -version = "0.12.0" +name = "der" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c11bdc11a0c47bc7d37d582b5285da6849c96681023680b906673c5707af7b0f" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ - "darling 0.14.4", - "proc-macro2", - "quote", - "syn 1.0.109", + "const-oid", + "pem-rfc7468", + "zeroize", ] [[package]] -name = "derive_builder_macro" -version = "0.12.0" +name = "deranged" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebcda35c7a396850a55ffeac740804b40ffec779b98fffbb1738f4033f0ee79e" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ - "derive_builder_core", - "syn 1.0.109", + "powerfmt", + "serde", ] [[package]] @@ -2235,19 +2320,10 @@ dependencies = [ "convert_case 0.4.0", "proc-macro2", "quote", - "rustc_version", + "rustc_version 0.4.1", "syn 2.0.77", ] -[[package]] -name = "des" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffdd80ce8ce993de27e9f063a444a4d53ce8e8db4c1f00cc03af5ad5a9867a1e" -dependencies = [ - "cipher", -] - [[package]] name = "dialoguer" version = "0.11.0" @@ -2289,13 +2365,11 @@ dependencies = [ "dioxus-config-macro", "dioxus-core", "dioxus-core-macro", - "dioxus-core-types", "dioxus-desktop", "dioxus-devtools", "dioxus-fullstack", "dioxus-hooks", "dioxus-html", - "dioxus-isrg", "dioxus-liveview", "dioxus-mobile", "dioxus-router", @@ -2303,7 +2377,7 @@ dependencies = [ "dioxus-ssr", "dioxus-static-site-generation", "dioxus-web", - "env_logger 0.11.5", + "env_logger 0.10.2", "futures-util", "manganis", "rand 0.8.5", @@ -2351,18 +2425,19 @@ dependencies = [ "axum-server", "brotli", "built", - "cargo-config2", "cargo-generate", "cargo_metadata", "cargo_toml", "chrono", "clap", + "colored 2.1.0", "console", "console-subscriber", "crossterm", "ctrlc", "dioxus-autofmt", "dioxus-check", + "dioxus-cli-config", "dioxus-core", "dioxus-core-types", "dioxus-devtools", @@ -2370,23 +2445,23 @@ dependencies = [ "dioxus-rsx", "dioxus-rsx-hotreload", "dioxus-rsx-rosetta", - "dioxus-runtime-config", "dirs", "env_logger 0.11.5", + "fern", "flate2", "fs_extra", "futures-channel", "futures-util", - "headers", + "headers 0.3.9", "html_parser", "hyper 1.4.1", "hyper-rustls", "hyper-util", "ignore", "krates", - "manganis-core", + "log", + "manganis-cli-support", "notify", - "object", "once_cell", "open", "prettyplease", @@ -2407,14 +2482,12 @@ dependencies = [ "thiserror", "tokio", "tokio-stream", - "tokio-util", - "toml", - "toml_edit 0.22.21", + "toml 0.8.19", + "toml_edit 0.22.20", "tower", "tower-http", "tracing", "tracing-subscriber", - "uuid", "walkdir", "wasm-bindgen-cli-support", "wasm-bindgen-shared", @@ -2440,13 +2513,13 @@ version = "0.6.0-alpha.2" dependencies = [ "const_format", "dioxus", - "dioxus-core-types", "dioxus-html", "dioxus-ssr", "futures-channel", "futures-util", "generational-box", "longest-increasing-subsequence", + "manganis", "pretty_assertions", "rand 0.8.5", "reqwest", @@ -2488,15 +2561,15 @@ name = "dioxus-desktop" version = "0.6.0-alpha.2" dependencies = [ "async-trait", - "cocoa", - "core-foundation 0.10.0", + "cocoa 0.25.0", + "core-foundation 0.9.4", "dioxus", + "dioxus-cli-config", "dioxus-core", "dioxus-devtools", "dioxus-hooks", "dioxus-html", "dioxus-interpreter-js", - "dioxus-runtime-config", "dioxus-signals", "dioxus-ssr", "dunce", @@ -2523,6 +2596,7 @@ dependencies = [ "tao", "thiserror", "tokio", + "tokio-tungstenite 0.23.1", "tracing", "urlencoding", "webbrowser", @@ -2557,7 +2631,7 @@ name = "dioxus-examples" version = "0.6.0-alpha.2" dependencies = [ "async-std", - "base64 0.22.1", + "base64 0.21.7", "ciborium", "dioxus", "dioxus-ssr", @@ -2565,12 +2639,14 @@ dependencies = [ "futures-util", "getrandom 0.2.15", "http-range", + "manganis", "rand 0.8.5", "reqwest", "separator", "serde", "serde_json", "tokio", + "tracing-subscriber", "web-time", ] @@ -2596,14 +2672,13 @@ dependencies = [ "bytes", "ciborium", "dioxus", - "dioxus-core-types", + "dioxus-cli-config", "dioxus-desktop", "dioxus-devtools", "dioxus-interpreter-js", "dioxus-isrg", "dioxus-lib", "dioxus-mobile", - "dioxus-runtime-config", "dioxus-ssr", "dioxus-web", "dioxus_server_macro", @@ -2657,17 +2732,21 @@ dependencies = [ "async-trait", "dioxus", "dioxus-core", + "dioxus-core-macro", "dioxus-core-types", + "dioxus-hooks", "dioxus-html-internal-macro", "dioxus-rsx", - "dioxus-rsx-hotreload", "dioxus-web", "enumset", "euclid", "futures-channel", + "generational-box", + "js-sys", "keyboard-types", "lazy-js-bundle", "manganis", + "rustversion", "serde", "serde_json", "serde_repr", @@ -2692,6 +2771,7 @@ version = "0.6.0-alpha.2" dependencies = [ "dioxus-core", "dioxus-core-types", + "dioxus-html", "js-sys", "lazy-js-bundle", "rustc-hash 1.1.0", @@ -2709,7 +2789,7 @@ version = "0.6.0-alpha.2" dependencies = [ "chrono", "http 1.1.0", - "lru", + "lru 0.12.4", "rustc-hash 1.1.0", "thiserror", "tracing", @@ -2727,7 +2807,6 @@ dependencies = [ "dioxus-html", "dioxus-rsx", "dioxus-signals", - "manganis", ] [[package]] @@ -2736,16 +2815,15 @@ version = "0.6.0-alpha.2" dependencies = [ "axum 0.7.5", "dioxus", + "dioxus-cli-config", "dioxus-core", "dioxus-devtools", "dioxus-html", "dioxus-interpreter-js", - "dioxus-runtime-config", - "env_logger 0.11.5", "futures-channel", "futures-util", "generational-box", - "lazy-js-bundle", + "pretty_env_logger", "rustc-hash 1.1.0", "serde", "serde_json", @@ -2833,10 +2911,11 @@ dependencies = [ "console_error_panic_hook", "criterion", "dioxus", - "dioxus-document", + "dioxus-cli-config", "dioxus-fullstack", "dioxus-lib", "dioxus-liveview", + "dioxus-router", "dioxus-router-macro", "dioxus-ssr", "gloo", @@ -2908,14 +2987,6 @@ dependencies = [ "syn 2.0.77", ] -[[package]] -name = "dioxus-runtime-config" -version = "0.6.0-alpha.2" - -[[package]] -name = "dioxus-server" -version = "0.6.0-alpha.2" - [[package]] name = "dioxus-signals" version = "0.6.0-alpha.2" @@ -2998,12 +3069,13 @@ dependencies = [ "futures-channel", "futures-util", "generational-box", - "gloo-timers 0.3.0", + "gloo-dialogs", + "gloo-timers 0.2.6", "js-sys", "lazy-js-bundle", "rustc-hash 1.1.0", "serde", - "serde-wasm-bindgen 0.6.5", + "serde-wasm-bindgen", "serde_json", "tracing", "tracing-wasm", @@ -3118,22 +3190,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" -[[package]] -name = "dsa" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48bc224a9084ad760195584ce5abb3c2c34a225fa312a128ad245a6b412b7689" -dependencies = [ - "digest", - "num-bigint-dig", - "num-traits", - "pkcs8", - "rfc6979", - "sha2", - "signature", - "zeroize", -] - [[package]] name = "dtoa" version = "1.0.9" @@ -3155,20 +3211,6 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" -[[package]] -name = "ecdsa" -version = "0.16.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" -dependencies = [ - "der", - "digest", - "elliptic-curve", - "rfc6979", - "signature", - "spki", -] - [[package]] name = "ecommerce-site" version = "0.1.1" @@ -3179,30 +3221,6 @@ dependencies = [ "serde", ] -[[package]] -name = "ed25519" -version = "2.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" -dependencies = [ - "pkcs8", - "signature", -] - -[[package]] -name = "ed25519-dalek" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" -dependencies = [ - "curve25519-dalek", - "ed25519", - "serde", - "sha2", - "subtle", - "zeroize", -] - [[package]] name = "either" version = "1.13.0" @@ -3212,27 +3230,6 @@ dependencies = [ "serde", ] -[[package]] -name = "elliptic-curve" -version = "0.13.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" -dependencies = [ - "base16ct", - "crypto-bigint", - "digest", - "ff", - "generic-array 0.14.7", - "group", - "hkdf", - "pem-rfc7468", - "pkcs8", - "rand_core 0.6.4", - "sec1", - "subtle", - "zeroize", -] - [[package]] name = "encode_unicode" version = "0.3.6" @@ -3254,28 +3251,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" -[[package]] -name = "enum-display-derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f16ef37b2a9b242295d61a154ee91ae884afff6b8b933b486b12481cc58310ca" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "enum-primitive-derive" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba7795da175654fe16979af73f81f26a8ea27638d8d9823d317016888a63dc4c" -dependencies = [ - "num-traits", - "quote", - "syn 2.0.77", -] - [[package]] name = "enumflags2" version = "0.7.10" @@ -3360,16 +3335,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" -[[package]] -name = "erased-serde" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24e2389d65ab4fab27dc2a5de7b191e1f6617d1f1c8855c0dc569c94a4cbb18d" -dependencies = [ - "serde", - "typeid", -] - [[package]] name = "errno" version = "0.3.9" @@ -3515,21 +3480,15 @@ dependencies = [ ] [[package]] -name = "ff" -version = "0.13.0" +name = "fern" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +checksum = "d9f0c14694cbd524c8720dd69b0e3179344f04ebb5f90f2e4a440c6ea3b2f1ee" dependencies = [ - "rand_core 0.6.4", - "subtle", + "colored 1.9.4", + "log", ] -[[package]] -name = "fiat-crypto" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" - [[package]] name = "field-offset" version = "0.3.6" @@ -3537,7 +3496,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" dependencies = [ "memoffset", - "rustc_version", + "rustc_version 0.4.1", ] [[package]] @@ -3578,15 +3537,6 @@ dependencies = [ "miniz_oxide 0.8.0", ] -[[package]] -name = "fluent-uri" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17c704e9dbe1ddd863da1e6ff3567795087b1eb201ce80d8fa81162e1516500d" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "flume" version = "0.11.0" @@ -3665,6 +3615,17 @@ dependencies = [ "thiserror", ] +[[package]] +name = "from_variant" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32016f1242eb82af5474752d00fd8ebcd9004bd69b462b1c91de833972d08ed4" +dependencies = [ + "proc-macro2", + "swc_macros_common", + "syn 2.0.77", +] + [[package]] name = "fs-err" version = "2.11.0" @@ -4039,7 +4000,6 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", - "zeroize", ] [[package]] @@ -4447,17 +4407,18 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "global-hotkey" -version = "0.6.2" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1b75248f33c73df1ed69673f6cb36d2e048ae84d29aa1d3e53199d138ebb1df" +checksum = "b436093d1598b05e3b7fddc097b2bad32763f53a1beb25ab6f9718c6a60acd09" dependencies = [ + "bitflags 2.6.0", + "cocoa 0.25.0", "crossbeam-channel", "keyboard-types", - "objc2", - "objc2-app-kit", + "objc", "once_cell", "thiserror", - "windows-sys 0.59.0", + "windows-sys 0.52.0", "x11-dl", ] @@ -4547,7 +4508,7 @@ dependencies = [ "gloo-events", "gloo-utils 0.1.7", "serde", - "serde-wasm-bindgen 0.5.0", + "serde-wasm-bindgen", "serde_urlencoded", "thiserror", "wasm-bindgen", @@ -4697,17 +4658,6 @@ dependencies = [ "system-deps", ] -[[package]] -name = "group" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" -dependencies = [ - "ff", - "rand_core 0.6.4", - "subtle", -] - [[package]] name = "gtk" version = "0.18.1" @@ -4810,9 +4760,9 @@ dependencies = [ [[package]] name = "handlebars" -version = "5.1.2" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d08485b96a0e6393e9e4d1b8d48cf74ad6c063cd905eb33f42c1ce3f0377539b" +checksum = "faa67bab9ff362228eb3d00bd024a4965d8231bbb7921167f0cfa66c6626b225" dependencies = [ "log", "pest", @@ -4836,6 +4786,9 @@ name = "hashbrown" version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash 0.8.11", +] [[package]] name = "hashbrown" @@ -4872,24 +4825,48 @@ dependencies = [ [[package]] name = "headers" -version = "0.4.0" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322106e6bd0cba2d5ead589ddb8150a13d7c4217cf80d7c4f682ca994ccc6aa9" +checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270" dependencies = [ "base64 0.21.7", "bytes", - "headers-core", - "http 1.1.0", + "headers-core 0.2.0", + "http 0.2.12", "httpdate", "mime", "sha1", ] [[package]] -name = "headers-core" -version = "0.3.0" +name = "headers" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" +checksum = "322106e6bd0cba2d5ead589ddb8150a13d7c4217cf80d7c4f682ca994ccc6aa9" +dependencies = [ + "base64 0.21.7", + "bytes", + "headers-core 0.3.0", + "http 1.1.0", + "httpdate", + "mime", + "sha1", +] + +[[package]] +name = "headers-core" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" +dependencies = [ + "http 0.2.12", +] + +[[package]] +name = "headers-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" dependencies = [ "http 1.1.0", ] @@ -4972,6 +4949,20 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "hstr" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dae404c0c5d4e95d4858876ab02eecd6a196bb8caa42050dfa809938833fc412" +dependencies = [ + "hashbrown 0.14.5", + "new_debug_unreachable", + "once_cell", + "phf 0.11.2", + "rustc-hash 1.1.0", + "triomphe", +] + [[package]] name = "html5ever" version = "0.26.0" @@ -5229,15 +5220,6 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" -[[package]] -name = "idea" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "075557004419d7f2031b8bb7f44bb43e55a83ca7b63076a8fb8fe75753836477" -dependencies = [ - "cipher", -] - [[package]] name = "ident_case" version = "1.0.1" @@ -5254,6 +5236,12 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "if_chain" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" + [[package]] name = "ignore" version = "0.4.23" @@ -5288,6 +5276,58 @@ dependencies = [ "tiff", ] +[[package]] +name = "image" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99314c8a2152b8ddb211f924cdae532d8c5e4c8bb54728e12fff1b0cd5963a10" +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.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f79afb8cbee2ef20f59ccd477a218c12a93943d075b492015ecb1bb81f8ee904" +dependencies = [ + "byteorder-lite", + "quick-error", +] + +[[package]] +name = "imagequant" +version = "4.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fecc99538c9061ee4d88476f6cd704c9f06575a34f0083affdaa1337a331aa7" +dependencies = [ + "arrayvec", + "once_cell", + "rayon", + "rgb", + "thread_local", +] + +[[package]] +name = "imgref" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44feda355f4159a7c757171a77de25daf6411e217b4cabd03bd6650690468126" + [[package]] name = "indexmap" version = "1.9.3" @@ -5331,9 +5371,18 @@ checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" [[package]] name = "infer" -version = "0.16.0" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6c16b11a665b26aeeb9b1d7f954cdeb034be38dd00adab4f2ae921a8fee804" +dependencies = [ + "cfb", +] + +[[package]] +name = "infer" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc150e5ce2330295b8616ce0e3f53250e53af31759a9dbedad1621ba29151847" +checksum = "f551f8c3a39f68f986517db0d1759de85881894fdc7db798bd2a9df9cb04b7fc" dependencies = [ "cfb", ] @@ -5386,6 +5435,17 @@ dependencies = [ "parking_lot", ] +[[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.77", +] + [[package]] name = "inventory" version = "0.3.15" @@ -5426,6 +5486,18 @@ dependencies = [ "once_cell", ] +[[package]] +name = "is-macro" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2069faacbe981460232f880d26bf3c7634e322d49053aa48c27e3ae642f728f1" +dependencies = [ + "Inflector", + "proc-macro2", + "quote", + "syn 2.0.77", +] + [[package]] name = "is-terminal" version = "0.4.13" @@ -5459,12 +5531,6 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" -[[package]] -name = "iter-read" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071ed4cc1afd86650602c7b11aa2e1ce30762a1c27193201cb5cee9c6ebb1294" - [[package]] name = "itertools" version = "0.8.2" @@ -5587,50 +5653,24 @@ dependencies = [ [[package]] name = "json-patch" -version = "2.0.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b1fb8864823fad91877e6caea0baca82e49e8db50f8e5c9f9a453e27d3330fc" +checksum = "ec9ad60d674508f3ca8f380a928cfe7b096bc729c4e2dbfe3852bc45da3ab30b" dependencies = [ - "jsonptr", "serde", "serde_json", "thiserror", ] [[package]] -name = "jsonptr" -version = "0.4.7" +name = "jsonc-parser" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c6e529149475ca0b2820835d3dce8fcc41c6b943ca608d32f35b449255e4627" +checksum = "7b56a20e76235284255a09fcd1f45cf55d3c524ea657ebd3854735925c57743d" dependencies = [ - "fluent-uri", - "serde", "serde_json", ] -[[package]] -name = "k256" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" -dependencies = [ - "cfg-if", - "ecdsa", - "elliptic-curve", - "once_cell", - "sha2", - "signature", -] - -[[package]] -name = "keccak" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" -dependencies = [ - "cpufeatures", -] - [[package]] name = "keyboard-types" version = "0.7.0" @@ -5671,7 +5711,7 @@ dependencies = [ "camino", "cfg-expr 0.16.0", "petgraph", - "semver", + "semver 1.0.23", "serde", "serde_json", ] @@ -5692,7 +5732,7 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f29e4755b7b995046f510a7520c42b2fed58b77bd94d5a87a8eb43d2fd126da8" dependencies = [ - "cssparser", + "cssparser 0.27.2", "html5ever", "indexmap 1.9.3", "matches", @@ -5745,6 +5785,41 @@ version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +[[package]] +name = "libflate" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45d9dfdc14ea4ef0900c1cddbc8dcd553fbaacd8a4a282cf4018ae9dd04fb21e" +dependencies = [ + "adler32", + "core2", + "crc32fast", + "dary_heap", + "libflate_lz77", +] + +[[package]] +name = "libflate_lz77" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e0d73b369f386f1c44abd9c570d5318f55ccde816ff4b562fa452e5182863d" +dependencies = [ + "core2", + "hashbrown 0.14.5", + "rle-decode-fast", +] + +[[package]] +name = "libfuzzer-sys" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7" +dependencies = [ + "arbitrary", + "cc", + "once_cell", +] + [[package]] name = "libgit2-sys" version = "0.17.0+1.8.1" @@ -5842,6 +5917,50 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "lightningcss" +version = "1.0.0-alpha.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53e225b3fa0a8bd5562c8833b1a32afa88761c4e661d3177b8cdc4e13cbf078e" +dependencies = [ + "ahash 0.8.11", + "bitflags 2.6.0", + "const-str", + "cssparser 0.33.0", + "cssparser-color", + "dashmap", + "data-encoding", + "getrandom 0.2.15", + "itertools 0.10.5", + "lazy_static", + "lightningcss-derive", + "parcel_selectors", + "parcel_sourcemap", + "paste", + "pathdiff", + "rayon", + "serde", + "smallvec", +] + +[[package]] +name = "lightningcss-derive" +version = "1.0.0-alpha.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c12744d1279367caed41739ef094c325d53fb0ffcd4f9b84a368796f870252" +dependencies = [ + "convert_case 0.6.0", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "line-col" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e69cdf6b85b5c8dce514f694089a2cf8b1a702f6cd28607bcb3cf296c9778db" + [[package]] name = "link-cplusplus" version = "1.0.9" @@ -5939,24 +6058,31 @@ 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 = "lru" -version = "0.12.4" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" +checksum = "718e8fae447df0c7e1ba7f5189829e63fd536945c8988d61444c19039f16b670" dependencies = [ - "hashbrown 0.14.5", + "hashbrown 0.13.2", ] [[package]] -name = "lzma-sys" -version = "0.1.20" +name = "lru" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27" +checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" dependencies = [ - "cc", - "libc", - "pkg-config", + "hashbrown 0.14.5", ] [[package]] @@ -5986,20 +6112,18 @@ dependencies = [ [[package]] name = "manganis" -version = "0.6.0-alpha.2" +version = "0.3.0-alpha.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6250942459fe37aecefbf52d5ef5904534d4c3e6383b26835fa3bde98ab5e491" dependencies = [ - "anyhow", - "base64 0.22.1", - "dioxus-core-types", - "dunce", "manganis-macro", - "once_cell", - "serde", ] [[package]] -name = "manganis-core" -version = "0.6.0-alpha.2" +name = "manganis-cli-support" +version = "0.3.0-alpha.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d9251654f07951e80b1fccde6389a37059f18f1a7dd747ecd7771336f1a1ae6" dependencies = [ "anyhow", "image 0.25.2", @@ -6016,18 +6140,90 @@ dependencies = [ "rustc-hash 1.1.0", "serde", "serde_json", + "swc", + "swc_allocator", + "swc_atoms", + "swc_cached", + "swc_common", + "swc_compiler_base 0.19.0", + "swc_config", + "swc_config_macro", + "swc_ecma_ast", + "swc_ecma_codegen", + "swc_ecma_codegen_macros", + "swc_ecma_compat_bugfixes 0.12.0", + "swc_ecma_compat_common", + "swc_ecma_compat_es2015 0.12.0", + "swc_ecma_compat_es2016 0.12.0", + "swc_ecma_compat_es2017 0.12.0", + "swc_ecma_compat_es2018 0.12.0", + "swc_ecma_compat_es2019 0.12.0", + "swc_ecma_compat_es2020 0.12.0", + "swc_ecma_compat_es2021 0.12.0", + "swc_ecma_compat_es2022 0.12.0", + "swc_ecma_compat_es3 0.12.0", + "swc_ecma_ext_transforms", + "swc_ecma_lints 0.100.0", + "swc_ecma_loader", + "swc_ecma_minifier 0.204.0", + "swc_ecma_parser", + "swc_ecma_preset_env 0.217.0", + "swc_ecma_transforms 0.239.0", + "swc_ecma_transforms_base 0.145.0", + "swc_ecma_transforms_classes 0.134.0", + "swc_ecma_transforms_compat 0.171.0", + "swc_ecma_transforms_macros", + "swc_ecma_transforms_module 0.190.0", + "swc_ecma_transforms_optimization 0.208.0", + "swc_ecma_transforms_proposal 0.178.0", + "swc_ecma_transforms_react 0.191.0", + "swc_ecma_transforms_typescript 0.198.1", + "swc_ecma_usage_analyzer", + "swc_ecma_utils", + "swc_ecma_visit", + "swc_eq_ignore_macros", + "swc_error_reporters", + "swc_fast_graph", + "swc_macros_common", + "swc_node_comments", + "swc_timer", + "swc_trace_macro", + "swc_transform_common", + "swc_typescript", + "swc_visit", + "tracing", + "url", +] + +[[package]] +name = "manganis-common" +version = "0.3.0-alpha.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f9048495439cee460d6092341dc54088895f985ca41379169502d8f46af08e" +dependencies = [ + "anyhow", + "base64 0.21.7", + "built", + "home", + "infer 0.11.0", + "serde", + "toml 0.7.8", + "tracing", + "url", ] [[package]] name = "manganis-macro" -version = "0.6.0-alpha.2" +version = "0.3.0-alpha.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc48d9ad7dccb08194ec0613ad22ad28f4d28dd8565397a1d3fb4a6986bbebef" dependencies = [ - "manganis-core", + "manganis-common", "proc-macro2", "quote", - "serde", "serde_json", "syn 2.0.77", + "tracing-subscriber", ] [[package]] @@ -6065,6 +6261,15 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" +[[package]] +name = "maybe-rayon" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" +dependencies = [ + "cfg-if", +] + [[package]] name = "md-5" version = "0.10.6" @@ -6105,6 +6310,31 @@ dependencies = [ "autocfg", ] +[[package]] +name = "miette" +version = "7.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4edc8853320c2a0dab800fbda86253c8938f6ea88510dc92c5f1ed20e794afc1" +dependencies = [ + "cfg-if", + "miette-derive", + "owo-colors", + "textwrap", + "thiserror", + "unicode-width", +] + +[[package]] +name = "miette-derive" +version = "7.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + [[package]] name = "mime" version = "0.3.17" @@ -6187,14 +6417,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9be0862c1b3f26a88803c4a49de6889c10e608b3ee9344e6ef5b45fb37ad3d1" [[package]] -name = "muda" -version = "0.14.1" +name = "mozjpeg" +version = "0.10.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "969e1dbc9af2f18ffe6ddba72bbe86506c7214ecb28670d98ecfba51cb9b8c6b" +dependencies = [ + "arrayvec", + "bytemuck", + "libc", + "mozjpeg-sys", + "rgb", +] + +[[package]] +name = "mozjpeg-sys" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27e31c0171e0b1158c0dfb7386dbdf999f4a9afaa83fd68de39c7929f4d5c16f" +dependencies = [ + "cc", + "dunce", + "libc", + "nasm-rs", +] + +[[package]] +name = "muda" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba8ac4080fb1e097c2c22acae467e46e4da72d941f02e82b67a87a2a89fa38b1" +checksum = "4c47e7625990fc1af2226ea4f34fb2412b03c12639fcb91868581eb3a6893453" dependencies = [ - "cocoa", + "cocoa 0.25.0", "crossbeam-channel", - "dpi", "gtk", "keyboard-types", "libxdo", @@ -6202,7 +6456,7 @@ dependencies = [ "once_cell", "png", "thiserror", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -6231,6 +6485,15 @@ dependencies = [ "rand 0.8.5", ] +[[package]] +name = "nasm-rs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12fcfa1bd49e0342ec1d07ed2be83b59963e7acbeb9310e1bb2c07b69dadd959" +dependencies = [ + "jobserver", +] + [[package]] name = "native-tls" version = "0.2.12" @@ -6248,6 +6511,20 @@ dependencies = [ "tempfile", ] +[[package]] +name = "ndk" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" +dependencies = [ + "bitflags 1.3.2", + "jni-sys", + "ndk-sys 0.4.1+23.1.7779620", + "num_enum 0.5.11", + "raw-window-handle 0.5.2", + "thiserror", +] + [[package]] name = "ndk" version = "0.9.0" @@ -6257,8 +6534,8 @@ dependencies = [ "bitflags 2.6.0", "jni-sys", "log", - "ndk-sys", - "num_enum", + "ndk-sys 0.6.0+11769913", + "num_enum 0.7.3", "raw-window-handle 0.6.2", "thiserror", ] @@ -6269,6 +6546,15 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" +[[package]] +name = "ndk-sys" +version = "0.4.1+23.1.7779620" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cf2aae958bd232cac5069850591667ad422d263686d75b52a065f9badeee5a3" +dependencies = [ + "jni-sys", +] + [[package]] name = "ndk-sys" version = "0.6.0+11769913" @@ -6341,6 +6627,21 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9e591e719385e6ebaeb5ce5d3887f7d5676fceca6411d1925ccc95745f3d6f7" +[[package]] +name = "noop_proc_macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" + +[[package]] +name = "normpath" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a9da8c9922c35a1033d76f7272dfc2e7ee20392083d75aeea6ced23c6266578" +dependencies = [ + "winapi", +] + [[package]] name = "normpath" version = "1.3.0" @@ -6380,20 +6681,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "num" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" -dependencies = [ - "num-bigint", - "num-complex", - "num-integer", - "num-iter", - "num-rational", - "num-traits", -] - [[package]] name = "num-bigint" version = "0.4.6" @@ -6402,6 +6689,7 @@ checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ "num-integer", "num-traits", + "serde", ] [[package]] @@ -6417,20 +6705,10 @@ dependencies = [ "num-iter", "num-traits", "rand 0.8.5", - "serde", "smallvec", "zeroize", ] -[[package]] -name = "num-complex" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" -dependencies = [ - "num-traits", -] - [[package]] name = "num-conv" version = "0.1.0" @@ -6489,13 +6767,44 @@ dependencies = [ "libm", ] +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi 0.3.9", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +dependencies = [ + "num_enum_derive 0.5.11", +] + [[package]] name = "num_enum" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" dependencies = [ - "num_enum_derive", + "num_enum_derive 0.7.3", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] @@ -6546,105 +6855,6 @@ dependencies = [ "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.6.0", - "block2", - "libc", - "objc2", - "objc2-core-data", - "objc2-core-image", - "objc2-foundation", - "objc2-quartz-core", -] - -[[package]] -name = "objc2-core-data" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" -dependencies = [ - "bitflags 2.6.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-encode" -version = "4.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7891e71393cd1f227313c9379a26a584ff3d7e6e7159e988851f0934c993f0f8" - -[[package]] -name = "objc2-foundation" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" -dependencies = [ - "bitflags 2.6.0", - "block2", - "libc", - "objc2", -] - -[[package]] -name = "objc2-metal" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" -dependencies = [ - "bitflags 2.6.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.6.0", - "block2", - "objc2", - "objc2-foundation", - "objc2-metal", -] - [[package]] name = "objc_exception" version = "0.1.2" @@ -6675,16 +6885,6 @@ dependencies = [ "wasmparser 0.216.0", ] -[[package]] -name = "once-cell-regex" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3de7e389a5043420c8f2b95ed03f3f104ad6f4c41f7d7e27298f033abc253e8" -dependencies = [ - "once_cell", - "regex", -] - [[package]] name = "once_cell" version = "1.19.0" @@ -6784,6 +6984,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "outref" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f222829ae9293e33a9f5e9f440c6760a3d450a64affe1846486b140db81c1f4" + [[package]] name = "overload" version = "0.1.1" @@ -6800,30 +7006,6 @@ dependencies = [ "supports-color 3.0.1", ] -[[package]] -name = "p256" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" -dependencies = [ - "ecdsa", - "elliptic-curve", - "primeorder", - "sha2", -] - -[[package]] -name = "p384" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" -dependencies = [ - "ecdsa", - "elliptic-curve", - "primeorder", - "sha2", -] - [[package]] name = "pango" version = "0.18.3" @@ -6849,6 +7031,36 @@ dependencies = [ "system-deps", ] +[[package]] +name = "parcel_selectors" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f4d26c18a8377a64728c04bf3b2e48ec43b0c77e687a18e03eb837d65e08a14" +dependencies = [ + "bitflags 2.6.0", + "cssparser 0.33.0", + "fxhash", + "log", + "phf 0.10.1", + "phf_codegen 0.10.0", + "precomputed-hash", + "smallvec", +] + +[[package]] +name = "parcel_sourcemap" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "485b74d7218068b2b7c0e3ff12fbc61ae11d57cb5d8224f525bd304c6be05bbb" +dependencies = [ + "base64-simd", + "data-url", + "rkyv", + "serde", + "serde_json", + "vlq", +] + [[package]] name = "parking" version = "2.2.1" @@ -6904,6 +7116,18 @@ dependencies = [ "path-dedot", ] +[[package]] +name = "path-clean" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecba01bf2678719532c5e3059e0b5f0811273d94b397088b82e3bd0a78c78fdd" + +[[package]] +name = "path-clean" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17359afc20d7ab31fdb42bb844c8b3bb1dabd7dcf7e68428492da7f16966fcef" + [[package]] name = "path-dedot" version = "3.1.1" @@ -6931,16 +7155,6 @@ dependencies = [ "sha2", ] -[[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 = "pem-rfc7468" version = "0.7.0" @@ -7011,62 +7225,6 @@ dependencies = [ "indexmap 2.5.0", ] -[[package]] -name = "pgp" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "031fa1e28c4cb54c90502ef0642a44ef10ec8349349ebe6372089f1b1ef4f297" -dependencies = [ - "aes", - "base64 0.21.7", - "bitfield", - "block-padding", - "blowfish", - "bstr", - "buffer-redux", - "byteorder", - "camellia", - "cast5", - "cfb-mode", - "chrono", - "cipher", - "const-oid", - "crc24", - "curve25519-dalek", - "derive_builder", - "des", - "digest", - "dsa", - "ed25519-dalek", - "elliptic-curve", - "flate2", - "generic-array 0.14.7", - "hex", - "idea", - "iter-read", - "k256", - "log", - "md-5", - "nom", - "num-bigint-dig", - "num-traits", - "num_enum", - "p256", - "p384", - "rand 0.8.5", - "ripemd", - "rsa", - "sha1", - "sha2", - "sha3", - "signature", - "smallvec", - "thiserror", - "twofish", - "x25519-dalek", - "zeroize", -] - [[package]] name = "phf" version = "0.8.0" @@ -7386,8 +7544,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] -name = "prettier-please" -version = "0.3.0" +name = "preset_env_base" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b30eab18be480c194938e433e269d5298a279f6410f02fbc73f3576a325c110" +dependencies = [ + "ahash 0.8.11", + "anyhow", + "browserslist-rs", + "dashmap", + "from_variant", + "once_cell", + "semver 1.0.23", + "serde", + "st-map", + "tracing", +] + +[[package]] +name = "prettier-please" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32db37eb2b0ec0af154e9c1b33425902d8cd9481e35167c4e9ffb28fec3916bb" dependencies = [ @@ -7425,15 +7601,6 @@ dependencies = [ "syn 2.0.77", ] -[[package]] -name = "primeorder" -version = "0.13.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" -dependencies = [ - "elliptic-curve", -] - [[package]] name = "proc-macro-crate" version = "1.3.1" @@ -7459,7 +7626,7 @@ version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" dependencies = [ - "toml_edit 0.22.21", + "toml_edit 0.22.20", ] [[package]] @@ -7519,6 +7686,25 @@ version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "744a264d26b88a6a7e37cbad97953fa233b94d585236310bcbc88474b4092d79" +[[package]] +name = "profiling" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d84d1d7a6ac92673717f9f6d1518374ef257669c24ebc5ac25d5033828be58" +dependencies = [ + "profiling-procmacros", +] + +[[package]] +name = "profiling-procmacros" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8021cf59c8ec9c432cfc2526ac6b8aa508ecaf29cd415f271b8406c1b851c3fd" +dependencies = [ + "quote", + "syn 2.0.77", +] + [[package]] name = "prost" version = "0.12.6" @@ -7551,6 +7737,15 @@ dependencies = [ "prost", ] +[[package]] +name = "psm" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa37f80ca58604976033fae9515a8a2989fc13797d953f7c04fb8fa36a11f205" +dependencies = [ + "cc", +] + [[package]] name = "ptr_meta" version = "0.1.4" @@ -7580,6 +7775,12 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + [[package]] name = "quick-xml" version = "0.32.0" @@ -7652,6 +7853,28 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" +[[package]] +name = "radix_fmt" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce082a9940a7ace2ad4a8b7d0b1eac6aa378895f18be598230c5f2284ac05426" + +[[package]] +name = "railwind" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bc2b6ca634162c78a6e8d399e12e22b42bcbf2eb60f2b7a2f99aa2379d41800" +dependencies = [ + "hex", + "indexmap 1.9.3", + "lazy_static", + "line-col", + "regex", + "ron", + "serde", + "serde_regex", +] + [[package]] name = "rand" version = "0.7.3" @@ -7744,7 +7967,7 @@ dependencies = [ "compact_str", "crossterm", "itertools 0.13.0", - "lru", + "lru 0.12.4", "paste", "stability", "strum 0.26.3", @@ -7754,6 +7977,55 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "rav1e" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9" +dependencies = [ + "arbitrary", + "arg_enum_proc_macro", + "arrayvec", + "av1-grain", + "bitstream-io", + "built", + "cfg-if", + "interpolate_name", + "itertools 0.12.1", + "libc", + "libfuzzer-sys", + "log", + "maybe-rayon", + "new_debug_unreachable", + "noop_proc_macro", + "num-derive", + "num-traits", + "once_cell", + "paste", + "profiling", + "rand 0.8.5", + "rand_chacha 0.3.1", + "simd_helpers", + "system-deps", + "thiserror", + "v_frame", + "wasm-bindgen", +] + +[[package]] +name = "ravif" +version = "0.11.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f0bfd976333248de2078d350bfdf182ff96e168a24d23d2436cef320dd4bdd" +dependencies = [ + "avif-serialize", + "imgref", + "loop9", + "quick-error", + "rav1e", + "rgb", +] + [[package]] name = "raw-window-handle" version = "0.5.2" @@ -7860,7 +8132,7 @@ dependencies = [ "cvt", "fs_at", "libc", - "normpath", + "normpath 1.3.0", "windows-sys 0.52.0", ] @@ -7922,17 +8194,7 @@ dependencies = [ "wasm-streams", "web-sys", "webpki-roots 0.26.5", - "windows-registry 0.2.0", -] - -[[package]] -name = "rfc6979" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" -dependencies = [ - "hmac", - "subtle", + "windows-registry", ] [[package]] @@ -7958,6 +8220,15 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "rgb" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a" +dependencies = [ + "bytemuck", +] + [[package]] name = "rhai" version = "1.19.0" @@ -8001,15 +8272,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "ripemd" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" -dependencies = [ - "digest", -] - [[package]] name = "rkyv" version = "0.7.45" @@ -8040,41 +8302,30 @@ dependencies = [ ] [[package]] -name = "router-static-generation" -version = "0.1.0" -dependencies = [ - "dioxus", - "tracing-subscriber", -] +name = "rle-decode-fast" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3582f63211428f83597b51b2ddb88e2a91a9d52d12831f9d08f5e624e8977422" [[package]] -name = "rpm" -version = "0.14.0" +name = "ron" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e68a0d60350e5f4229cd69f08ec8f373e34424701702d1ee51a89aee1e9adcd1" +checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" dependencies = [ + "base64 0.21.7", "bitflags 2.6.0", - "bzip2", - "chrono", - "cpio", - "digest", - "enum-display-derive", - "enum-primitive-derive", - "flate2", - "hex", - "itertools 0.12.1", - "log", - "md-5", - "nom", - "num", - "num-derive", - "num-traits", - "pgp", - "sha1", - "sha2", - "thiserror", - "xz2", - "zstd 0.13.2", + "indexmap 2.5.0", + "serde", + "serde_derive", +] + +[[package]] +name = "router-static-generation" +version = "0.1.0" +dependencies = [ + "dioxus", + "tracing-subscriber", ] [[package]] @@ -8131,13 +8382,22 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver 0.9.0", +] + [[package]] name = "rustc_version" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "semver", + "semver 1.0.23", ] [[package]] @@ -8261,6 +8521,12 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +[[package]] +name = "ryu-js" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad97d4ce1560a5e27cec89519dc8300d1aa6035b099821261c651486a19e44d5" + [[package]] name = "same-file" version = "1.0.6" @@ -8323,20 +8589,6 @@ version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" -[[package]] -name = "sec1" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" -dependencies = [ - "base16ct", - "der", - "generic-array 0.14.7", - "pkcs8", - "subtle", - "zeroize", -] - [[package]] name = "security-framework" version = "2.11.1" @@ -8367,7 +8619,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe" dependencies = [ "bitflags 1.3.2", - "cssparser", + "cssparser 0.27.2", "derive_more", "fxhash", "log", @@ -8380,6 +8632,15 @@ dependencies = [ "thin-slice", ] +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + [[package]] name = "semver" version = "1.0.23" @@ -8389,6 +8650,12 @@ dependencies = [ "serde", ] +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + [[package]] name = "send_wrapper" version = "0.6.0" @@ -8413,17 +8680,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "serde-untagged" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2676ba99bd82f75cae5cbd2c8eda6fa0b8760f18978ea840e980dd5567b5c5b6" -dependencies = [ - "erased-serde", - "serde", - "typeid", -] - [[package]] name = "serde-wasm-bindgen" version = "0.5.0" @@ -8435,17 +8691,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "serde-wasm-bindgen" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b" -dependencies = [ - "js-sys", - "serde", - "wasm-bindgen", -] - [[package]] name = "serde_derive" version = "1.0.210" @@ -8490,6 +8735,16 @@ dependencies = [ "thiserror", ] +[[package]] +name = "serde_regex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8136f1a4ea815d7eac4101cfd0b16dc0cb5e1fe1b8609dfd728058656b7badf" +dependencies = [ + "regex", + "serde", +] + [[package]] name = "serde_repr" version = "0.1.19" @@ -8649,16 +8904,6 @@ dependencies = [ "digest", ] -[[package]] -name = "sha3" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" -dependencies = [ - "digest", - "keccak", -] - [[package]] name = "sharded-slab" version = "0.1.7" @@ -8720,12 +8965,30 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "simd-abstraction" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cadb29c57caadc51ff8346233b5cec1d240b68ce55cf1afc764818791876987" +dependencies = [ + "outref", +] + [[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 = "simdutf8" version = "0.1.4" @@ -8746,7 +9009,7 @@ version = "4.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e7e46c8c90251d47d08b28b8a419ffb4aede0f87c2eea95e17d1d5bacbf3ef1" dependencies = [ - "colored", + "colored 2.1.0", "log", "time", "windows-sys 0.48.0", @@ -8823,6 +9086,12 @@ dependencies = [ "version_check", ] +[[package]] +name = "smawk" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" + [[package]] name = "socket2" version = "0.5.7" @@ -8833,17 +9102,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "socks" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c3dbbd9ae980613c6dd8e28a9407b50509d3803b57624d5dfe8315218cd58b" -dependencies = [ - "byteorder", - "libc", - "winapi", -] - [[package]] name = "soup3" version = "0.5.0" @@ -8870,6 +9128,25 @@ dependencies = [ "system-deps", ] +[[package]] +name = "sourcemap" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dab08a862c70980b8e23698b507e272317ae52a608a164a844111f5372374f1f" +dependencies = [ + "base64-simd", + "bitvec", + "data-encoding", + "debugid", + "if_chain", + "rustc-hash 1.1.0", + "rustc_version 0.2.3", + "serde", + "serde_json", + "unicode-id-start", + "url", +] + [[package]] name = "spin" version = "0.9.8" @@ -9122,6 +9399,16 @@ dependencies = [ "uuid", ] +[[package]] +name = "st-map" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8257dd592de7614be71a2342d36ba2d527ddad3f9a0c8d09d6ceed4c371531e4" +dependencies = [ + "arrayvec", + "static-map-macro", +] + [[package]] name = "stability" version = "0.2.1" @@ -9139,11 +9426,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] -name = "static_assertions" -version = "1.1.0" +name = "stacker" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - +checksum = "799c883d55abdb5e98af1a7b3f23b9b6de8ecada0ecac058672d7635eb48ca7b" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "windows-sys 0.59.0", +] + +[[package]] +name = "static-map-macro" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "710e9696ef338691287aeb937ee6ffe60022f579d3c8d2fd9d58973a9a10a466" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "string_cache" version = "0.8.7" @@ -9170,6 +9481,18 @@ dependencies = [ "quote", ] +[[package]] +name = "string_enum" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05e383308aebc257e7d7920224fa055c632478d92744eca77f99be8fa1545b90" +dependencies = [ + "proc-macro2", + "quote", + "swc_macros_common", + "syn 2.0.77", +] + [[package]] name = "stringprep" version = "0.1.5" @@ -9273,10 +9596,1433 @@ dependencies = [ name = "suspense-carousel" version = "0.6.0-alpha.2" dependencies = [ - "dioxus", - "gloo-timers 0.3.0", - "serde", - "tokio", + "async-std", + "dioxus", + "serde", +] + +[[package]] +name = "swc" +version = "0.283.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cb7fe4bd6a604528819c84fc9acc5d5ec1b3bd0d11d13ee5d8a77d49ff678fa" +dependencies = [ + "anyhow", + "base64 0.21.7", + "dashmap", + "either", + "indexmap 2.5.0", + "jsonc-parser", + "lru 0.10.1", + "once_cell", + "parking_lot", + "pathdiff", + "regex", + "rustc-hash 1.1.0", + "serde", + "serde_json", + "sourcemap", + "swc_atoms", + "swc_cached", + "swc_common", + "swc_compiler_base 0.16.0", + "swc_config", + "swc_ecma_ast", + "swc_ecma_codegen", + "swc_ecma_ext_transforms", + "swc_ecma_lints 0.99.0", + "swc_ecma_loader", + "swc_ecma_minifier 0.201.0", + "swc_ecma_parser", + "swc_ecma_preset_env 0.214.0", + "swc_ecma_transforms 0.236.0", + "swc_ecma_transforms_base 0.144.0", + "swc_ecma_transforms_compat 0.170.0", + "swc_ecma_transforms_optimization 0.205.0", + "swc_ecma_utils", + "swc_ecma_visit", + "swc_error_reporters", + "swc_node_comments", + "swc_timer", + "swc_transform_common", + "swc_typescript", + "swc_visit", + "tracing", + "url", +] + +[[package]] +name = "swc_allocator" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc8bd3075d1c6964010333fae9ddcd91ad422a4f8eb8b3206a9b2b6afb4209e" +dependencies = [ + "bumpalo", + "hashbrown 0.14.5", + "ptr_meta", + "rustc-hash 1.1.0", + "triomphe", +] + +[[package]] +name = "swc_atoms" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb6567e4e67485b3e7662b486f1565bdae54bd5b9d6b16b2ba1a9babb1e42125" +dependencies = [ + "hstr", + "once_cell", + "rustc-hash 1.1.0", + "serde", +] + +[[package]] +name = "swc_cached" +version = "0.3.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83406221c501860fce9c27444f44125eafe9e598b8b81be7563d7036784cd05c" +dependencies = [ + "ahash 0.8.11", + "anyhow", + "dashmap", + "once_cell", + "regex", + "serde", +] + +[[package]] +name = "swc_common" +version = "0.37.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12d0a8eaaf1606c9207077d75828008cb2dfb51b095a766bd2b72ef893576e31" +dependencies = [ + "ahash 0.8.11", + "ast_node", + "better_scoped_tls", + "cfg-if", + "either", + "from_variant", + "new_debug_unreachable", + "num-bigint", + "once_cell", + "parking_lot", + "rustc-hash 1.1.0", + "serde", + "siphasher", + "sourcemap", + "swc_allocator", + "swc_atoms", + "swc_eq_ignore_macros", + "swc_visit", + "tracing", + "unicode-width", + "url", +] + +[[package]] +name = "swc_compiler_base" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9b47fac27c2e84e033bdea294ac12f575322c81c9d5d44f423e3f11ff239a86" +dependencies = [ + "anyhow", + "base64 0.21.7", + "once_cell", + "pathdiff", + "rustc-hash 1.1.0", + "serde", + "serde_json", + "sourcemap", + "swc_allocator", + "swc_atoms", + "swc_common", + "swc_config", + "swc_ecma_ast", + "swc_ecma_codegen", + "swc_ecma_minifier 0.201.0", + "swc_ecma_parser", + "swc_ecma_visit", + "swc_timer", +] + +[[package]] +name = "swc_compiler_base" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "feb87f8dc7be1a034d5c29bcc4be9d504ddfd2f8aa1a1338fc568e104e087d29" +dependencies = [ + "anyhow", + "base64 0.21.7", + "once_cell", + "pathdiff", + "rustc-hash 1.1.0", + "serde", + "serde_json", + "sourcemap", + "swc_allocator", + "swc_atoms", + "swc_common", + "swc_config", + "swc_ecma_ast", + "swc_ecma_codegen", + "swc_ecma_minifier 0.204.0", + "swc_ecma_parser", + "swc_ecma_visit", + "swc_timer", +] + +[[package]] +name = "swc_config" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4740e53eaf68b101203c1df0937d5161a29f3c13bceed0836ddfe245b72dd000" +dependencies = [ + "anyhow", + "indexmap 2.5.0", + "serde", + "serde_json", + "sourcemap", + "swc_cached", + "swc_config_macro", +] + +[[package]] +name = "swc_config_macro" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c5f56139042c1a95b54f5ca48baa0e0172d369bcc9d3d473dad1de36bae8399" +dependencies = [ + "proc-macro2", + "quote", + "swc_macros_common", + "syn 2.0.77", +] + +[[package]] +name = "swc_ecma_ast" +version = "0.118.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6f866d12e4d519052b92a0a86d1ac7ff17570da1272ca0c89b3d6f802cd79df" +dependencies = [ + "bitflags 2.6.0", + "is-macro", + "num-bigint", + "phf 0.11.2", + "scoped-tls", + "serde", + "string_enum", + "swc_atoms", + "swc_common", + "unicode-id-start", +] + +[[package]] +name = "swc_ecma_codegen" +version = "0.155.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7641608ef117cfbef9581a99d02059b522fcca75e5244fa0cbbd8606689c6f" +dependencies = [ + "memchr", + "num-bigint", + "once_cell", + "serde", + "sourcemap", + "swc_allocator", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_codegen_macros", + "tracing", +] + +[[package]] +name = "swc_ecma_codegen_macros" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "859fabde36db38634f3fad548dd5e3410c1aebba1b67a3c63e67018fa57a0bca" +dependencies = [ + "proc-macro2", + "quote", + "swc_macros_common", + "syn 2.0.77", +] + +[[package]] +name = "swc_ecma_compat_bugfixes" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f9cac39f19d6509db921f4b75934aaa64fc84b599416e5c1fcaed1c313132f" +dependencies = [ + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_compat_es2015 0.11.1", + "swc_ecma_transforms_base 0.144.0", + "swc_ecma_utils", + "swc_ecma_visit", + "swc_trace_macro", + "tracing", +] + +[[package]] +name = "swc_ecma_compat_bugfixes" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75429b44cc479cbe018d5994eddae5ac7ab887ebefeb3596720921bc4cdff551" +dependencies = [ + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_compat_es2015 0.12.0", + "swc_ecma_transforms_base 0.145.0", + "swc_ecma_utils", + "swc_ecma_visit", + "swc_trace_macro", + "tracing", +] + +[[package]] +name = "swc_ecma_compat_common" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9acdf402b36f8e83084b10e119d7ba9d07e5229ef39e1343f147db816c7b73e" +dependencies = [ + "swc_common", + "swc_ecma_ast", + "swc_ecma_utils", + "swc_ecma_visit", + "swc_trace_macro", +] + +[[package]] +name = "swc_ecma_compat_es2015" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dade6e0c6e8ddb61281fee2331c3775a920c31535b91e8cace2e0c4eed6158d3" +dependencies = [ + "arrayvec", + "indexmap 2.5.0", + "is-macro", + "serde", + "serde_derive", + "smallvec", + "swc_atoms", + "swc_common", + "swc_config", + "swc_ecma_ast", + "swc_ecma_compat_common", + "swc_ecma_transforms_base 0.144.0", + "swc_ecma_transforms_classes 0.133.0", + "swc_ecma_transforms_macros", + "swc_ecma_utils", + "swc_ecma_visit", + "swc_trace_macro", + "tracing", +] + +[[package]] +name = "swc_ecma_compat_es2015" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c988d9018d6abb22b0fcc2da6a624be2db7c56681b6180d1cb5faa2672fd8001" +dependencies = [ + "arrayvec", + "indexmap 2.5.0", + "is-macro", + "rustc-hash 1.1.0", + "serde", + "serde_derive", + "smallvec", + "swc_atoms", + "swc_common", + "swc_config", + "swc_ecma_ast", + "swc_ecma_compat_common", + "swc_ecma_transforms_base 0.145.0", + "swc_ecma_transforms_classes 0.134.0", + "swc_ecma_transforms_macros", + "swc_ecma_utils", + "swc_ecma_visit", + "swc_trace_macro", + "tracing", +] + +[[package]] +name = "swc_ecma_compat_es2016" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "209e347cdc3fb56632a1d882f981f3448f5f529c16d8da9d770207fffda4a8f6" +dependencies = [ + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_transforms_base 0.144.0", + "swc_ecma_transforms_macros", + "swc_ecma_utils", + "swc_ecma_visit", + "swc_trace_macro", + "tracing", +] + +[[package]] +name = "swc_ecma_compat_es2016" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b7a3e086151c70ff940531ddcd04c01351ae80aa4593fd2906255d18a836b4f" +dependencies = [ + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_transforms_base 0.145.0", + "swc_ecma_transforms_macros", + "swc_ecma_utils", + "swc_ecma_visit", + "swc_trace_macro", + "tracing", +] + +[[package]] +name = "swc_ecma_compat_es2017" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4aa87a6861b2adc8b0178fb450165101c4396409481c8726ec90ad28398cae5d" +dependencies = [ + "serde", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_transforms_base 0.144.0", + "swc_ecma_transforms_macros", + "swc_ecma_utils", + "swc_ecma_visit", + "swc_trace_macro", + "tracing", +] + +[[package]] +name = "swc_ecma_compat_es2017" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3b74c89c9bd4fa532fba3d1ec47b129ec450b4143d3914118cd61b0e44d4a4b" +dependencies = [ + "serde", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_transforms_base 0.145.0", + "swc_ecma_transforms_macros", + "swc_ecma_utils", + "swc_ecma_visit", + "swc_trace_macro", + "tracing", +] + +[[package]] +name = "swc_ecma_compat_es2018" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f577f098e7c3738ade709caadb17c9f3bd911ea2ee6cfacca561d12addcc5761" +dependencies = [ + "serde", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_compat_common", + "swc_ecma_transforms_base 0.144.0", + "swc_ecma_transforms_macros", + "swc_ecma_utils", + "swc_ecma_visit", + "swc_trace_macro", + "tracing", +] + +[[package]] +name = "swc_ecma_compat_es2018" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a40bf74a06c433eee502ea6347596d5766d77da8baf32653d14a6655df4e181a" +dependencies = [ + "serde", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_compat_common", + "swc_ecma_transforms_base 0.145.0", + "swc_ecma_transforms_macros", + "swc_ecma_utils", + "swc_ecma_visit", + "swc_trace_macro", + "tracing", +] + +[[package]] +name = "swc_ecma_compat_es2019" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d52253dc2f83a3fca526c387c33e4ff9a8423b68c271414c9f870e1ced3231" +dependencies = [ + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_transforms_base 0.144.0", + "swc_ecma_utils", + "swc_ecma_visit", + "swc_trace_macro", + "tracing", +] + +[[package]] +name = "swc_ecma_compat_es2019" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10afb20890ffda37eefdfe06c3bb0d12e5ec8698667cb9e3689b74066b398845" +dependencies = [ + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_transforms_base 0.145.0", + "swc_ecma_utils", + "swc_ecma_visit", + "swc_trace_macro", + "tracing", +] + +[[package]] +name = "swc_ecma_compat_es2020" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed343932876fad34b1d4a13e30c55b94531e89916f45e7c04203bc49a29565b9" +dependencies = [ + "serde", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_compat_es2022 0.11.0", + "swc_ecma_transforms_base 0.144.0", + "swc_ecma_utils", + "swc_ecma_visit", + "swc_trace_macro", + "tracing", +] + +[[package]] +name = "swc_ecma_compat_es2020" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0608c4814a362d5362bc536507d8c89b287521778e8b678fe4590bfa1843803a" +dependencies = [ + "serde", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_compat_es2022 0.12.0", + "swc_ecma_transforms_base 0.145.0", + "swc_ecma_utils", + "swc_ecma_visit", + "swc_trace_macro", + "tracing", +] + +[[package]] +name = "swc_ecma_compat_es2021" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b6b28a2c109466eaa809d9b9a5b81dcbb4e269ba293a9c5c34aabc67b6427bc" +dependencies = [ + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_transforms_base 0.144.0", + "swc_ecma_utils", + "swc_ecma_visit", + "swc_trace_macro", + "tracing", +] + +[[package]] +name = "swc_ecma_compat_es2021" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f12ffb0f4282f4b333efa98c9653d181d89e1b5339d4be0d789189a246ef34b" +dependencies = [ + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_transforms_base 0.145.0", + "swc_ecma_utils", + "swc_ecma_visit", + "swc_trace_macro", + "tracing", +] + +[[package]] +name = "swc_ecma_compat_es2022" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab3a644e271ea2a9df88e3e456c5c204c4916ef5136b7d946f9cd25607f47ec6" +dependencies = [ + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_compat_common", + "swc_ecma_transforms_base 0.144.0", + "swc_ecma_transforms_classes 0.133.0", + "swc_ecma_transforms_macros", + "swc_ecma_utils", + "swc_ecma_visit", + "swc_trace_macro", + "tracing", +] + +[[package]] +name = "swc_ecma_compat_es2022" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc16be9dd64e1b32569375b0b73ecc7dc74f9d848e8caaf2007896e2cf8d68a7" +dependencies = [ + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_compat_common", + "swc_ecma_transforms_base 0.145.0", + "swc_ecma_transforms_classes 0.134.0", + "swc_ecma_transforms_macros", + "swc_ecma_utils", + "swc_ecma_visit", + "swc_trace_macro", + "tracing", +] + +[[package]] +name = "swc_ecma_compat_es3" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e55ffadc12067b21524bf7b5d6938021ee918f65f18937ed27245c23544bc910" +dependencies = [ + "swc_common", + "swc_ecma_ast", + "swc_ecma_transforms_base 0.144.0", + "swc_ecma_utils", + "swc_ecma_visit", + "swc_trace_macro", + "tracing", +] + +[[package]] +name = "swc_ecma_compat_es3" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e684ae87d26ad3012e588d0e268158cadee10ddc0cda261069f0f280a8b23ce7" +dependencies = [ + "swc_common", + "swc_ecma_ast", + "swc_ecma_transforms_base 0.145.0", + "swc_ecma_utils", + "swc_ecma_visit", + "swc_trace_macro", + "tracing", +] + +[[package]] +name = "swc_ecma_ext_transforms" +version = "0.120.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad03ee53c734eb74757d03c07ec71b1a982261830c9253ef3e2e4a089f9af25d" +dependencies = [ + "phf 0.11.2", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_utils", + "swc_ecma_visit", +] + +[[package]] +name = "swc_ecma_lints" +version = "0.99.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20c11bcc9e3dc49929500c07c8b0c84a88064847d31e9ee16204b257e6bd315c" +dependencies = [ + "auto_impl", + "dashmap", + "parking_lot", + "rayon", + "regex", + "serde", + "swc_atoms", + "swc_common", + "swc_config", + "swc_ecma_ast", + "swc_ecma_utils", + "swc_ecma_visit", +] + +[[package]] +name = "swc_ecma_lints" +version = "0.100.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89907376ce67b56d8fbf79cca830a12cb41f93dccc306008c07d8eba8f6d388e" +dependencies = [ + "auto_impl", + "dashmap", + "parking_lot", + "rayon", + "regex", + "serde", + "swc_atoms", + "swc_common", + "swc_config", + "swc_ecma_ast", + "swc_ecma_utils", + "swc_ecma_visit", +] + +[[package]] +name = "swc_ecma_loader" +version = "0.49.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55fa3d55045b97894bfb04d38aff6d6302ac8a6a38e3bb3dfb0d20475c4974a9" +dependencies = [ + "anyhow", + "dashmap", + "lru 0.10.1", + "normpath 0.2.0", + "once_cell", + "parking_lot", + "path-clean 0.1.0", + "pathdiff", + "serde", + "serde_json", + "swc_atoms", + "swc_cached", + "swc_common", + "tracing", +] + +[[package]] +name = "swc_ecma_minifier" +version = "0.201.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d75a42254926bad8b7fa9390767a18ac608d99cfd5a3be675e4739c2cf7db1b" +dependencies = [ + "arrayvec", + "indexmap 2.5.0", + "num-bigint", + "num_cpus", + "once_cell", + "parking_lot", + "phf 0.11.2", + "radix_fmt", + "regex", + "rustc-hash 1.1.0", + "ryu-js", + "serde", + "serde_json", + "swc_allocator", + "swc_atoms", + "swc_common", + "swc_config", + "swc_ecma_ast", + "swc_ecma_codegen", + "swc_ecma_parser", + "swc_ecma_transforms_base 0.144.0", + "swc_ecma_transforms_optimization 0.205.0", + "swc_ecma_usage_analyzer", + "swc_ecma_utils", + "swc_ecma_visit", + "swc_timer", + "tracing", +] + +[[package]] +name = "swc_ecma_minifier" +version = "0.204.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34d88917a66b8f457c5953d2ff2d7788259658c89578636b28e9ac6ae56bbfd9" +dependencies = [ + "arrayvec", + "indexmap 2.5.0", + "num-bigint", + "num_cpus", + "once_cell", + "parking_lot", + "phf 0.11.2", + "radix_fmt", + "regex", + "rustc-hash 1.1.0", + "ryu-js", + "serde", + "serde_json", + "swc_allocator", + "swc_atoms", + "swc_common", + "swc_config", + "swc_ecma_ast", + "swc_ecma_codegen", + "swc_ecma_parser", + "swc_ecma_transforms_base 0.145.0", + "swc_ecma_transforms_optimization 0.208.0", + "swc_ecma_usage_analyzer", + "swc_ecma_utils", + "swc_ecma_visit", + "swc_timer", + "tracing", +] + +[[package]] +name = "swc_ecma_parser" +version = "0.149.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683dada14722714588b56481399c699378b35b2ba4deb5c4db2fb627a97fb54b" +dependencies = [ + "either", + "new_debug_unreachable", + "num-bigint", + "num-traits", + "phf 0.11.2", + "serde", + "smallvec", + "smartstring", + "stacker", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "tracing", + "typed-arena", +] + +[[package]] +name = "swc_ecma_preset_env" +version = "0.214.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e85162c77f8c80b55e5aed3a5e5477b74a53ce9cca05dec6e3dabec1de0f49af" +dependencies = [ + "anyhow", + "dashmap", + "indexmap 2.5.0", + "once_cell", + "preset_env_base", + "rustc-hash 1.1.0", + "semver 1.0.23", + "serde", + "serde_json", + "st-map", + "string_enum", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_transforms 0.236.0", + "swc_ecma_utils", + "swc_ecma_visit", +] + +[[package]] +name = "swc_ecma_preset_env" +version = "0.217.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51992e6bb854ef2e6c7a1b9a14ed8d0e3c8f905d348f694759f9a97bfa6a425" +dependencies = [ + "anyhow", + "dashmap", + "indexmap 2.5.0", + "once_cell", + "preset_env_base", + "rustc-hash 1.1.0", + "semver 1.0.23", + "serde", + "serde_json", + "st-map", + "string_enum", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_transforms 0.239.0", + "swc_ecma_utils", + "swc_ecma_visit", +] + +[[package]] +name = "swc_ecma_transforms" +version = "0.236.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d289a83ab829d076d6c2bf2cc0beea7fbf0480ac47a415401f683181a65f4856" +dependencies = [ + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_transforms_base 0.144.0", + "swc_ecma_transforms_compat 0.170.0", + "swc_ecma_transforms_module 0.187.0", + "swc_ecma_transforms_optimization 0.205.0", + "swc_ecma_transforms_proposal 0.178.0", + "swc_ecma_transforms_react 0.190.0", + "swc_ecma_transforms_typescript 0.195.1", + "swc_ecma_utils", + "swc_ecma_visit", +] + +[[package]] +name = "swc_ecma_transforms" +version = "0.239.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82df2dd8048fe23f1df72acd52bfebf846b3d5a76e048eee32acf9af9bee6a98" +dependencies = [ + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_transforms_base 0.145.0", + "swc_ecma_transforms_compat 0.171.0", + "swc_ecma_transforms_proposal 0.179.0", + "swc_ecma_utils", + "swc_ecma_visit", +] + +[[package]] +name = "swc_ecma_transforms_base" +version = "0.144.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c0a71579d030e12fd3cfbfc8712c4ce21afc526f2a759903c77d8df61950f5e" +dependencies = [ + "better_scoped_tls", + "bitflags 2.6.0", + "indexmap 2.5.0", + "once_cell", + "phf 0.11.2", + "rustc-hash 1.1.0", + "serde", + "smallvec", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_parser", + "swc_ecma_utils", + "swc_ecma_visit", + "tracing", +] + +[[package]] +name = "swc_ecma_transforms_base" +version = "0.145.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65f21494e75d0bd8ef42010b47cabab9caaed8f2207570e809f6f4eb51a710d1" +dependencies = [ + "better_scoped_tls", + "bitflags 2.6.0", + "indexmap 2.5.0", + "once_cell", + "phf 0.11.2", + "rustc-hash 1.1.0", + "serde", + "smallvec", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_parser", + "swc_ecma_utils", + "swc_ecma_visit", + "tracing", +] + +[[package]] +name = "swc_ecma_transforms_classes" +version = "0.133.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f37ec04525798a09ce02e52dc15433acee2d86664da0b8ede55bb5cefd95384" +dependencies = [ + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_transforms_base 0.144.0", + "swc_ecma_utils", + "swc_ecma_visit", +] + +[[package]] +name = "swc_ecma_transforms_classes" +version = "0.134.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3d884594385bea9405a2e1721151470d9a14d3ceec5dd773c0ca6894791601" +dependencies = [ + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_transforms_base 0.145.0", + "swc_ecma_utils", + "swc_ecma_visit", +] + +[[package]] +name = "swc_ecma_transforms_compat" +version = "0.170.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bb500b65423646da940e289ad37e7c88332d7194248c33fc63a9e768e104fe5" +dependencies = [ + "arrayvec", + "indexmap 2.5.0", + "is-macro", + "num-bigint", + "serde", + "smallvec", + "swc_atoms", + "swc_common", + "swc_config", + "swc_ecma_ast", + "swc_ecma_compat_bugfixes 0.11.0", + "swc_ecma_compat_common", + "swc_ecma_compat_es2015 0.11.1", + "swc_ecma_compat_es2016 0.11.0", + "swc_ecma_compat_es2017 0.11.1", + "swc_ecma_compat_es2018 0.11.0", + "swc_ecma_compat_es2019 0.11.0", + "swc_ecma_compat_es2020 0.11.0", + "swc_ecma_compat_es2021 0.11.0", + "swc_ecma_compat_es2022 0.11.0", + "swc_ecma_compat_es3 0.11.0", + "swc_ecma_transforms_base 0.144.0", + "swc_ecma_transforms_classes 0.133.0", + "swc_ecma_transforms_macros", + "swc_ecma_utils", + "swc_ecma_visit", + "swc_trace_macro", + "tracing", +] + +[[package]] +name = "swc_ecma_transforms_compat" +version = "0.171.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f23da29c1279b6e0c1ac0df9d0f7fd6c955a141a9770e5a0a2d54292509bcf6" +dependencies = [ + "arrayvec", + "indexmap 2.5.0", + "is-macro", + "num-bigint", + "serde", + "smallvec", + "swc_atoms", + "swc_common", + "swc_config", + "swc_ecma_ast", + "swc_ecma_compat_bugfixes 0.12.0", + "swc_ecma_compat_common", + "swc_ecma_compat_es2015 0.12.0", + "swc_ecma_compat_es2016 0.12.0", + "swc_ecma_compat_es2017 0.12.0", + "swc_ecma_compat_es2018 0.12.0", + "swc_ecma_compat_es2019 0.12.0", + "swc_ecma_compat_es2020 0.12.0", + "swc_ecma_compat_es2021 0.12.0", + "swc_ecma_compat_es2022 0.12.0", + "swc_ecma_compat_es3 0.12.0", + "swc_ecma_transforms_base 0.145.0", + "swc_ecma_transforms_classes 0.134.0", + "swc_ecma_transforms_macros", + "swc_ecma_utils", + "swc_ecma_visit", + "swc_trace_macro", + "tracing", +] + +[[package]] +name = "swc_ecma_transforms_macros" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "500a1dadad1e0e41e417d633b3d6d5de677c9e0d3159b94ba3348436cdb15aab" +dependencies = [ + "proc-macro2", + "quote", + "swc_macros_common", + "syn 2.0.77", +] + +[[package]] +name = "swc_ecma_transforms_module" +version = "0.187.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc022be297cdc70d5e71720587c7e810401a958577d77830f3108411e73c466d" +dependencies = [ + "Inflector", + "anyhow", + "bitflags 2.6.0", + "indexmap 2.5.0", + "is-macro", + "path-clean 1.0.1", + "pathdiff", + "regex", + "serde", + "swc_atoms", + "swc_cached", + "swc_common", + "swc_ecma_ast", + "swc_ecma_loader", + "swc_ecma_parser", + "swc_ecma_transforms_base 0.144.0", + "swc_ecma_utils", + "swc_ecma_visit", + "tracing", +] + +[[package]] +name = "swc_ecma_transforms_module" +version = "0.190.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c4d0255362149854b923125e9910ce0a5405ce6d03fb325c5fdd8e9f13a0845" +dependencies = [ + "Inflector", + "anyhow", + "bitflags 2.6.0", + "indexmap 2.5.0", + "is-macro", + "path-clean 1.0.1", + "pathdiff", + "regex", + "serde", + "swc_atoms", + "swc_cached", + "swc_common", + "swc_ecma_ast", + "swc_ecma_loader", + "swc_ecma_parser", + "swc_ecma_transforms_base 0.145.0", + "swc_ecma_utils", + "swc_ecma_visit", + "tracing", +] + +[[package]] +name = "swc_ecma_transforms_optimization" +version = "0.205.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17446e46b75654901d962251ec4d0063423ee81759a325ed82fcf073308d97ca" +dependencies = [ + "dashmap", + "indexmap 2.5.0", + "once_cell", + "petgraph", + "rustc-hash 1.1.0", + "serde_json", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_parser", + "swc_ecma_transforms_base 0.144.0", + "swc_ecma_transforms_macros", + "swc_ecma_utils", + "swc_ecma_visit", + "swc_fast_graph", + "tracing", +] + +[[package]] +name = "swc_ecma_transforms_optimization" +version = "0.208.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98d8447ea20ef76958a8240feef95743702485a84331e6df5bdbe7e383c87838" +dependencies = [ + "dashmap", + "indexmap 2.5.0", + "once_cell", + "petgraph", + "rustc-hash 1.1.0", + "serde_json", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_parser", + "swc_ecma_transforms_base 0.145.0", + "swc_ecma_transforms_macros", + "swc_ecma_utils", + "swc_ecma_visit", + "swc_fast_graph", + "tracing", +] + +[[package]] +name = "swc_ecma_transforms_proposal" +version = "0.178.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9c7ddb3aae86f19eb9e41b0c62509d8e400c1dc79c0889df98f6df1ab893f3f" +dependencies = [ + "either", + "rustc-hash 1.1.0", + "serde", + "smallvec", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_transforms_base 0.144.0", + "swc_ecma_transforms_classes 0.133.0", + "swc_ecma_transforms_macros", + "swc_ecma_utils", + "swc_ecma_visit", +] + +[[package]] +name = "swc_ecma_transforms_proposal" +version = "0.179.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79938ff510fc647febd8c6c3ef4143d099fdad87a223680e632623d056dae2dd" +dependencies = [ + "either", + "rustc-hash 1.1.0", + "serde", + "smallvec", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_transforms_base 0.145.0", + "swc_ecma_transforms_classes 0.134.0", + "swc_ecma_transforms_macros", + "swc_ecma_utils", + "swc_ecma_visit", +] + +[[package]] +name = "swc_ecma_transforms_react" +version = "0.190.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e54a8c87d90812bf69b0f07931bb629111a3f24efe83b9190c3a40a5ebc25e" +dependencies = [ + "base64 0.21.7", + "dashmap", + "indexmap 2.5.0", + "once_cell", + "serde", + "sha1", + "string_enum", + "swc_allocator", + "swc_atoms", + "swc_common", + "swc_config", + "swc_ecma_ast", + "swc_ecma_parser", + "swc_ecma_transforms_base 0.144.0", + "swc_ecma_transforms_macros", + "swc_ecma_utils", + "swc_ecma_visit", +] + +[[package]] +name = "swc_ecma_transforms_react" +version = "0.191.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76c76d8b9792ce51401d38da0fa62158d61f6d80d16d68fe5b03ce4bf5fba383" +dependencies = [ + "base64 0.21.7", + "dashmap", + "indexmap 2.5.0", + "once_cell", + "serde", + "sha1", + "string_enum", + "swc_allocator", + "swc_atoms", + "swc_common", + "swc_config", + "swc_ecma_ast", + "swc_ecma_parser", + "swc_ecma_transforms_base 0.145.0", + "swc_ecma_transforms_macros", + "swc_ecma_utils", + "swc_ecma_visit", +] + +[[package]] +name = "swc_ecma_transforms_typescript" +version = "0.195.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f814b32ec83fde097df19e7346c429825390d156d0015f321f1f6434b6a06c0c" +dependencies = [ + "ryu-js", + "serde", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_transforms_base 0.144.0", + "swc_ecma_transforms_react 0.190.0", + "swc_ecma_utils", + "swc_ecma_visit", +] + +[[package]] +name = "swc_ecma_transforms_typescript" +version = "0.198.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15455da4768f97186c40523e83600495210c11825d3a44db43383fd81eace88d" +dependencies = [ + "ryu-js", + "serde", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_transforms_base 0.145.0", + "swc_ecma_transforms_react 0.191.0", + "swc_ecma_utils", + "swc_ecma_visit", +] + +[[package]] +name = "swc_ecma_usage_analyzer" +version = "0.30.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7689421c6a892642c5907fd608c56d982fdef0d6456f9dba3cc418c6ea7e07" +dependencies = [ + "indexmap 2.5.0", + "rustc-hash 1.1.0", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_utils", + "swc_ecma_visit", + "swc_timer", + "tracing", +] + +[[package]] +name = "swc_ecma_utils" +version = "0.134.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "029eec7dd485923a75b5a45befd04510288870250270292fc2c1b3a9e7547408" +dependencies = [ + "indexmap 2.5.0", + "num_cpus", + "once_cell", + "rustc-hash 1.1.0", + "ryu-js", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_visit", + "tracing", + "unicode-id", +] + +[[package]] +name = "swc_ecma_visit" +version = "0.104.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b1c6802e68e51f336e8bc9644e9ff9da75d7da9c1a6247d532f2e908aa33e81" +dependencies = [ + "new_debug_unreachable", + "num-bigint", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_visit", + "tracing", +] + +[[package]] +name = "swc_eq_ignore_macros" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63db0adcff29d220c3d151c5b25c0eabe7e32dd936212b84cdaa1392e3130497" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "swc_error_reporters" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d049e9256abf29d9fc66d3db3ea44b6815a64ad565ce31e117a74ee96478bb3" +dependencies = [ + "anyhow", + "miette", + "once_cell", + "parking_lot", + "swc_common", +] + +[[package]] +name = "swc_fast_graph" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357e2c97bb51431d65080f25b436bc4e2fc1a7f64a643bc21a8353e478dc799f" +dependencies = [ + "indexmap 2.5.0", + "petgraph", + "rustc-hash 1.1.0", + "swc_common", +] + +[[package]] +name = "swc_macros_common" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f486687bfb7b5c560868f69ed2d458b880cebc9babebcb67e49f31b55c5bf847" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "swc_node_comments" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d016ab18b432523b2a3c104ce3aaf7d869db46c0a41477dbfb6201ddc86c1eb0" +dependencies = [ + "dashmap", + "swc_atoms", + "swc_common", +] + +[[package]] +name = "swc_timer" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b5fb6f8b8b85512aacbb3d7140a828666e0e0b1bcc69bf84000a0cd36306bab" +dependencies = [ + "tracing", +] + +[[package]] +name = "swc_trace_macro" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff9719b6085dd2824fd61938a881937be14b08f95e2d27c64c825a9f65e052ba" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "swc_transform_common" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eda3e80e1ad638d3575bc07745a914af13dcb02215098659f864731078271f2c" +dependencies = [ + "better_scoped_tls", + "once_cell", + "rustc-hash 1.1.0", + "serde", + "serde_json", +] + +[[package]] +name = "swc_typescript" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5d043347b109a8aebfe01aaeada4af322304ea0f54ae8e5721df9afcb9305ca" +dependencies = [ + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "thiserror", +] + +[[package]] +name = "swc_visit" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ceb044142ba2719ef9eb3b6b454fce61ab849eb696c34d190f04651955c613d" +dependencies = [ + "either", + "new_debug_unreachable", ] [[package]] @@ -9328,19 +11074,6 @@ dependencies = [ "futures-core", ] -[[package]] -name = "sysctl" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225e483f02d0ad107168dc57381a8a40c3aeea6abe47f37506931f861643cfa8" -dependencies = [ - "bitflags 1.3.2", - "byteorder", - "libc", - "thiserror", - "walkdir", -] - [[package]] name = "system-configuration" version = "0.6.1" @@ -9371,24 +11104,23 @@ dependencies = [ "cfg-expr 0.15.8", "heck 0.5.0", "pkg-config", - "toml", + "toml 0.8.19", "version-compare", ] [[package]] name = "tao" -version = "0.30.0" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a93f2c6b8fdaeb7f417bda89b5bc767999745c3052969664ae1fa65892deb7e" +checksum = "69ebbccb78deb5a36744c079eea2981b4a48ecbbe6b1b2ffbaa528bea3f5e5db" dependencies = [ - "bitflags 2.6.0", - "cocoa", - "core-foundation 0.10.0", - "core-graphics", + "bitflags 1.3.2", + "cocoa 0.25.0", + "core-foundation 0.9.4", + "core-graphics 0.23.2", "crossbeam-channel", "dispatch", "dlopen2", - "dpi", "gdkwayland-sys", "gdkx11-sys", "gtk", @@ -9397,9 +11129,9 @@ dependencies = [ "lazy_static", "libc", "log", - "ndk", + "ndk 0.7.0", "ndk-context", - "ndk-sys", + "ndk-sys 0.4.1+23.1.7779620", "objc", "once_cell", "parking_lot", @@ -9409,8 +11141,7 @@ dependencies = [ "tao-macros", "unicode-segmentation", "url", - "windows", - "windows-core 0.58.0", + "windows 0.54.0", "windows-version", "x11-dl", ] @@ -9451,36 +11182,33 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tauri-bundler" -version = "2.0.0-rc.0" +version = "1.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afefaa18bdaa49afff89a299d6afb05de7ffc2017d84d717239455e6645d21ea" +checksum = "f4693930d30c4836ef41f51097502fd77a0ae9f54c3af6aafb8997b2a81f40b4" dependencies = [ "anyhow", "ar", - "bitness", - "dirs", + "dirs-next", "dunce", - "flate2", "glob", "handlebars", - "heck 0.5.0", + "heck 0.4.1", "hex", - "image", + "image 0.24.9", + "libflate", "log", "md5", "os_pipe", "plist", "regex", - "rpm", - "semver", + "semver 1.0.23", "serde", "serde_json", "sha1", "sha2", - "strsim 0.11.1", + "strsim 0.10.0", "tar", "tauri-icns", - "tauri-macos-sign", "tauri-utils", "tempfile", "thiserror", @@ -9503,51 +11231,31 @@ dependencies = [ "png", ] -[[package]] -name = "tauri-macos-sign" -version = "0.1.0-beta.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a689cc26acd1e619b317f7cec95db2eb9fa1c69888030162a546a7fafa7eb3d" -dependencies = [ - "anyhow", - "dirs-next", - "once-cell-regex", - "os_pipe", - "plist", - "rand 0.8.5", - "serde", - "serde_json", - "tempfile", - "x509-certificate", -] - [[package]] name = "tauri-utils" -version = "2.0.0-rc.11" +version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3019641087c9039b57ebfca95fa42a93c07056845b7d8d57c8966061bcee83b4" +checksum = "450b17a7102e5d46d4bdabae0d1590fd27953e704e691fc081f06c06d2253b35" dependencies = [ "ctor", "dunce", "glob", + "heck 0.5.0", "html5ever", - "infer", + "infer 0.13.0", "json-patch", "kuchikiki", "log", "memchr", "phf 0.11.2", - "regex", - "semver", + "semver 1.0.23", "serde", - "serde-untagged", "serde_json", "serde_with", "thiserror", - "toml", "url", - "urlpattern", "walkdir", + "windows-version", ] [[package]] @@ -9602,6 +11310,17 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "textwrap" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" +dependencies = [ + "smawk", + "unicode-linebreak", + "unicode-width", +] + [[package]] name = "thin-slice" version = "0.1.1" @@ -9807,6 +11526,21 @@ dependencies = [ "tungstenite 0.21.0", ] +[[package]] +name = "tokio-tungstenite" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6989540ced10490aaf14e6bad2e3d33728a2813310a0c71d1574304c49631cd" +dependencies = [ + "futures-util", + "log", + "native-tls", + "rustls 0.23.13", + "tokio", + "tokio-native-tls", + "tungstenite 0.23.0", +] + [[package]] name = "tokio-util" version = "0.7.12" @@ -9815,15 +11549,25 @@ checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" dependencies = [ "bytes", "futures-core", - "futures-io", "futures-sink", "futures-util", "hashbrown 0.14.5", "pin-project-lite", - "slab", "tokio", ] +[[package]] +name = "toml" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.19.15", +] + [[package]] name = "toml" version = "0.8.19" @@ -9834,7 +11578,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.21", + "toml_edit 0.22.20", ] [[package]] @@ -9853,6 +11597,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ "indexmap 2.5.0", + "serde", + "serde_spanned", "toml_datetime", "winnow 0.5.40", ] @@ -9870,9 +11616,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.21" +version = "0.22.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf" +checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" dependencies = [ "indexmap 2.5.0", "serde", @@ -10065,6 +11811,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "triomphe" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6631e42e10b40c0690bf92f404ebcfe6e1fdb480391d15f17cc8e96eeed5369" +dependencies = [ + "serde", + "stable_deref_trait", +] + [[package]] name = "try-lock" version = "0.2.5" @@ -10083,7 +11839,7 @@ dependencies = [ "serde_derive", "serde_json", "termcolor", - "toml", + "toml 0.8.19", ] [[package]] @@ -10117,21 +11873,13 @@ dependencies = [ "http 1.1.0", "httparse", "log", + "native-tls", "rand 0.8.5", "sha1", "thiserror", "utf-8", ] -[[package]] -name = "twofish" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a78e83a30223c757c3947cd144a31014ff04298d8719ae10d03c31c0448c8013" -dependencies = [ - "cipher", -] - [[package]] name = "twox-hash" version = "1.6.3" @@ -10143,10 +11891,10 @@ dependencies = [ ] [[package]] -name = "typeid" -version = "1.0.2" +name = "typed-arena" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e13db2e0ccd5e14a544e8a246ba2312cd25223f616442d7f2cb0e3db614236e" +checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" [[package]] name = "typenum" @@ -10171,56 +11919,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "uname" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b72f89f0ca32e4db1c04e2a72f5345d59796d4866a1ee0609084569f73683dc8" -dependencies = [ - "libc", -] - -[[package]] -name = "unic-char-property" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" -dependencies = [ - "unic-char-range", -] - -[[package]] -name = "unic-char-range" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" - -[[package]] -name = "unic-common" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" - -[[package]] -name = "unic-ucd-ident" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987" -dependencies = [ - "unic-char-property", - "unic-char-range", - "unic-ucd-version", -] - -[[package]] -name = "unic-ucd-version" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" -dependencies = [ - "unic-common", -] - [[package]] name = "unicase" version = "2.7.0" @@ -10242,12 +11940,30 @@ version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7eec5d1121208364f6793f7d2e222bf75a915c19557537745b195b253dd64217" +[[package]] +name = "unicode-id" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1b6def86329695390197b82c1e244a54a131ceb66c996f2088a3876e2ae083f" + +[[package]] +name = "unicode-id-start" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc3882f69607a2ac8cc4de3ee7993d8f68bb06f2974271195065b3bd07f2edea" + [[package]] name = "unicode-ident" version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +[[package]] +name = "unicode-linebreak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" + [[package]] name = "unicode-normalization" version = "0.1.23" @@ -10331,7 +12047,6 @@ dependencies = [ "once_cell", "rustls 0.23.13", "rustls-pki-types", - "socks", "url", "webpki-roots 0.26.5", ] @@ -10354,18 +12069,6 @@ version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" -[[package]] -name = "urlpattern" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70acd30e3aa1450bc2eece896ce2ad0d178e9c079493819301573dae3c37ba6d" -dependencies = [ - "regex", - "serde", - "unic-ucd-ident", - "url", -] - [[package]] name = "utf-8" version = "0.7.6" @@ -10389,6 +12092,17 @@ dependencies = [ "sha1_smol", ] +[[package]] +name = "v_frame" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f32aaa24bacd11e488aa9ba66369c7cd514885742c9fe08cfe85884db3e92b" +dependencies = [ + "aligned-vec", + "num-traits", + "wasm-bindgen", +] + [[package]] name = "valuable" version = "0.1.0" @@ -10419,6 +12133,12 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "vlq" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65dd7eed29412da847b0f78bcec0ac98588165988a8cfe41d4ea1d429f8ccfff" + [[package]] name = "walkdir" version = "2.5.0" @@ -10761,7 +12481,7 @@ dependencies = [ "bitflags 2.6.0", "hashbrown 0.14.5", "indexmap 2.5.0", - "semver", + "semver 1.0.23", "serde", ] @@ -10796,18 +12516,17 @@ dependencies = [ [[package]] name = "webbrowser" -version = "1.0.2" +version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e5f07fb9bc8de2ddfe6b24a71a75430673fd679e568c48b52716cef1cfae923" +checksum = "db67ae75a9405634f5882791678772c94ff5f16a66535aae186e26aa0841fc8b" dependencies = [ - "block2", - "core-foundation 0.10.0", + "core-foundation 0.9.4", "home", "jni", "log", "ndk-context", - "objc2", - "objc2-foundation", + "objc", + "raw-window-handle 0.5.2", "url", "web-sys", ] @@ -10879,10 +12598,10 @@ checksum = "6f61ff3d9d0ee4efcb461b14eb3acfda2702d10dc329f339303fc3e57215ae2c" dependencies = [ "webview2-com-macros", "webview2-com-sys", - "windows", + "windows 0.58.0", "windows-core 0.58.0", - "windows-implement", - "windows-interface", + "windows-implement 0.58.0", + "windows-interface 0.58.0", ] [[package]] @@ -10903,7 +12622,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3a3e2eeb58f82361c93f9777014668eb3d07e7d174ee4c819575a9208011886" dependencies = [ "thiserror", - "windows", + "windows 0.58.0", "windows-core 0.58.0", ] @@ -10989,6 +12708,18 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49" +dependencies = [ + "windows-core 0.54.0", + "windows-implement 0.53.0", + "windows-interface 0.53.0", + "windows-targets 0.52.6", +] + [[package]] name = "windows" version = "0.58.0" @@ -11008,19 +12739,40 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-core" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65" +dependencies = [ + "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", - "windows-interface", + "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.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942ac266be9249c84ca862f0a164a39533dc2f6f33dc98ec89c8da99b82ea0bd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + [[package]] name = "windows-implement" version = "0.58.0" @@ -11034,9 +12786,9 @@ dependencies = [ [[package]] name = "windows-interface" -version = "0.58.0" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +checksum = "da33557140a288fae4e1d5f8873aaf9eb6613a9cf82c3e070223ff177f598b60" dependencies = [ "proc-macro2", "quote", @@ -11044,13 +12796,14 @@ dependencies = [ ] [[package]] -name = "windows-registry" -version = "0.1.2" +name = "windows-interface" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acc134c90a0318d873ec962b13149e9c862ff0d2669082a709a4810167a3c6ee" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ - "windows-result 0.1.2", - "windows-targets 0.52.6", + "proc-macro2", + "quote", + "syn 2.0.77", ] [[package]] @@ -11351,8 +13104,8 @@ checksum = "f4d715cf5fe88e9647f3d17b207b6d060d4a88e7171d4ccb2d2c657dd1d44728" dependencies = [ "base64 0.22.1", "block", - "cocoa", - "core-graphics", + "cocoa 0.26.0", + "core-graphics 0.24.0", "crossbeam-channel", "dpi", "dunce", @@ -11364,7 +13117,7 @@ dependencies = [ "jni", "kuchikiki", "libc", - "ndk", + "ndk 0.9.0", "objc", "objc_id", "once_cell", @@ -11377,7 +13130,7 @@ dependencies = [ "webkit2gtk", "webkit2gtk-sys", "webview2-com", - "windows", + "windows 0.58.0", "windows-core 0.58.0", "windows-version", "x11-dl", @@ -11413,37 +13166,6 @@ dependencies = [ "pkg-config", ] -[[package]] -name = "x25519-dalek" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" -dependencies = [ - "curve25519-dalek", - "rand_core 0.6.4", - "serde", - "zeroize", -] - -[[package]] -name = "x509-certificate" -version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66534846dec7a11d7c50a74b7cdb208b9a581cad890b7866430d438455847c85" -dependencies = [ - "bcder", - "bytes", - "chrono", - "der", - "hex", - "pem", - "ring", - "signature", - "spki", - "thiserror", - "zeroize", -] - [[package]] name = "xattr" version = "1.3.1" @@ -11471,15 +13193,6 @@ version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a5cbf750400958819fb6178eaa83bee5cd9c29a26a40cc241df8c70fdd46984" -[[package]] -name = "xz2" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2" -dependencies = [ - "lzma-sys", -] - [[package]] name = "yansi" version = "1.0.1" @@ -11569,20 +13282,6 @@ name = "zeroize" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] [[package]] name = "zip" @@ -11651,6 +13350,12 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" + [[package]] name = "zune-inflate" version = "0.2.54" @@ -11660,6 +13365,15 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "zune-jpeg" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16099418600b4d8f028622f73ff6e3deaabdff330fb9a2a131dea781ee8b0768" +dependencies = [ + "zune-core", +] + [[package]] name = "zvariant" version = "4.2.0" diff --git a/Cargo.toml b/Cargo.toml index d9f2b56d4f..b408c9d09e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ members = [ "packages/core", "packages/core-types", "packages/cli", - "packages/core-types", + "packages/cli-config", "packages/core-macro", "packages/config-macro", "packages/router-macro", @@ -34,31 +34,6 @@ members = [ "packages/server-macro", "packages/static-generation", "packages/lazy-js-bundle", - "packages/document", - "packages/runtime-config", - "packages/devtools", - "packages/devtools-types", - "packages/isrg", - "packages/manganis-core", - "packages/server", - "packages/rsx-hotreload", - - # Static generation examples - # "packages/static-generation/examples/simple", - # "packages/static-generation/examples/router", - # "packages/static-generation/examples/github-pages", - - # Playwright tests - "packages/playwright-tests/liveview", - "packages/playwright-tests/web", - "packages/playwright-tests/static-generation", - "packages/playwright-tests/fullstack", - "packages/playwright-tests/suspense-carousel", - "packages/playwright-tests/nested-suspense", - - # manganis - "packages/manganis", - "packages/manganis-macro", # Full project examples "example-projects/fullstack-hackernews", @@ -86,6 +61,7 @@ members = [ "packages/playwright-tests/suspense-carousel", "packages/playwright-tests/nested-suspense", ] +exclude = ["examples/mobile_demo", "examples/openid_connect_demo"] [workspace.package] version = "0.6.0-alpha.2" @@ -125,13 +101,14 @@ dioxus-static-site-generation = { path = "packages/static-generation", version = dioxus_server_macro = { path = "packages/server-macro", version = "0.6.0-alpha.0", default-features = false } lazy-js-bundle = { path = "packages/lazy-js-bundle", version = "0.6.0-alpha.0" } +manganis-cli-support = { version = "0.3.0-alpha.1", features = ["html"] } +manganis = { version = "0.3.0-alpha.1", default-features = false, features = ["html", "macro"]} warnings = { version = "0.2.0" } # a fork of pretty please for tests prettier-please = { version = "0.3.0", features = ["verbatim"]} -clap = { version = "4.5.7" } askama_escape = "0.10.3" tracing = "0.1.37" tracing-futures = "0.2.5" @@ -149,8 +126,8 @@ thiserror = "1.0.40" prettyplease = { version = "0.2.20", features = ["verbatim"] } const_format = "0.2.32" cargo_toml = { version = "0.20.3" } -tauri-utils = { version = "=2.0.0-rc.0" } -tauri-bundler = { version = "=2.0.0-rc.0" } +tauri-utils = { version = "=1.5.*" } +tauri-bundler = { version = "=1.4.*" } lru = "0.12.2" async-trait = "0.1.77" axum = "0.7.0" @@ -213,33 +190,8 @@ core-foundation = "0.10.0" objc = { version = "0.2.7", features = ["exception"] } objc_id = "0.1.1" -# cli, cli-config -dirs = "5.0.1" -cargo-config2 = "0.1.26" - -criterion = { version = "0.5" } - -# dirs = "5.0.1" -# cargo-config2 = "0.1.26" -# criterion = { version = "0.5" } - -# # desktop -# wry = { version = "0.43.0", default-features = false } -# tao = { version = "0.30.0", features = ["rwh_05"] } -# webbrowser = "1.0.1" -# infer = "0.16.0" -# dunce = "1.0.2" -# urlencoding = "2.1.2" -# global-hotkey = "0.6.0" -# rfd = { version = "0.14", default-features = false } -# muda = "0.14.0" -# cocoa = "0.26" -# core-foundation = "0.10.0" -# objc = { version = "0.2.7", features = ["exception"] } -# objc_id = "0.1.1" - -# [profile.dev.package.dioxus-core-macro] -# opt-level = 3 +[profile.dev.package.dioxus-core-macro] +opt-level = 3 # wasm bindgen is slooooooow, but it's because we're actually processing the wasm # so, lets just bump up walrus to make it faster, no need for any special profiles @@ -268,10 +220,12 @@ publish = false version = "0.6.0-alpha.2" [dependencies] +manganis = { workspace = true, optional = true } reqwest = { workspace = true, features = ["json"], optional = true } -ciborium = { workspace = true, optional = true } -base64 = { workspace = true, optional = true } http-range = { version = "0.1.5", optional = true } +ciborium = { version = "0.2.1", optional = true } +base64 = { version = "0.21.0", optional = true } +tracing-subscriber = "0.3.17" [dev-dependencies] dioxus = { workspace = true, features = ["router"] } @@ -308,6 +262,7 @@ fullstack = ["dioxus/fullstack"] axum = ["dioxus/axum"] server = ["dioxus/axum"] web = ["dioxus/web"] +collect-assets = ["dep:manganis"] http = ["dep:reqwest", "dep:http-range"] [[example]] diff --git a/packages/cli-config/.gitignore b/packages/cli-config/.gitignore new file mode 100644 index 0000000000..6700b1332d --- /dev/null +++ b/packages/cli-config/.gitignore @@ -0,0 +1,4 @@ +/target +Cargo.lock +.DS_Store +.idea/ diff --git a/packages/config-macro/src/lib.rs b/packages/config-macro/src/lib.rs index c803314dd3..0660539d2b 100644 --- a/packages/config-macro/src/lib.rs +++ b/packages/config-macro/src/lib.rs @@ -27,7 +27,7 @@ macro_rules! define_config_macro { }; } -define_config_macro!(server_only if any(feature = "server", feature = "liveview")); +define_config_macro!(server_only if any(feature = "ssr", feature = "liveview")); define_config_macro!(client if any(feature = "desktop", feature = "web")); define_config_macro!(web if feature = "web"); define_config_macro!(desktop if feature = "desktop"); diff --git a/packages/core-macro/src/props/mod.rs b/packages/core-macro/src/props/mod.rs index 14a0ddcc15..865f86b7ce 100644 --- a/packages/core-macro/src/props/mod.rs +++ b/packages/core-macro/src/props/mod.rs @@ -1015,11 +1015,11 @@ Finally, call `.build()` to create the instance of `{name}`. Ok(quote! { #[allow(dead_code, non_camel_case_types, missing_docs)] impl #impl_generics dioxus_core::prelude::HasAttributes for #builder_name < #( #ty_generics ),* > #where_clause { - fn push_attribute( + fn push_attribute( mut self, name: &'static str, ns: Option<&'static str>, - attr: impl dioxus_core::prelude::IntoAttributeValue, + attr: impl dioxus_core::prelude::IntoAttributeValue, volatile: bool ) -> Self { let ( #(#descructuring,)* ) = self.fields; diff --git a/packages/core-types/src/event.rs b/packages/core-types/src/event.rs deleted file mode 100644 index c8a5ff68a8..0000000000 --- a/packages/core-types/src/event.rs +++ /dev/null @@ -1,203 +0,0 @@ -use std::{any::Any, cell::RefCell, rc::Rc}; - -/// A wrapper around some generic data that handles the event's state -/// -/// -/// Prevent this event from continuing to bubble up the tree to parent elements. -/// -/// # Example -/// -/// ```rust, no_run -/// # use dioxus::prelude::*; -/// rsx! { -/// button { -/// onclick: move |evt: Event| { -/// evt.stop_propagation(); -/// } -/// } -/// }; -/// ``` -pub struct Event { - /// The data associated with this event - pub data: Rc, - pub(crate) metadata: Rc>, -} - -#[derive(Clone, Copy)] -pub(crate) struct EventMetadata { - pub(crate) propagates: bool, - pub(crate) prevent_default: bool, -} - -impl Event { - /// Create a new event from the inner data - pub fn new(data: Rc, propagates: bool) -> Self { - Self { - data, - metadata: Rc::new(RefCell::new(EventMetadata { - propagates, - prevent_default: false, - })), - } - } -} - -impl Event { - /// Map the event data to a new type - /// - /// # Example - /// - /// ```rust, no_run - /// # use dioxus::prelude::*; - /// rsx! { - /// button { - /// onclick: move |evt: MouseEvent| { - /// let data = evt.map(|data| data.client_coordinates()); - /// println!("{:?}", data.data()); - /// } - /// } - /// }; - /// ``` - pub fn map U>(&self, f: F) -> Event { - Event { - data: Rc::new(f(&self.data)), - metadata: self.metadata.clone(), - } - } - - /// Prevent this event from continuing to bubble up the tree to parent elements. - /// - /// # Example - /// - /// ```rust, no_run - /// # use dioxus::prelude::*; - /// rsx! { - /// button { - /// onclick: move |evt: Event| { - /// # #[allow(deprecated)] - /// evt.cancel_bubble(); - /// } - /// } - /// }; - /// ``` - #[deprecated = "use stop_propagation instead"] - pub fn cancel_bubble(&self) { - self.metadata.borrow_mut().propagates = false; - } - - /// Check if the event propagates up the tree to parent elements - pub fn propagates(&self) -> bool { - self.metadata.borrow().propagates - } - - /// Prevent this event from continuing to bubble up the tree to parent elements. - /// - /// # Example - /// - /// ```rust, no_run - /// # use dioxus::prelude::*; - /// rsx! { - /// button { - /// onclick: move |evt: Event| { - /// evt.stop_propagation(); - /// } - /// } - /// }; - /// ``` - pub fn stop_propagation(&self) { - self.metadata.borrow_mut().propagates = false; - } - - /// Get a reference to the inner data from this event - /// - /// ```rust, no_run - /// # use dioxus::prelude::*; - /// rsx! { - /// button { - /// onclick: move |evt: Event| { - /// let data = evt.data(); - /// async move { - /// println!("{:?}", data); - /// } - /// } - /// } - /// }; - /// ``` - pub fn data(&self) -> Rc { - self.data.clone() - } - - /// Prevent the default action of the event. - /// - /// # Example - /// - /// ```rust - /// # use dioxus::prelude::*; - /// fn App() -> Element { - /// rsx! { - /// a { - /// // You can prevent the default action of the event with `prevent_default` - /// onclick: move |event| { - /// event.prevent_default(); - /// }, - /// href: "https://dioxuslabs.com", - /// "don't go to the link" - /// } - /// } - /// } - /// ``` - /// - /// Note: This must be called synchronously when handling the event. Calling it after the event has been handled will have no effect. - /// - ///
      - /// - /// This method is not available on the LiveView renderer because LiveView handles all events over a websocket which cannot block. - /// - ///
      - #[track_caller] - pub fn prevent_default(&self) { - self.metadata.borrow_mut().prevent_default = true; - } - - /// Check if the default action of the event is enabled. - pub fn default_action_enabled(&self) -> bool { - !self.metadata.borrow().prevent_default - } -} - -impl Event { - /// Downcast this `Event`` to a concrete type - pub fn downcast(self) -> Option> { - let data = self.data.downcast::().ok()?; - Some(Event { - metadata: self.metadata.clone(), - data, - }) - } -} - -impl Clone for Event { - fn clone(&self) -> Self { - Self { - metadata: self.metadata.clone(), - data: self.data.clone(), - } - } -} - -impl std::ops::Deref for Event { - type Target = Rc; - fn deref(&self) -> &Self::Target { - &self.data - } -} - -impl std::fmt::Debug for Event { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("UiEvent") - .field("bubble_state", &self.propagates()) - .field("prevent_default", &!self.default_action_enabled()) - .field("data", &self.data) - .finish() - } -} diff --git a/packages/core-types/src/formatter.rs b/packages/core-types/src/formatter.rs index 4048dadfec..a4df243729 100644 --- a/packages/core-types/src/formatter.rs +++ b/packages/core-types/src/formatter.rs @@ -2,38 +2,13 @@ use std::borrow::Cow; /// Take this type and format it into a Cow<'static, str> /// -/// This trait exists so libraries like manganis can implement this type for assets without depending +/// This trait exists so libraries like manganis can implement this type for asssets without depending /// on dioxus-core, which can be heavy in proc macros and build scripts. /// /// We don't want a blanket impl for T: Display because that might conflict for the other integral data /// types of AttributeValue +/// +/// Todo: we might be able to specialize without this just with Display. pub trait DioxusFormattable { fn format(&self) -> Cow<'static, str>; } - -impl DioxusFormattable for &'static str { - fn format(&self) -> Cow<'static, str> { - self.into() - } -} - -impl DioxusFormattable for String { - fn format(&self) -> Cow<'static, str> { - self.into() - } -} - -impl DioxusFormattable for Arguments<'_> { - fn format(&self) -> Cow<'static, str> { - self.to_string().into() - } -} - -/// A marker trait that automatically implements [`DioxusFormattable`] through the display impl -pub trait DioxusFormattableThroughDisplay: Display {} - -impl DioxusFormattable for T { - fn format(&self) -> Cow<'static, str> { - self.to_string().into() - } -} diff --git a/packages/core-types/src/hotreload.rs b/packages/core-types/src/hotreload.rs deleted file mode 100644 index 098328f7fc..0000000000 --- a/packages/core-types/src/hotreload.rs +++ /dev/null @@ -1,480 +0,0 @@ -// use std::{ -// any::{Any, TypeId}, -// hash::{Hash, Hasher}, -// }; - -// #[cfg(feature = "serialize")] -// use crate::nodes::deserialize_string_leaky; -// // use crate::{ -// // Attribute, AttributeValue, DynamicNode, Template, TemplateAttribute, TemplateNode, VNode, VText, -// // }; - -// #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -// #[doc(hidden)] -// #[derive(Debug, PartialEq, Clone)] -// pub struct HotreloadedLiteral { -// pub name: String, -// pub value: HotReloadLiteral, -// } - -// #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -// #[doc(hidden)] -// #[derive(Debug, PartialEq, Clone)] -// pub enum HotReloadLiteral { -// Fmted(FmtedSegments), -// Float(f64), -// Int(i64), -// Bool(bool), -// } - -// impl HotReloadLiteral { -// pub fn as_fmted(&self) -> Option<&FmtedSegments> { -// match self { -// Self::Fmted(segments) => Some(segments), -// _ => None, -// } -// } - -// pub fn as_float(&self) -> Option { -// match self { -// Self::Float(f) => Some(*f), -// _ => None, -// } -// } - -// pub fn as_int(&self) -> Option { -// match self { -// Self::Int(i) => Some(*i), -// _ => None, -// } -// } - -// pub fn as_bool(&self) -> Option { -// match self { -// Self::Bool(b) => Some(*b), -// _ => None, -// } -// } -// } - -// impl Hash for HotReloadLiteral { -// fn hash(&self, state: &mut H) { -// match self { -// Self::Fmted(segments) => segments.hash(state), -// Self::Float(f) => f.to_bits().hash(state), -// Self::Int(i) => i.hash(state), -// Self::Bool(b) => b.hash(state), -// } -// } -// } - -// #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -// #[doc(hidden)] -// #[derive(Debug, PartialEq, Eq, Clone, Hash)] -// pub struct FmtedSegments { -// pub(crate) segments: Vec, -// } - -// impl FmtedSegments { -// pub fn new(segments: Vec) -> Self { -// Self { segments } -// } - -// /// Render the formatted string by stitching together the segments -// pub(crate) fn render_with(&self, dynamic_text: &[String]) -> String { -// let mut out = String::new(); - -// for segment in &self.segments { -// match segment { -// FmtSegment::Literal { value } => out.push_str(value), -// FmtSegment::Dynamic { id } => out.push_str(&dynamic_text[*id]), -// } -// } - -// out -// } -// } - -// type StaticStr = &'static str; - -// #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -// #[doc(hidden)] -// #[derive(Debug, PartialEq, Eq, Clone, Hash)] -// pub enum FmtSegment { -// Literal { -// #[cfg_attr( -// feature = "serialize", -// serde(deserialize_with = "deserialize_string_leaky") -// )] -// value: StaticStr, -// }, -// Dynamic { -// id: usize, -// }, -// } - -// // let __pool = DynamicValuePool::new( -// // vec![...], -// // vec![...], -// // vec![...], -// // ); -// // VNode::new( -// // None, -// // Template { -// // name: "...", -// // roots: &[...], -// // node_paths: &[..], -// // attr_paths: &[...], -// // }, -// // Box::new([...]), -// // Box::new([...]), -// // ) - -// // Open questions: -// // - How do we handle type coercion for different sized component property integers? -// // - Should non-string hot literals go through the centralized pool? -// // - Should formatted strings be a runtime concept? - -// #[doc(hidden)] -// pub struct DynamicLiteralPool { -// dynamic_text: Box<[String]>, -// } - -// impl DynamicLiteralPool { -// pub fn new(dynamic_text: Vec) -> Self { -// Self { -// dynamic_text: dynamic_text.into_boxed_slice(), -// } -// } - -// pub fn get_component_property<'a, T>( -// &self, -// id: usize, -// hot_reload: &'a HotReloadedTemplate, -// f: impl FnOnce(&'a HotReloadLiteral) -> Option, -// ) -> Option { -// f(hot_reload.component_values.get(id)?) -// } - -// /// Get a component property of a specific type at the component property index -// pub fn component_property( -// &mut self, -// id: usize, -// hot_reload: &HotReloadedTemplate, -// // We pass in the original value for better type inference -// // For example, if the original literal is `0i128`, we know the output must be the type `i128` -// _coherse_type: T, -// ) -> T { -// fn assert_type(t: T) -> T2 { -// *(Box::new(t) as Box).downcast::().unwrap() -// } -// let grab_float = || { -// self.get_component_property(id, hot_reload, HotReloadLiteral::as_float).unwrap_or_else(|| { -// tracing::error!("Expected a float component property, because the type was {}. The CLI gave the hot reloading engine a type of {:?}. This is probably caused by a bug in dioxus hot reloading. Please report this issue.", std::any::type_name::(), hot_reload.component_values.get(id)); -// Default::default() - -// }) -// }; -// let grab_int = || { -// self.get_component_property(id, hot_reload, HotReloadLiteral::as_int).unwrap_or_else(|| { -// tracing::error!("Expected a integer component property, because the type was {}. The CLI gave the hot reloading engine a type of {:?}. This is probably caused by a bug in dioxus hot reloading. Please report this issue.", std::any::type_name::(), hot_reload.component_values.get(id)); -// Default::default() -// }) -// }; -// let grab_bool = || { -// self.get_component_property(id, hot_reload, HotReloadLiteral::as_bool).unwrap_or_else(|| { -// tracing::error!("Expected a bool component property, because the type was {}. The CLI gave the hot reloading engine a type of {:?}. This is probably caused by a bug in dioxus hot reloading. Please report this issue.", std::any::type_name::(), hot_reload.component_values.get(id)); -// Default::default() -// }) -// }; -// let grab_fmted = || { -// self.get_component_property(id, hot_reload, |fmted| HotReloadLiteral::as_fmted(fmted).map(|segments| self.render_formatted(segments))).unwrap_or_else(|| { -// tracing::error!("Expected a string component property, because the type was {}. The CLI gave the hot reloading engine a type of {:?}. This is probably caused by a bug in dioxus hot reloading. Please report this issue.", std::any::type_name::(), hot_reload.component_values.get(id)); -// Default::default() -// }) -// }; -// match TypeId::of::() { -// // Any string types that accept a literal -// _ if TypeId::of::() == TypeId::of::() => assert_type(grab_fmted()), -// _ if TypeId::of::<&str>() == TypeId::of::() => { -// assert_type(Box::leak(grab_fmted().into_boxed_str()) as &'static str) -// } -// // Any integer types that accept a literal -// _ if TypeId::of::() == TypeId::of::() => assert_type(grab_int() as i128), -// _ if TypeId::of::() == TypeId::of::() => assert_type(grab_int()), -// _ if TypeId::of::() == TypeId::of::() => assert_type(grab_int() as i32), -// _ if TypeId::of::() == TypeId::of::() => assert_type(grab_int() as i16), -// _ if TypeId::of::() == TypeId::of::() => assert_type(grab_int() as i8), -// _ if TypeId::of::() == TypeId::of::() => assert_type(grab_int() as isize), -// _ if TypeId::of::() == TypeId::of::() => assert_type(grab_int() as u128), -// _ if TypeId::of::() == TypeId::of::() => assert_type(grab_int() as u64), -// _ if TypeId::of::() == TypeId::of::() => assert_type(grab_int() as u32), -// _ if TypeId::of::() == TypeId::of::() => assert_type(grab_int() as u16), -// _ if TypeId::of::() == TypeId::of::() => assert_type(grab_int() as u8), -// _ if TypeId::of::() == TypeId::of::() => assert_type(grab_int() as usize), -// // Any float types that accept a literal -// _ if TypeId::of::() == TypeId::of::() => assert_type(grab_float()), -// _ if TypeId::of::() == TypeId::of::() => assert_type(grab_float() as f32), -// // Any bool types that accept a literal -// _ if TypeId::of::() == TypeId::of::() => assert_type(grab_bool()), -// _ => panic!("Unsupported component property type"), -// } -// } - -// pub fn render_formatted(&self, segments: &FmtedSegments) -> String { -// segments.render_with(&self.dynamic_text) -// } -// } -// #[doc(hidden)] -// pub struct DynamicValuePool { -// dynamic_attributes: Box<[Box<[Attribute]>]>, -// dynamic_nodes: Box<[DynamicNode]>, -// literal_pool: DynamicLiteralPool, -// } - -// impl DynamicValuePool { -// pub fn new( -// dynamic_nodes: Vec, -// dynamic_attributes: Vec>, -// literal_pool: DynamicLiteralPool, -// ) -> Self { -// Self { -// dynamic_attributes: dynamic_attributes.into_boxed_slice(), -// dynamic_nodes: dynamic_nodes.into_boxed_slice(), -// literal_pool, -// } -// } - -// pub fn render_with(&mut self, hot_reload: &HotReloadedTemplate) -> VNode { -// // Get the node_paths from a depth first traversal of the template -// let key = hot_reload -// .key -// .as_ref() -// .map(|key| self.literal_pool.render_formatted(key)); -// let dynamic_nodes = hot_reload -// .dynamic_nodes -// .iter() -// .map(|node| self.render_dynamic_node(node)) -// .collect(); -// let dynamic_attrs = hot_reload -// .dynamic_attributes -// .iter() -// .map(|attr| self.render_attribute(attr)) -// .collect(); - -// VNode::new(key, hot_reload.template, dynamic_nodes, dynamic_attrs) -// } - -// fn render_dynamic_node(&mut self, node: &HotReloadDynamicNode) -> DynamicNode { -// match node { -// // If the node is dynamic, take it from the pool and return it -// HotReloadDynamicNode::Dynamic(id) => self.dynamic_nodes[*id].clone(), -// // Otherwise, format the text node and return it -// HotReloadDynamicNode::Formatted(segments) => DynamicNode::Text(VText { -// value: self.literal_pool.render_formatted(segments), -// }), -// } -// } - -// fn render_attribute(&mut self, attr: &HotReloadDynamicAttribute) -> Box<[Attribute]> { -// match attr { -// HotReloadDynamicAttribute::Dynamic(id) => self.dynamic_attributes[*id].clone(), -// HotReloadDynamicAttribute::Named(NamedAttribute { -// name, -// namespace, -// value, -// }) => Box::new([Attribute { -// name, -// namespace: *namespace, -// value: match value { -// HotReloadAttributeValue::Literal(HotReloadLiteral::Fmted(segments)) => { -// AttributeValue::Text(self.literal_pool.render_formatted(segments)) -// } -// HotReloadAttributeValue::Literal(HotReloadLiteral::Float(f)) => { -// AttributeValue::Float(*f) -// } -// HotReloadAttributeValue::Literal(HotReloadLiteral::Int(i)) => { -// AttributeValue::Int(*i) -// } -// HotReloadAttributeValue::Literal(HotReloadLiteral::Bool(b)) => { -// AttributeValue::Bool(*b) -// } -// HotReloadAttributeValue::Dynamic(id) => { -// self.dynamic_attributes[*id][0].value.clone() -// } -// }, -// volatile: false, -// }]), -// } -// } -// } - -// #[doc(hidden)] -// #[derive(Debug, Clone, PartialEq)] -// #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -// pub struct HotReloadTemplateWithLocation { -// pub location: String, -// pub template: HotReloadedTemplate, -// } - -// type StaticTemplateArray = &'static [TemplateNode]; - -// #[doc(hidden)] -// #[derive(Debug, PartialEq, Clone)] -// #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -// pub struct HotReloadedTemplate { -// pub key: Option, -// pub dynamic_nodes: Vec, -// pub dynamic_attributes: Vec, -// pub component_values: Vec, -// #[cfg_attr( -// feature = "serialize", -// serde(deserialize_with = "crate::nodes::deserialize_leaky") -// )] -// pub roots: StaticTemplateArray, -// /// The template that is computed from the hot reload roots -// template: Template, -// } - -// impl HotReloadedTemplate { -// pub fn new( -// key: Option, -// dynamic_nodes: Vec, -// dynamic_attributes: Vec, -// component_values: Vec, -// roots: &'static [TemplateNode], -// ) -> Self { -// let node_paths = Self::node_paths(roots); -// let attr_paths = Self::attr_paths(roots); - -// let template = Template { -// roots, -// node_paths, -// attr_paths, -// }; -// Self { -// key, -// dynamic_nodes, -// dynamic_attributes, -// component_values, -// roots, -// template, -// } -// } - -// fn node_paths(roots: &'static [TemplateNode]) -> &'static [&'static [u8]] { -// fn add_node_paths( -// roots: &[TemplateNode], -// node_paths: &mut Vec<&'static [u8]>, -// current_path: Vec, -// ) { -// for (idx, node) in roots.iter().enumerate() { -// let mut path = current_path.clone(); -// path.push(idx as u8); -// match node { -// TemplateNode::Element { children, .. } => { -// add_node_paths(children, node_paths, path); -// } -// TemplateNode::Text { .. } => {} -// TemplateNode::Dynamic { id } => { -// debug_assert_eq!(node_paths.len(), *id); -// node_paths.push(Box::leak(path.into_boxed_slice())); -// } -// } -// } -// } - -// let mut node_paths = Vec::new(); -// add_node_paths(roots, &mut node_paths, Vec::new()); -// let leaked: &'static [&'static [u8]] = Box::leak(node_paths.into_boxed_slice()); -// leaked -// } - -// fn attr_paths(roots: &'static [TemplateNode]) -> &'static [&'static [u8]] { -// fn add_attr_paths( -// roots: &[TemplateNode], -// attr_paths: &mut Vec<&'static [u8]>, -// current_path: Vec, -// ) { -// for (idx, node) in roots.iter().enumerate() { -// let mut path = current_path.clone(); -// path.push(idx as u8); -// if let TemplateNode::Element { -// children, attrs, .. -// } = node -// { -// for attr in *attrs { -// if let TemplateAttribute::Dynamic { id } = attr { -// debug_assert_eq!(attr_paths.len(), *id); -// attr_paths.push(Box::leak(path.clone().into_boxed_slice())); -// } -// } -// add_attr_paths(children, attr_paths, path); -// } -// } -// } - -// let mut attr_paths = Vec::new(); -// add_attr_paths(roots, &mut attr_paths, Vec::new()); -// let leaked: &'static [&'static [u8]] = Box::leak(attr_paths.into_boxed_slice()); -// leaked -// } -// } - -// #[doc(hidden)] -// #[derive(Debug, PartialEq, Clone, Hash)] -// #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -// pub enum HotReloadDynamicNode { -// Dynamic(usize), -// Formatted(FmtedSegments), -// } - -// #[doc(hidden)] -// #[derive(Debug, PartialEq, Clone, Hash)] -// #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -// pub enum HotReloadDynamicAttribute { -// Dynamic(usize), -// Named(NamedAttribute), -// } - -// #[doc(hidden)] -// #[derive(Debug, PartialEq, Clone, Hash)] -// #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -// pub struct NamedAttribute { -// /// The name of this attribute. -// #[cfg_attr( -// feature = "serialize", -// serde(deserialize_with = "crate::nodes::deserialize_string_leaky") -// )] -// name: StaticStr, -// /// The namespace of this attribute. Does not exist in the HTML spec -// #[cfg_attr( -// feature = "serialize", -// serde(deserialize_with = "crate::nodes::deserialize_option_leaky") -// )] -// namespace: Option, - -// value: HotReloadAttributeValue, -// } - -// impl NamedAttribute { -// pub fn new( -// name: &'static str, -// namespace: Option<&'static str>, -// value: HotReloadAttributeValue, -// ) -> Self { -// Self { -// name, -// namespace, -// value, -// } -// } -// } - -// #[doc(hidden)] -// #[derive(Debug, PartialEq, Clone, Hash)] -// #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -// pub enum HotReloadAttributeValue { -// Literal(HotReloadLiteral), -// Dynamic(usize), -// } diff --git a/packages/core-types/src/lib.rs b/packages/core-types/src/lib.rs index 956ca51a7c..25fa11493c 100644 --- a/packages/core-types/src/lib.rs +++ b/packages/core-types/src/lib.rs @@ -1,15 +1,5 @@ -pub mod attributes; pub mod bubbles; -mod core; -mod event; -pub mod formatter; -mod hotreload; pub mod hr_context; -pub use attributes::*; pub use bubbles::*; -pub use core::*; -pub use event::*; -pub use formatter::*; -pub use hotreload::*; pub use hr_context::*; diff --git a/packages/core/Cargo.toml b/packages/core/Cargo.toml index 9ba11ad466..ec35fda571 100644 --- a/packages/core/Cargo.toml +++ b/packages/core/Cargo.toml @@ -10,37 +10,45 @@ homepage = "https://dioxuslabs.com" keywords = ["dom", "ui", "gui", "react"] [dependencies] -dioxus-core-types = { workspace = true } -const_format = { workspace = true } -futures-channel = { workspace = true } -generational-box = { workspace = true } -longest-increasing-subsequence = { workspace = true } rustc-hash = { workspace = true } -rustversion = { workspace = true } +longest-increasing-subsequence = { workspace = true } +futures-util = { workspace = true, default-features = false, features = [ + "alloc", + "std", +] } slab = { workspace = true } slotmap = { workspace = true } +futures-channel = { workspace = true } tracing = { workspace = true } +serde = {workspace = true, optional = true, features = ["derive"] } +generational-box = { workspace = true } +rustversion = { workspace = true } +const_format = { workspace = true } warnings = { workspace = true } -futures-util = { workspace = true, default-features = false, features = ["alloc", "std"] } -serde = { workspace = true, optional = true, features = ["derive"] } +manganis = { workspace = true, default-features = false, optional = true } [dev-dependencies] +tokio = { workspace = true, features = ["full"] } +tracing-fluent-assertions = "0.3.0" dioxus = { workspace = true } -dioxus-ssr = { workspace = true } dioxus-html = { workspace = true, features = ["serialize"] } -tokio = { workspace = true, features = ["full"] } +pretty_assertions = "1.3.0" rand = { workspace = true } -reqwest = { workspace = true } +dioxus-ssr = { workspace = true } +reqwest = { workspace = true} tracing-subscriber = { workspace = true } -tracing-fluent-assertions = "0.3.0" -pretty_assertions = "1.3.0" [dev-dependencies.web-sys] version = "0.3.56" -features = ["Document", "HtmlElement", "Window"] +features = [ + "Document", + "HtmlElement", + "Window" +] [features] serialize = ["dep:serde"] +manganis = ["dep:manganis"] [package.metadata.docs.rs] cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"] diff --git a/packages/core/src/events.rs b/packages/core/src/events.rs index 63f3f96d04..486d60cf12 100644 --- a/packages/core/src/events.rs +++ b/packages/core/src/events.rs @@ -2,6 +2,197 @@ use crate::{properties::SuperFrom, runtime::RuntimeGuard, Runtime, ScopeId}; use generational_box::GenerationalBox; use std::{cell::RefCell, marker::PhantomData, rc::Rc}; +/// A wrapper around some generic data that handles the event's state +/// +/// +/// Prevent this event from continuing to bubble up the tree to parent elements. +/// +/// # Example +/// +/// ```rust, no_run +/// # use dioxus::prelude::*; +/// rsx! { +/// button { +/// onclick: move |evt: Event| { +/// evt.stop_propagation(); +/// } +/// } +/// }; +/// ``` +pub struct Event { + /// The data associated with this event + pub data: Rc, + pub(crate) metadata: Rc>, +} + +#[derive(Clone, Copy)] +pub(crate) struct EventMetadata { + pub(crate) propagates: bool, + pub(crate) prevent_default: bool, +} + +impl Event { + /// Create a new event from the inner data + pub fn new(data: Rc, propagates: bool) -> Self { + Self { + data, + metadata: Rc::new(RefCell::new(EventMetadata { + propagates, + prevent_default: false, + })), + } + } +} + +impl Event { + /// Map the event data to a new type + /// + /// # Example + /// + /// ```rust, no_run + /// # use dioxus::prelude::*; + /// rsx! { + /// button { + /// onclick: move |evt: MouseEvent| { + /// let data = evt.map(|data| data.client_coordinates()); + /// println!("{:?}", data.data()); + /// } + /// } + /// }; + /// ``` + pub fn map U>(&self, f: F) -> Event { + Event { + data: Rc::new(f(&self.data)), + metadata: self.metadata.clone(), + } + } + + /// Prevent this event from continuing to bubble up the tree to parent elements. + /// + /// # Example + /// + /// ```rust, no_run + /// # use dioxus::prelude::*; + /// rsx! { + /// button { + /// onclick: move |evt: Event| { + /// # #[allow(deprecated)] + /// evt.cancel_bubble(); + /// } + /// } + /// }; + /// ``` + #[deprecated = "use stop_propagation instead"] + pub fn cancel_bubble(&self) { + self.metadata.borrow_mut().propagates = false; + } + + /// Check if the event propagates up the tree to parent elements + pub fn propagates(&self) -> bool { + self.metadata.borrow().propagates + } + + /// Prevent this event from continuing to bubble up the tree to parent elements. + /// + /// # Example + /// + /// ```rust, no_run + /// # use dioxus::prelude::*; + /// rsx! { + /// button { + /// onclick: move |evt: Event| { + /// evt.stop_propagation(); + /// } + /// } + /// }; + /// ``` + pub fn stop_propagation(&self) { + self.metadata.borrow_mut().propagates = false; + } + + /// Get a reference to the inner data from this event + /// + /// ```rust, no_run + /// # use dioxus::prelude::*; + /// rsx! { + /// button { + /// onclick: move |evt: Event| { + /// let data = evt.data(); + /// async move { + /// println!("{:?}", data); + /// } + /// } + /// } + /// }; + /// ``` + pub fn data(&self) -> Rc { + self.data.clone() + } + + /// Prevent the default action of the event. + /// + /// # Example + /// + /// ```rust + /// # use dioxus::prelude::*; + /// fn App() -> Element { + /// rsx! { + /// a { + /// // You can prevent the default action of the event with `prevent_default` + /// onclick: move |event| { + /// event.prevent_default(); + /// }, + /// href: "https://dioxuslabs.com", + /// "don't go to the link" + /// } + /// } + /// } + /// ``` + /// + /// Note: This must be called synchronously when handling the event. Calling it after the event has been handled will have no effect. + /// + ///
      + /// + /// This method is not available on the LiveView renderer because LiveView handles all events over a websocket which cannot block. + /// + ///
      + #[track_caller] + pub fn prevent_default(&self) { + self.metadata.borrow_mut().prevent_default = true; + } + + /// Check if the default action of the event is enabled. + pub fn default_action_enabled(&self) -> bool { + !self.metadata.borrow().prevent_default + } +} + +impl Clone for Event { + fn clone(&self) -> Self { + Self { + metadata: self.metadata.clone(), + data: self.data.clone(), + } + } +} + +impl std::ops::Deref for Event { + type Target = Rc; + fn deref(&self) -> &Self::Target { + &self.data + } +} + +impl std::fmt::Debug for Event { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("UiEvent") + .field("bubble_state", &self.propagates()) + .field("prevent_default", &!self.default_action_enabled()) + .field("data", &self.data) + .finish() + } +} + /// The callback type generated by the `rsx!` macro when an `on` field is specified for components. /// /// This makes it possible to pass `move |evt| {}` style closures into components as property fields. diff --git a/packages/core/src/hotreload_utils.rs b/packages/core/src/hotreload_utils.rs index e94ce0e505..8527c170e6 100644 --- a/packages/core/src/hotreload_utils.rs +++ b/packages/core/src/hotreload_utils.rs @@ -113,23 +113,23 @@ pub enum FmtSegment { }, } -// let __pool = DynamicValuePool::new( -// vec![...], -// vec![...], -// vec![...], -// ); -// VNode::new( -// None, -// Template { -// name: "...", -// roots: &[...], -// node_paths: &[..], -// attr_paths: &[...], -// }, -// Box::new([...]), -// Box::new([...]), -// ) - +/// let __pool = DynamicValuePool::new( +/// vec![...], +/// vec![...], +/// vec![...], +/// ); +/// VNode::new( +/// None, +/// Template { +/// name: "...", +/// roots: &[...], +/// node_paths: &[..], +/// attr_paths: &[...], +/// }, +/// Box::new([...]), +/// Box::new([...]), +/// ) +/// // Open questions: // - How do we handle type coercion for different sized component property integers? // - Should non-string hot literals go through the centralized pool? diff --git a/packages/core/src/lib.rs b/packages/core/src/lib.rs index c8fc9a7645..4f2c747c13 100644 --- a/packages/core/src/lib.rs +++ b/packages/core/src/lib.rs @@ -62,7 +62,6 @@ pub(crate) mod innerlude { pub use crate::suspense::*; pub use crate::tasks::*; pub use crate::virtual_dom::*; - pub use dioxus_core_types::*; /// An [`Element`] is a possibly-none [`VNode`] created by calling `render` on [`ScopeId`] or [`ScopeState`]. /// diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index ef8dcf25c7..aec3c08ca1 100644 --- a/packages/core/src/nodes.rs +++ b/packages/core/src/nodes.rs @@ -1,5 +1,3 @@ -use dioxus_core_types::DioxusFormattable; - use crate::innerlude::VProps; use crate::prelude::RenderError; use crate::{any_props::BoxedAnyProps, innerlude::ScopeState}; @@ -9,6 +7,7 @@ use crate::{ properties::ComponentFunction, }; use crate::{Properties, ScopeId, VirtualDom}; +use std::ops::Deref; use std::rc::Rc; use std::vec; use std::{ @@ -16,7 +15,6 @@ use std::{ cell::Cell, fmt::{Arguments, Debug}, }; -use std::{fmt::Display, ops::Deref}; /// The information about the #[derive(Debug)] @@ -520,6 +518,13 @@ pub enum DynamicNode { Fragment(Vec), } +impl DynamicNode { + /// Convert any item that implements [`IntoDynNode`] into a [`DynamicNode`] + pub fn make_node<'c, I>(into: impl IntoDynNode + 'c) -> DynamicNode { + into.into_dyn_node() + } +} + impl Default for DynamicNode { fn default() -> Self { Self::Placeholder(Default::default()) @@ -717,9 +722,9 @@ impl Attribute { /// /// "Volatile" refers to whether or not Dioxus should always override the value. This helps prevent the UI in /// some renderers stay in sync with the VirtualDom's understanding of the world - pub fn new( + pub fn new( name: &'static str, - value: impl IntoAttributeValue, + value: impl IntoAttributeValue, namespace: Option<&'static str>, volatile: bool, ) -> Attribute { @@ -768,7 +773,11 @@ impl AttributeValue { // TODO: maybe don't use the copy-variant of EventHandler here? // Maybe, create an Owned variant so we are less likely to run into leaks AttributeValue::Listener(EventHandler::leak(move |event: Event| { - callback(event.downcast::().unwrap()); + let data = event.data.downcast::().unwrap(); + callback(Event { + metadata: event.metadata.clone(), + data, + }); })) } @@ -853,7 +862,14 @@ impl IntoDynNode for DynamicNode { self } } - +impl IntoDynNode for Option { + fn into_dyn_node(self) -> DynamicNode { + match self { + Some(val) => val.into_dyn_node(), + None => DynamicNode::default(), + } + } +} impl IntoDynNode for &Element { fn into_dyn_node(self) -> DynamicNode { match self.as_ref() { @@ -878,44 +894,25 @@ impl IntoDynNode for &Option { } } } -pub struct DisplayMarker; -impl IntoDynNode for T -where - T: Display, -{ +impl IntoDynNode for &str { fn into_dyn_node(self) -> DynamicNode { DynamicNode::Text(VText { value: self.to_string(), }) } } - -impl IntoDynNode for Option { +impl IntoDynNode for String { fn into_dyn_node(self) -> DynamicNode { - match self { - Some(val) => val.into_dyn_node(), - None => DynamicNode::default(), - } + DynamicNode::Text(VText { value: self }) } } - -pub struct FromNodeIterator; -impl IntoDynNode for T -where - T: Iterator, - I: IntoVNode, -{ +impl IntoDynNode for Arguments<'_> { fn into_dyn_node(self) -> DynamicNode { - let children: Vec<_> = self.into_iter().map(|node| node.into_vnode()).collect(); - - if children.is_empty() { - DynamicNode::default() - } else { - DynamicNode::Fragment(children) - } + DynamicNode::Text(VText { + value: self.to_string(), + }) } } - impl IntoDynNode for &VNode { fn into_dyn_node(self) -> DynamicNode { DynamicNode::Fragment(vec![self.clone()]) @@ -984,8 +981,26 @@ impl IntoVNode for &Option { } } +// Note that we're using the E as a generic but this is never crafted anyways. +pub struct FromNodeIterator; +impl IntoDynNode for T +where + T: Iterator, + I: IntoVNode, +{ + fn into_dyn_node(self) -> DynamicNode { + let children: Vec<_> = self.into_iter().map(|node| node.into_vnode()).collect(); + + if children.is_empty() { + DynamicNode::default() + } else { + DynamicNode::Fragment(children) + } + } +} + /// A value that can be converted into an attribute value -pub trait IntoAttributeValue { +pub trait IntoAttributeValue { /// Convert into an attribute value fn into_value(self) -> AttributeValue; } @@ -1063,21 +1078,21 @@ impl IntoAttributeValue for Option { } } -pub struct DioxusFormattableMarker; -impl IntoAttributeValue for T { +#[cfg(feature = "manganis")] +impl IntoAttributeValue for manganis::ImageAsset { fn into_value(self) -> AttributeValue { - AttributeValue::Text(self.format().into_owned()) + AttributeValue::Text(self.path().to_string()) } } /// A trait for anything that has a dynamic list of attributes pub trait HasAttributes { /// Push an attribute onto the list of attributes - fn push_attribute( + fn push_attribute( self, name: &'static str, ns: Option<&'static str>, - attr: impl IntoAttributeValue, + attr: impl IntoAttributeValue, volatile: bool, ) -> Self; } diff --git a/packages/core/src/runtime.rs b/packages/core/src/runtime.rs index 9c7d2cc98b..b1f0f0f36e 100644 --- a/packages/core/src/runtime.rs +++ b/packages/core/src/runtime.rs @@ -107,13 +107,6 @@ impl Runtime { } } - /// Mark a scope as dirty. This will cause it to re-render. - pub fn mark_dirty(&self, id: ScopeId) { - self.sender - .unbounded_send(SchedulerMsg::Immediate(id)) - .unwrap(); - } - /// Create a scope context. This slab is synchronized with the scope slab. pub(crate) fn create_scope(&self, context: Scope) { let id = context.id; @@ -384,7 +377,9 @@ impl Runtime { self.rendering.set(false); listener.call(uievent.clone()); self.rendering.set(true); - if !uievent.propagates() { + let metadata = uievent.metadata.borrow(); + + if !metadata.propagates { return; } } diff --git a/packages/core/tests/suspense.rs b/packages/core/tests/suspense.rs index cfabe841fe..f2775ab70d 100644 --- a/packages/core/tests/suspense.rs +++ b/packages/core/tests/suspense.rs @@ -517,7 +517,7 @@ fn nested_suspense_resolves_client() { let title = use_resource(move || async_content(0)).suspend()?(); rsx! { - document::Title { "{title.title}" } + Title { "{title.title}" } } } diff --git a/packages/desktop/Cargo.toml b/packages/desktop/Cargo.toml index bd163b6faa..08f281caf1 100644 --- a/packages/desktop/Cargo.toml +++ b/packages/desktop/Cargo.toml @@ -17,7 +17,6 @@ dioxus-html = { workspace = true, features = [ "file_engine", "document", ] } -dioxus-document = { workspace = true } dioxus-signals = { workspace = true, optional = true } dioxus-interpreter-js = { workspace = true, features = ["binary-protocol", "serialize"] } dioxus-cli-config = { workspace = true } @@ -29,8 +28,7 @@ serde = "1.0.136" serde_json = "1.0.79" thiserror = { workspace = true } tracing = { workspace = true } -tao = { workspace = true, features = ["rwh_05"] } -wry = { workspace = true, default-features = false, features = [ +wry = { version = "0.43.0", default-features = false, features = [ "os-webview", "protocol", "drag-drop", @@ -43,20 +41,18 @@ tokio = { workspace = true, features = [ "time", "macros", "fs", - "io-util", ], optional = true } +webbrowser = "0.8.0" +infer = "0.11.0" +dunce = "1.0.2" slab = { workspace = true } rustc-hash = { workspace = true } dioxus-hooks = { workspace = true } futures-util = { workspace = true } +urlencoding = "2.1.2" +async-trait = "0.1.68" +tao = { version = "0.26.1", features = ["rwh_05"] } -webbrowser = { workspace = true } -infer = { workspace = true } -dunce = { workspace = true } -urlencoding = { workspace = true } -async-trait = { workspace = true } -serde = { workspace = true } -serde_json = { workspace = true } [target.'cfg(unix)'.dependencies] signal-hook = "0.3.17" @@ -70,15 +66,27 @@ wry = { version = "0.43.0", features = [ ] } [target.'cfg(any(target_os = "windows",target_os = "macos",target_os = "linux",target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))'.dependencies] -global-hotkey = { workspace = true } -rfd = { workspace = true, default-features = false, features = ["xdg-portal", "tokio"] } -muda = { workspace = true } +global-hotkey = "0.5.0" +rfd = { version = "0.14", default-features = false, features = ["xdg-portal", "tokio"] } +muda = "0.11.3" -[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies] -cocoa = { workspace = true } -core-foundation = { workspace = true } -objc = { workspace = true } -objc_id = { workspace = true } + +[target.'cfg(target_os = "ios")'.dependencies] +objc = "0.2.7" +objc_id = "0.1.1" + +# use rustls on android +[target.'cfg(target_os = "android")'.dependencies] +tokio-tungstenite = { workspace = true, optional = true, features = ["rustls"]} + +# use native tls on other platforms +[target.'cfg(not(target_os = "android"))'.dependencies] +tokio-tungstenite = { workspace = true, optional = true, features = ["native-tls"]} + +[target.'cfg(target_os = "macos")'.dependencies] +cocoa = "0.25" +core-foundation = "0.9.3" +objc = "0.2.7" [build-dependencies] lazy-js-bundle = { workspace = true } @@ -108,10 +116,10 @@ rustdoc-args = [ "--cfg", "docsrs" ] [dev-dependencies] dioxus = { workspace = true, features = ["desktop"] } -reqwest = { workspace = true, features = ["json"] } -dioxus-ssr = { workspace = true, default-features = false } exitcode = "1.1.2" +reqwest = { workspace = true, features = ["json"] } http-range = { version = "0.1.5" } +dioxus-ssr = { workspace = true, default-features = false } separator = "0.4.1" rand = { version = "0.8.4", features = ["small_rng"] } diff --git a/packages/desktop/build.rs b/packages/desktop/build.rs index ff73657cb1..db08a8f551 100644 --- a/packages/desktop/build.rs +++ b/packages/desktop/build.rs @@ -1,11 +1,5 @@ use std::{io::Write as _, path::PathBuf}; -fn main() { - check_gnu(); - - maybe_copy_doc_scrope(); -} - fn check_gnu() { // WARN about wry support on windows gnu targets. GNU windows targets don't work well in wry currently if std::env::var("CARGO_CFG_WINDOWS").is_ok() @@ -14,10 +8,7 @@ fn check_gnu() { { println!("cargo:warning=GNU windows targets have some limitations within Wry. Using the MSVC windows toolchain is recommended. If you would like to use continue using GNU, you can read https://github.com/wravery/webview2-rs#cross-compilation and disable this warning by adding the gnu feature to dioxus-desktop in your Cargo.toml") } -} -// todo: maybe we don't want to do this in this build script -fn maybe_copy_doc_scrope() { // To prepare for a release, we add extra examples to desktop for doc scraping and copy assets from the workspace to make those examples compile if option_env!("DIOXUS_RELEASE").is_some() { // Append EXAMPLES_TOML to the cargo.toml diff --git a/packages/desktop/headless_tests/eval.rs b/packages/desktop/headless_tests/eval.rs index c23eac17bb..4e9c9df795 100644 --- a/packages/desktop/headless_tests/eval.rs +++ b/packages/desktop/headless_tests/eval.rs @@ -16,24 +16,23 @@ static EVALS_RETURNED: GlobalSignal = Signal::global(|| 0); fn app() -> Element { // Double 100 values in the value use_future(|| async { - let mut eval = document::eval( + let mut eval = eval( r#"for (let i = 0; i < 100; i++) { let value = await dioxus.recv(); dioxus.send(value*2); }"#, ); - todo!("Fix eval tests") - // for i in 0..100 { - // eval.send(serde_json::Value::from(i)).unwrap(); - // let value = eval.recv().await.unwrap(); - // assert_eq!(value, serde_json::Value::from(i * 2)); - // EVALS_RECEIVED.with_mut(|x| *x += 1); - // } + for i in 0..100 { + eval.send(serde_json::Value::from(i)).unwrap(); + let value = eval.recv().await.unwrap(); + assert_eq!(value, serde_json::Value::from(i * 2)); + EVALS_RECEIVED.with_mut(|x| *x += 1); + } }); // Make sure returning no value resolves the future use_future(|| async { - let eval = document::eval(r#"return;"#); + let eval = eval(r#"return;"#); eval.await.unwrap(); EVALS_RETURNED.with_mut(|x| *x += 1); @@ -41,18 +40,17 @@ fn app() -> Element { // Return a value from the future use_future(|| async { - let eval = document::eval( + let eval = eval( r#" return [1, 2, 3]; "#, ); - todo!() - // assert_eq!( - // Vec::::deserialize(&eval.await.unwrap()).unwrap(), - // vec![1, 2, 3] - // ); - // EVALS_RETURNED.with_mut(|x| *x += 1); + assert_eq!( + Vec::::deserialize(&eval.await.unwrap()).unwrap(), + vec![1, 2, 3] + ); + EVALS_RETURNED.with_mut(|x| *x += 1); }); use_memo(|| { diff --git a/packages/desktop/headless_tests/rendering.rs b/packages/desktop/headless_tests/rendering.rs index cb67e7bec6..ace615a020 100644 --- a/packages/desktop/headless_tests/rendering.rs +++ b/packages/desktop/headless_tests/rendering.rs @@ -16,7 +16,7 @@ fn use_inner_html(id: &'static str) -> Option { spawn(async move { tokio::time::sleep(std::time::Duration::from_millis(500)).await; - let res = document::eval(&format!( + let res = eval(&format!( r#"let element = document.getElementById('{}'); return element.innerHTML"#, id diff --git a/packages/desktop/headless_tests/utils.rs b/packages/desktop/headless_tests/utils.rs index 04e6b59ba5..1f15cdd536 100644 --- a/packages/desktop/headless_tests/utils.rs +++ b/packages/desktop/headless_tests/utils.rs @@ -1,6 +1,7 @@ #![allow(unused)] // for whatever reason, the compiler is not recognizing the use of these functions use dioxus::prelude::*; +use dioxus_core::Element; pub fn check_app_exits(app: fn() -> Element) { use dioxus_desktop::tao::window::WindowBuilder; @@ -16,7 +17,7 @@ pub fn check_app_exits(app: fn() -> Element) { } }); - dioxus::launch::builder() + LaunchBuilder::desktop() .with_cfg(Config::new().with_window(WindowBuilder::new().with_visible(false))) .launch(app); @@ -49,7 +50,7 @@ pub fn mock_event_with_extra(id: &'static str, value: &'static str, extra: &'sta "# ); - document::eval(&js).await.unwrap(); + eval(&js).await.unwrap(); }); }) } diff --git a/packages/desktop/src/app.rs b/packages/desktop/src/app.rs index 24484c5519..830891cda8 100644 --- a/packages/desktop/src/app.rs +++ b/packages/desktop/src/app.rs @@ -3,6 +3,7 @@ use crate::{ event_handlers::WindowEventHandlers, file_upload::{DesktopFileUploadForm, FileDialogRequest, NativeFileEngine}, ipc::{IpcMessage, UserWindowEvent}, + query::QueryResult, shortcut::ShortcutRegistry, webview::WebviewInstance, }; diff --git a/packages/desktop/src/assets.rs b/packages/desktop/src/assets.rs index 3297cf379f..a02630811f 100644 --- a/packages/desktop/src/assets.rs +++ b/packages/desktop/src/assets.rs @@ -1,4 +1,4 @@ -use dioxus_core::prelude::Callback; +use dioxus_core::prelude::{Runtime, ScopeId}; use rustc_hash::FxHashMap; use std::{cell::RefCell, rc::Rc}; use wry::{http::Request, RequestAsyncResponder}; @@ -7,17 +7,20 @@ use wry::{http::Request, RequestAsyncResponder}; pub type AssetRequest = Request>; pub struct AssetHandler { - f: Callback<(AssetRequest, RequestAsyncResponder)>, + f: Box, + scope: ScopeId, } #[derive(Clone)] -pub struct AssetHandlers { +pub struct AssetHandlerRegistry { + dom_rt: Rc, handlers: Rc>>, } -impl AssetHandlers { - pub fn new() -> Self { - AssetHandlers { +impl AssetHandlerRegistry { + pub fn new(dom_rt: Rc) -> Self { + AssetHandlerRegistry { + dom_rt, handlers: Default::default(), } } @@ -33,16 +36,21 @@ impl AssetHandlers { responder: RequestAsyncResponder, ) { if let Some(handler) = self.handlers.borrow().get(name) { - handler.f.call((request, responder)); + // And run the handler in the scope of the component that created it + self.dom_rt + .on_scope(handler.scope, || (handler.f)(request, responder)); } } pub fn register_handler( &self, name: String, - f: Callback<(AssetRequest, RequestAsyncResponder)>, + f: Box, + scope: ScopeId, ) { - self.handlers.borrow_mut().insert(name, AssetHandler { f }); + self.handlers + .borrow_mut() + .insert(name, AssetHandler { f, scope }); } pub fn remove_handler(&self, name: &str) -> Option { diff --git a/packages/desktop/src/desktop_context.rs b/packages/desktop/src/desktop_context.rs index 3b1c94df3a..415f4b73a1 100644 --- a/packages/desktop/src/desktop_context.rs +++ b/packages/desktop/src/desktop_context.rs @@ -1,21 +1,24 @@ use crate::{ app::SharedContext, - assets::AssetHandlers, + assets::AssetHandlerRegistry, file_upload::NativeFileHover, ipc::UserWindowEvent, + query::QueryEngine, shortcut::{HotKey, ShortcutHandle, ShortcutRegistryError}, webview::WebviewInstance, - Config, WryEventHandler, + AssetRequest, Config, WryEventHandler, +}; +use dioxus_core::{ + prelude::{current_scope_id, ScopeId}, + VirtualDom, }; -use dioxus_core::{prelude::ScopeId, VirtualDom}; -use dioxus_document::Eval; use std::rc::{Rc, Weak}; use tao::{ event::Event, event_loop::EventLoopWindowTarget, window::{Fullscreen as WryFullscreen, Window, WindowId}, }; -use wry::WebView; +use wry::{RequestAsyncResponder, WebView}; #[cfg(target_os = "ios")] use tao::platform::ios::WindowExtIOS; @@ -53,7 +56,9 @@ pub struct DesktopService { pub(crate) shared: Rc, - pub(crate) asset_handlers: AssetHandlers, + /// The receiver for queries about the current window + pub(super) query: QueryEngine, + pub(crate) asset_handlers: AssetHandlerRegistry, pub(crate) file_hover: NativeFileHover, #[cfg(target_os = "ios")] @@ -74,7 +79,7 @@ impl DesktopService { webview: WebView, window: Window, shared: Rc, - asset_handlers: AssetHandlers, + asset_handlers: AssetHandlerRegistry, file_hover: NativeFileHover, ) -> Self { Self { @@ -83,6 +88,7 @@ impl DesktopService { shared, asset_handlers, file_hover, + query: Default::default(), #[cfg(target_os = "ios")] views: Default::default(), } @@ -220,9 +226,31 @@ impl DesktopService { self.shared.shortcut_manager.remove_all() } - /// Eval a javascript string into the current document - pub fn eval(&self, js: String) -> Eval { - dioxus_document::Document::eval(self, js) + /// Provide a callback to handle asset loading yourself. + /// If the ScopeId isn't provided, defaults to a global handler. + /// Note that the handler is namespaced by name, not ScopeId. + /// + /// When the component is dropped, the handler is removed. + /// + /// See [`use_asset_handle`](crate::use_asset_handle) for a convenient hook. + pub fn register_asset_handler( + &self, + name: String, + handler: Box, + scope: Option, + ) { + self.asset_handlers.register_handler( + name, + handler, + scope.unwrap_or(current_scope_id().unwrap_or(ScopeId(0))), + ) + } + + /// Removes an asset handler by its identifier. + /// + /// Returns `None` if the handler did not exist. + pub fn remove_asset_handler(&self, name: &str) -> Option<()> { + self.asset_handlers.remove_handler(name).map(|_| ()) } /// Push an objc view to the window diff --git a/packages/desktop/src/document.rs b/packages/desktop/src/document.rs index dfab2a6811..26c724ff4b 100644 --- a/packages/desktop/src/document.rs +++ b/packages/desktop/src/document.rs @@ -22,83 +22,61 @@ impl Document for DesktopDocument { DesktopEvaluator::create(self.desktop_ctx.clone(), js) } -impl Document for DesktopService { fn set_title(&self, title: String) { - self.window.set_title(&title); + self.desktop_ctx.window.set_title(&title); } - fn eval(&self, js: String) -> Eval { - let (tx, eval) = Eval::from_parts(); - - // Dumb wry has a signature of Fn instead of FnOnce, meaning we need to put the callback in a closure - // that uses rwlock + option to make sure we don't run the callback twice - let tx = Arc::new(Mutex::new(Some(tx))); - let callback = { - let tx = tx.clone(); - move |res: String| { - if let Ok(res) = serde_json::from_str(&res) { - if let Some(tx) = tx.lock().unwrap().take() { - let _ = tx.send(Ok(res)); - } - } else { - tracing::error!("Failed to deserialize eval result: {res:?}"); - } - } - }; - - let res = self.webview.evaluate_script_with_callback(&js, callback); - if let Err(err) = res { - _ = tx - .lock() - .unwrap() - .take() - .unwrap() - .send(Err(EvalError::Communication(err.to_string()))); - } - - eval + fn as_any(&self) -> &dyn std::any::Any { + self } +} - fn create_head_element( - &self, - name: &str, - attributes: Vec<(&str, String)>, - contents: Option, - ) { - let contents = contents.unwrap_or_default(); - let attr_iter = attributes - .into_iter() - .map(|(name, value)| format!(r#"element.setAttribute("{name}", "{value}");"#)) - .collect::>() - .join(""); +/// Represents a desktop-target's JavaScript evaluator. +pub(crate) struct DesktopEvaluator { + query: Query, +} - self.eval(format!( - r#" - let element = document.createElement("{name}"); - {attr_iter} - element.innerHTML = "{contents}"; - document.head.appendChild(element); - "#, - )); - } +impl DesktopEvaluator { + /// Creates a new evaluator for desktop-based targets. + pub fn create(desktop_ctx: DesktopContext, js: String) -> GenerationalBox> { + let ctx = desktop_ctx.clone(); + let query = desktop_ctx.query.new_query(&js, ctx); - fn current_route(&self) -> String { - todo!() - } + // We create a generational box that is owned by the query slot so that when we drop the query slot, the generational box is also dropped. + let owner = UnsyncStorage::owner(); + let query_id = query.id; + let query = owner.insert(Box::new(DesktopEvaluator { query }) as Box); + desktop_ctx.query.active_requests.slab.borrow_mut()[query_id].owner = Some(owner); - fn go_back(&self) { - todo!() + query } +} - fn go_forward(&self) { - todo!() +impl Evaluator for DesktopEvaluator { + fn poll_join( + &mut self, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + self.query + .poll_result(cx) + .map_err(|e| EvalError::Communication(e.to_string())) } - fn push_route(&self, route: String) { - todo!() + /// Sends a message to the evaluated JavaScript. + fn send(&self, data: serde_json::Value) -> Result<(), EvalError> { + if let Err(e) = self.query.send(data) { + return Err(EvalError::Communication(e.to_string())); + } + Ok(()) } - fn replace_route(&self, path: String) { - todo!() + /// Gets an UnboundedReceiver to receive messages from the evaluated JavaScript. + fn poll_recv( + &mut self, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + self.query + .poll_recv(cx) + .map_err(|e| EvalError::Communication(e.to_string())) } } diff --git a/packages/desktop/src/element.rs b/packages/desktop/src/element.rs index 89d84b219c..505b45c435 100644 --- a/packages/desktop/src/element.rs +++ b/packages/desktop/src/element.rs @@ -1,129 +1,142 @@ use dioxus_core::ElementId; use dioxus_html::{ geometry::{PixelsRect, PixelsSize, PixelsVector2D}, - MountedElement, MountedResult, ScrollBehavior, + MountedResult, RenderedElementBacking, }; -use crate::desktop_context::DesktopContext; +use crate::{desktop_context::DesktopContext, query::QueryEngine}; #[derive(Clone)] /// A mounted element passed to onmounted events pub struct DesktopElement { id: ElementId, webview: DesktopContext, + query: QueryEngine, } impl DesktopElement { - pub(crate) fn new(id: ElementId, webview: DesktopContext) -> Self { - Self { id, webview } + pub(crate) fn new(id: ElementId, webview: DesktopContext, query: QueryEngine) -> Self { + Self { id, webview, query } } } -#[async_trait::async_trait(?Send)] -impl MountedElement for DesktopElement { +macro_rules! scripted_getter { + ($meth_name:ident, $script:literal, $output_type:path) => { + fn $meth_name( + &self, + ) -> std::pin::Pin< + Box>>, + > { + let script = format!($script, id = self.id.0); + + let fut = self + .query + .new_query::>(&script, self.webview.clone()) + .resolve(); + Box::pin(async move { + match fut.await { + Ok(Some(res)) => Ok(res), + Ok(None) => MountedResult::Err(dioxus_html::MountedError::OperationFailed( + Box::new(DesktopQueryError::FailedToQuery), + )), + Err(err) => MountedResult::Err(dioxus_html::MountedError::OperationFailed( + Box::new(err), + )), + } + }) + } + }; +} + +impl RenderedElementBacking for DesktopElement { fn as_any(&self) -> &dyn std::any::Any { self } - // async fn get_scroll_offset(&self) -> MountedResult { - // let id = self.id.0; - // let res = self.webview.eval(format!( - // "return [window.interpreter.getScrollLeft({id}), window.interpreter.getScrollTop({id})]" - // )); - // todo!() - // // Box::pin(res.recv_as()) - // // Box::pin(async { Err(dioxus_html::MountedError::NotSupported) }) - // } - - // async fn get_scroll_size(&self) -> MountedResult { - // todo!() - // // Box::pin(async { Err(dioxus_html::MountedError::NotSupported) }) - // } - - // async fn get_client_rect(&self) -> MountedResult { - // todo!() - // // Box::pin(async { Err(dioxus_html::MountedError::NotSupported) }) - // } - - // async fn scroll_to(&self, _behavior: ScrollBehavior) -> MountedResult<()> { - // todo!() - // // Box::pin(async { Err(dioxus_html::MountedError::NotSupported) }) - // } - - // async fn set_focus(&self, _focus: bool) -> MountedResult<()> { - // todo!() - // // Box::pin(async { Err(dioxus_html::MountedError::NotSupported) }) - // } - - // scripted_getter!( - // get_scroll_offset, - // "return [window.interpreter.getScrollLeft({id}), window.interpreter.getScrollTop({id})]", - // PixelsVector2D - // ); - - // scripted_getter!( - // get_scroll_size, - // "return [window.interpreter.getScrollWidth({id}), window.interpreter.getScrollHeight({id})]", - // PixelsSize - // ); - - // scripted_getter!( - // get_client_rect, - // "return window.interpreter.getClientRect({id});", - // PixelsRect - // ); - - // fn scroll_to( - // &self, - // behavior: dioxus_html::ScrollBehavior, - // ) -> std::pin::Pin>>> { - // let script = format!( - // "return window.interpreter.scrollTo({}, {});", - // self.id.0, - // serde_json::to_string(&behavior).expect("Failed to serialize ScrollBehavior") - // ); - - // let fut = self - // .query - // .new_query::(&script, self.webview.clone()) - // .resolve(); - // Box::pin(async move { - // match fut.await { - // Ok(true) => Ok(()), - // Ok(false) => MountedResult::Err(dioxus_html::MountedError::OperationFailed( - // Box::new(DesktopQueryError::FailedToQuery), - // )), - // Err(err) => { - // MountedResult::Err(dioxus_html::MountedError::OperationFailed(Box::new(err))) - // } - // } - // }) - // } - - // fn set_focus( - // &self, - // focus: bool, - // ) -> std::pin::Pin>>> { - // let script = format!( - // "return window.interpreter.setFocus({}, {});", - // self.id.0, focus - // ); - - // let fut = self - // .query - // .new_query::(&script, self.webview.clone()) - // .resolve(); - - // Box::pin(async move { - // match fut.await { - // Ok(true) => Ok(()), - // Ok(false) => MountedResult::Err(dioxus_html::MountedError::OperationFailed( - // Box::new(DesktopQueryError::FailedToQuery), - // )), - // Err(err) => { - // MountedResult::Err(dioxus_html::MountedError::OperationFailed(Box::new(err))) - // } - // } - // }) - // } + scripted_getter!( + get_scroll_offset, + "return [window.interpreter.getScrollLeft({id}), window.interpreter.getScrollTop({id})]", + PixelsVector2D + ); + + scripted_getter!( + get_scroll_size, + "return [window.interpreter.getScrollWidth({id}), window.interpreter.getScrollHeight({id})]", + PixelsSize + ); + + scripted_getter!( + get_client_rect, + "return window.interpreter.getClientRect({id});", + PixelsRect + ); + + fn scroll_to( + &self, + behavior: dioxus_html::ScrollBehavior, + ) -> std::pin::Pin>>> { + let script = format!( + "return window.interpreter.scrollTo({}, {});", + self.id.0, + serde_json::to_string(&behavior).expect("Failed to serialize ScrollBehavior") + ); + + let fut = self + .query + .new_query::(&script, self.webview.clone()) + .resolve(); + Box::pin(async move { + match fut.await { + Ok(true) => Ok(()), + Ok(false) => MountedResult::Err(dioxus_html::MountedError::OperationFailed( + Box::new(DesktopQueryError::FailedToQuery), + )), + Err(err) => { + MountedResult::Err(dioxus_html::MountedError::OperationFailed(Box::new(err))) + } + } + }) + } + + fn set_focus( + &self, + focus: bool, + ) -> std::pin::Pin>>> { + let script = format!( + "return window.interpreter.setFocus({}, {});", + self.id.0, focus + ); + + let fut = self + .query + .new_query::(&script, self.webview.clone()) + .resolve(); + + Box::pin(async move { + match fut.await { + Ok(true) => Ok(()), + Ok(false) => MountedResult::Err(dioxus_html::MountedError::OperationFailed( + Box::new(DesktopQueryError::FailedToQuery), + )), + Err(err) => { + MountedResult::Err(dioxus_html::MountedError::OperationFailed(Box::new(err))) + } + } + }) + } } + +#[derive(Debug)] +enum DesktopQueryError { + FailedToQuery, +} + +impl std::fmt::Display for DesktopQueryError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + DesktopQueryError::FailedToQuery => write!(f, "Failed to query the element"), + } + } +} + +impl std::error::Error for DesktopQueryError {} diff --git a/packages/desktop/src/file_upload.rs b/packages/desktop/src/file_upload.rs index 219f0ae179..5448586d35 100644 --- a/packages/desktop/src/file_upload.rs +++ b/packages/desktop/src/file_upload.rs @@ -11,12 +11,18 @@ use dioxus_html::{ point_interaction::{ InteractionElementOffset, InteractionLocation, ModifiersInteraction, PointerInteraction, }, - prelude::SerializedPointInteraction, + prelude::{SerializedMouseData, SerializedPointInteraction}, FileEngine, HasDragData, HasFileData, HasFormData, HasMouseData, }; use serde::Deserialize; -use std::{cell::RefCell, path::PathBuf, rc::Rc, str::FromStr, sync::Arc}; +use std::{ + cell::{Cell, RefCell}, + path::PathBuf, + rc::Rc, + str::FromStr, + sync::Arc, +}; use wry::DragDropEvent; #[derive(Debug, Deserialize)] @@ -106,7 +112,7 @@ impl FileDialogRequest { enum Filters { Extension(String), - Mime, + Mime(String), Audio, Video, Image, @@ -116,7 +122,7 @@ impl Filters { fn as_extensions(&self) -> Vec<&str> { match self { Filters::Extension(extension) => vec![extension.as_str()], - Filters::Mime => vec![], + Filters::Mime(_) => vec![], Filters::Audio => vec!["mp3", "wav", "ogg"], Filters::Video => vec!["mp4", "webm"], Filters::Image => vec!["png", "jpg", "jpeg", "gif", "webp"], @@ -135,7 +141,7 @@ impl FromStr for Filters { "audio/*" => Ok(Filters::Audio), "video/*" => Ok(Filters::Video), "image/*" => Ok(Filters::Image), - _ => Ok(Filters::Mime), + _ => Ok(Filters::Mime(s.to_string())), } } } @@ -162,7 +168,6 @@ impl HasFormData for DesktopFileUploadForm { pub struct NativeFileHover { event: Rc>>, } - impl NativeFileHover { pub fn set(&self, event: DragDropEvent) { self.event.borrow_mut().replace(event); diff --git a/packages/desktop/src/hooks.rs b/packages/desktop/src/hooks.rs index cbf9fa8b3d..29e3324903 100644 --- a/packages/desktop/src/hooks.rs +++ b/packages/desktop/src/hooks.rs @@ -5,7 +5,7 @@ use crate::{ ShortcutHandle, ShortcutRegistryError, WryEventHandler, }; use dioxus_core::{ - prelude::{consume_context, use_hook_with_cleanup, RuntimeGuard}, + prelude::{consume_context, current_scope_id, use_hook_with_cleanup, RuntimeGuard}, use_hook, Runtime, }; @@ -64,15 +64,16 @@ pub fn use_asset_handler( name: &str, mut handler: impl FnMut(AssetRequest, RequestAsyncResponder) + 'static, ) { - let callback = use_callback(move |args: (AssetRequest, RequestAsyncResponder)| { - handler(args.0, args.1); - }); + // wrap the user's handler in something that keeps it up to date + let cb = use_callback(move |(asset, responder)| handler(asset, responder)); use_hook_with_cleanup( || { - crate::window() - .asset_handlers - .register_handler(name.to_string(), callback); + crate::window().asset_handlers.register_handler( + name.to_string(), + Box::new(move |asset, responder| cb((asset, responder))), + current_scope_id().unwrap(), + ); Rc::new(name.to_string()) }, diff --git a/packages/desktop/src/index.html b/packages/desktop/src/index.html index f70b8149eb..45926e3f1f 100644 --- a/packages/desktop/src/index.html +++ b/packages/desktop/src/index.html @@ -3,8 +3,6 @@ Dioxus app - - diff --git a/packages/desktop/src/ipc.rs b/packages/desktop/src/ipc.rs index a76bb9e442..e78114f688 100644 --- a/packages/desktop/src/ipc.rs +++ b/packages/desktop/src/ipc.rs @@ -44,6 +44,8 @@ pub struct IpcMessage { #[derive(Deserialize, Serialize, Debug, Clone)] pub enum IpcMethod<'a> { FileDialog, + UserEvent, + Query, BrowserOpen, Initialize, Other(&'a str), @@ -53,6 +55,8 @@ impl IpcMessage { pub(crate) fn method(&self) -> IpcMethod { match self.method.as_str() { "file_dialog" => IpcMethod::FileDialog, + "user_event" => IpcMethod::UserEvent, + "query" => IpcMethod::Query, "browser_open" => IpcMethod::BrowserOpen, "initialize" => IpcMethod::Initialize, _ => IpcMethod::Other(&self.method), diff --git a/packages/desktop/src/launch.rs b/packages/desktop/src/launch.rs index cf71002e70..1a66d169b7 100644 --- a/packages/desktop/src/launch.rs +++ b/packages/desktop/src/launch.rs @@ -47,6 +47,8 @@ pub fn launch_virtual_dom_blocking(virtual_dom: VirtualDom, desktop_config: Conf UserWindowEvent::Ipc { id, msg } => match msg.method() { IpcMethod::Initialize => app.handle_initialize_msg(id), IpcMethod::FileDialog => app.handle_file_dialog_msg(msg, id), + IpcMethod::UserEvent => {} + IpcMethod::Query => app.handle_query_msg(msg, id), IpcMethod::BrowserOpen => app.handle_browser_open(msg), IpcMethod::Other(_) => {} }, diff --git a/packages/desktop/src/lib.rs b/packages/desktop/src/lib.rs index 77eab0503a..f1882d50ae 100644 --- a/packages/desktop/src/lib.rs +++ b/packages/desktop/src/lib.rs @@ -18,6 +18,7 @@ mod hooks; mod ipc; mod menubar; mod protocol; +mod query; mod shortcut; mod waker; mod webview; diff --git a/packages/desktop/src/protocol.rs b/packages/desktop/src/protocol.rs index 37d6be17c3..2c159ef8a7 100644 --- a/packages/desktop/src/protocol.rs +++ b/packages/desktop/src/protocol.rs @@ -2,7 +2,12 @@ use crate::document::NATIVE_EVAL_JS; use crate::{assets::*, webview::WebviewEdits}; use dioxus_interpreter_js::unified_bindings::SLEDGEHAMMER_JS; use dioxus_interpreter_js::NATIVE_JS; -use std::path::{Path, PathBuf}; +use serde::Deserialize; +use std::{ + path::{Component, Path, PathBuf}, + process::Command, + sync::OnceLock, +}; use wry::{ http::{status::StatusCode, Request, Response}, RequestAsyncResponder, Result, @@ -22,94 +27,6 @@ const EVENTS_PATH: &str = "dioxus://index.html/__events"; static DEFAULT_INDEX: &str = include_str!("./index.html"); -/// Handle a request from the webview -/// -/// - Tries to stream edits if they're requested. -/// - If that doesn't match, tries a user provided asset handler -/// - If that doesn't match, tries to serve a file from the filesystem -pub(super) fn desktop_handler( - request: Request>, - asset_handlers: AssetHandlers, - responder: RequestAsyncResponder, - edit_state: &WebviewEdits, - custom_head: Option, - custom_index: Option, - root_name: &str, - headless: bool, -) { - // Try to serve the index file first - if let Some(index_bytes) = - index_request(&request, custom_head, custom_index, &root_name, headless) - { - return responder.respond(index_bytes); - } - - // If the request is asking for edits (ie binary protocol streaming), do that - let trimmed_uri = request.uri().path().trim_matches('/'); - if trimmed_uri == "__edits" { - return edit_state.wry_queue.handle_request(responder); - } - - // If the request is asking for an event response, do that - if trimmed_uri == "__events" { - return edit_state.handle_event(request, responder); - } - - // todo: we want to move the custom assets onto a different protocol or something - if let Some(name) = request.uri().path().split('/').next() { - if asset_handlers.has_handler(name) { - let _name = name.to_string(); - return asset_handlers.handle_request(&_name, request, responder); - } - } - - match serve_asset(request) { - Ok(res) => responder.respond(res), - Err(_e) => responder.respond( - Response::builder() - .status(StatusCode::INTERNAL_SERVER_ERROR) - .body(String::from("Failed to serve asset").into_bytes()) - .unwrap(), - ), - } -} - -fn serve_asset(request: Request>) -> Result>> { - // If the user provided a custom asset handler, then call it and return the response if the request was handled. - // The path is the first part of the URI, so we need to trim the leading slash. - let mut asset = PathBuf::from( - urlencoding::decode(request.uri().path()) - .expect("expected URL to be UTF-8 encoded") - .as_ref(), - ); - - // If the asset doesn't exist, then we might need to try resolving it from the bundle root manually. - // We can mostly guarantee that manganis will give us absolute paths, so the only way we're getting these - // is if someone is passing in a relative path to their project, manually - // - // Do I *think* you should be doing this? No. - // Eventually we want to remove this, so consider it deprecated - if !asset.exists() { - let relative_path = request.uri().path().trim_start_matches('/'); - let bundle_root = get_asset_root().unwrap_or_else(|| std::env::current_dir().unwrap()); - asset = bundle_root.join(relative_path); - } - - // If the asset exists, then we can serve it! - if asset.exists() { - let mime_type = get_mime_from_path(&asset); - println!("mime type: {:?} for {:?}", mime_type, asset); - return Ok(Response::builder() - .header("Content-Type", mime_type?) - .header("Access-Control-Allow-Origin", "*") - .body(std::fs::read(asset)?)?); - } - - return Ok(Response::builder() - .status(StatusCode::NOT_FOUND) - .body(String::from("Not Found").into_bytes())?); -} - /// Build the index.html file we use for bootstrapping a new app /// /// We use wry/webview by building a special index.html that forms a bridge between the webview and your rust code @@ -119,7 +36,7 @@ fn serve_asset(request: Request>) -> Result>> { /// mess with UI elements. We make this decision since other renderers like LiveView are very separate and can /// never properly bridge the gap. Eventually of course, the idea is to build a custom CSS/HTML renderer where you /// *do* have native control over elements, but that still won't work with liveview. -fn index_request( +pub(super) fn index_request( request: &Request>, custom_head: Option, custom_index: Option, @@ -136,8 +53,17 @@ fn index_request( // Insert a custom head if provided // We look just for the closing head tag. If a user provided a custom index with weird syntax, this might fail + let head = match custom_head { + Some(mut head) => { + if let Some(assets_head) = assets_head() { + head.push_str(&assets_head); + } + Some(head) + } + None => assets_head(), + }; - if let Some(head) = custom_head { + if let Some(head) = head { index.insert_str(index.find("").expect("Head element to exist"), &head); } @@ -156,6 +82,141 @@ fn index_request( .ok() } +fn assets_head() -> Option { + #[cfg(any( + target_os = "windows", + target_os = "macos", + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + { + let assets_head_path = PathBuf::from("__assets_head.html"); + let head = resolve_resource(&assets_head_path); + match std::fs::read_to_string(&head) { + Ok(s) => Some(s), + Err(err) => { + tracing::warn!("Assets built with manganis cannot be preloaded (failed to read {head:?}). This warning may occur when you build a desktop application without the dioxus CLI. If you do not use manganis, you can ignore this warning: {err}."); + None + } + } + } + #[cfg(not(any( + target_os = "windows", + target_os = "macos", + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + )))] + { + None + } +} + +fn resolve_resource(path: &Path) -> PathBuf { + let mut base_path = get_asset_root_or_default(); + + if running_in_dev_mode() { + base_path.push(path); + + // Special handler for Manganis filesystem fallback. + // We need this since Manganis provides assets from workspace root. + if !base_path.exists() { + let workspace_root = get_workspace_root_from_cargo(); + let asset_path = workspace_root.join(path); + return asset_path; + } + } else { + let mut resource_path = PathBuf::new(); + for component in path.components() { + // Tauri-bundle inserts special path segments for abnormal component paths + match component { + Component::Prefix(_) => {} + Component::RootDir => resource_path.push("_root_"), + Component::CurDir => {} + Component::ParentDir => resource_path.push("_up_"), + Component::Normal(p) => resource_path.push(p), + } + } + base_path.push(resource_path); + } + base_path +} + +/// Handle a request from the webview +/// +/// - Tries to stream edits if they're requested. +/// - If that doesn't match, tries a user provided asset handler +/// - If that doesn't match, tries to serve a file from the filesystem +pub(super) fn desktop_handler( + request: Request>, + asset_handlers: AssetHandlerRegistry, + responder: RequestAsyncResponder, + edit_state: &WebviewEdits, +) { + // If the request is asking for edits (ie binary protocol streaming), do that + let trimmed_uri = request.uri().path().trim_matches('/'); + if trimmed_uri == "__edits" { + return edit_state.wry_queue.handle_request(responder); + } + + // If the request is asking for an event response, do that + if trimmed_uri == "__events" { + return edit_state.handle_event(request, responder); + } + + // If the user provided a custom asset handler, then call it and return the response if the request was handled. + // The path is the first part of the URI, so we need to trim the leading slash. + let path = PathBuf::from( + urlencoding::decode(request.uri().path().trim_start_matches('/')) + .expect("expected URL to be UTF-8 encoded") + .as_ref(), + ); + + if path.parent().is_none() { + return tracing::error!("Asset request has no parent {path:?}"); + } + + if let Some(name) = path.iter().next().unwrap().to_str() { + if asset_handlers.has_handler(name) { + return asset_handlers.handle_request(name, request, responder); + } + } + + // Else, try to serve a file from the filesystem. + match serve_from_fs(path) { + Ok(res) => responder.respond(res), + Err(e) => { + tracing::error!("Error serving request from filesystem {}", e); + } + } +} + +fn serve_from_fs(path: PathBuf) -> Result>> { + // If the path is relative, we'll try to serve it from the assets directory. + let mut asset = resolve_resource(&path); + + // If we can't find it, make it absolute and try again + if !asset.exists() { + asset = PathBuf::from("/").join(&path); + } + + if !asset.exists() { + return Ok(Response::builder() + .status(StatusCode::NOT_FOUND) + .body(String::from("Not Found").into_bytes())?); + } + + Ok(Response::builder() + .header("Content-Type", get_mime_from_path(&asset)?) + .header("Access-Control-Allow-Origin", "*") + .body(std::fs::read(asset)?)?) +} + /// Construct the inline script that boots up the page and bridges the webview with rust code. /// /// The arguments here: @@ -185,18 +246,36 @@ fn module_loader(root_id: &str, headless: bool) -> String { window.interpreter.waitForRequest({headless}); }} + "# ) } +/// Get the asset directory, following tauri/cargo-bundles directory discovery approach +/// +/// Defaults to the current directory if no asset directory is found, which is useful for development when the app +/// isn't bundled. +fn get_asset_root_or_default() -> PathBuf { + get_asset_root().unwrap_or_else(|| std::env::current_dir().unwrap()) +} + +fn running_in_dev_mode() -> bool { + // If running under cargo, there's no bundle! + // There might be a smarter/more resilient way of doing this + std::env::var_os("CARGO").is_some() +} + /// Get the asset directory, following tauri/cargo-bundles directory discovery approach /// /// Currently supports: /// - [x] macOS -/// - [x] iOS +/// - [ ] Windows /// - [ ] Linux (rpm) /// - [ ] Linux (deb) -/// - [ ] Windows +/// - [ ] iOS /// - [ ] Android #[allow(unreachable_code)] fn get_asset_root() -> Option { @@ -207,53 +286,77 @@ fn get_asset_root() -> Option { return dioxus_cli_config::base_path(); } - // Use the prescence of the bundle to determine if we're in dev mode - // todo: for other platforms, we should check their bundles too. This currently only works for macOS and iOS - #[cfg(any(target_os = "macos", target_os = "ios"))] + #[cfg(target_os = "macos")] { - // Note that this will return `target/debug` if you're in debug mode - not reliable check if we're in dev mode - if let Some(resources) = core_foundation::bundle::CFBundle::main_bundle().resources_path() { - return dunce::canonicalize(resources).ok(); - } + let bundle = core_foundation::bundle::CFBundle::main_bundle(); + let bundle_path = bundle.path()?; + let resources_path = bundle.resources_path()?; + let absolute_resources_root = bundle_path.join(resources_path); + return dunce::canonicalize(absolute_resources_root).ok(); } None } /// Get the mime type from a path-like string -fn get_mime_from_path(asset: &Path) -> Result<&'static str> { - if asset.extension().is_some_and(|ext| ext == "svg") { +fn get_mime_from_path(trimmed: &Path) -> Result<&'static str> { + if trimmed.extension().is_some_and(|ext| ext == "svg") { return Ok("image/svg+xml"); } - match infer::get_from_path(asset)?.map(|f| f.mime_type()) { + match infer::get_from_path(trimmed)?.map(|f| f.mime_type()) { Some(f) if f != "text/plain" => Ok(f), - _other => Ok(get_mime_by_ext(asset)), + _ => Ok(get_mime_by_ext(trimmed)), } } /// Get the mime type from a URI using its extension fn get_mime_by_ext(trimmed: &Path) -> &'static str { match trimmed.extension().and_then(|e| e.to_str()) { - // The common assets are all utf-8 encoded - Some("js") => "text/javascript; charset=utf-8", - Some("css") => "text/css; charset=utf-8", - Some("json") => "application/json; charset=utf-8", - Some("svg") => "image/svg+xml; charset=utf-8", - Some("html") => "text/html; charset=utf-8", - - // the rest... idk? probably not - Some("mjs") => "text/javascript; charset=utf-8", Some("bin") => "application/octet-stream", + Some("css") => "text/css", Some("csv") => "text/csv", + Some("html") => "text/html", Some("ico") => "image/vnd.microsoft.icon", + Some("js") => "text/javascript", + Some("json") => "application/json", Some("jsonld") => "application/ld+json", + Some("mjs") => "text/javascript", Some("rtf") => "application/rtf", + Some("svg") => "image/svg+xml", Some("mp4") => "video/mp4", // Assume HTML when a TLD is found for eg. `dioxus:://dioxuslabs.app` | `dioxus://hello.com` - Some(_) => "text/html; charset=utf-8", + Some(_) => "text/html", // https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types // using octet stream according to this: None => "application/octet-stream", } } + +/// A global that stores the workspace root. Used in [`get_workspace_root_from_cargo`]. +static WORKSPACE_ROOT: OnceLock = OnceLock::new(); + +/// Describes the metadata we need from `cargo metadata`. +#[derive(Deserialize)] +struct CargoMetadata { + workspace_root: PathBuf, +} + +/// Get the workspace root using `cargo metadata`. Should not be used in release mode. +pub(crate) fn get_workspace_root_from_cargo() -> PathBuf { + WORKSPACE_ROOT + .get_or_init(|| { + let out = Command::new("cargo") + .args(["metadata", "--format-version", "1", "--no-deps"]) + .output() + .expect("`cargo metadata` failed to run"); + + let out = + String::from_utf8(out.stdout).expect("failed to parse output of `cargo metadata`"); + let metadata = serde_json::from_str::(&out) + .expect("failed to deserialize data from `cargo metadata`"); + + metadata.workspace_root + }) + .to_owned() +} diff --git a/packages/desktop/src/query.rs b/packages/desktop/src/query.rs new file mode 100644 index 0000000000..15d6253804 --- /dev/null +++ b/packages/desktop/src/query.rs @@ -0,0 +1,240 @@ +use crate::DesktopContext; +use futures_util::{FutureExt, StreamExt}; +use generational_box::Owner; +use serde::{de::DeserializeOwned, Deserialize}; +use serde_json::Value; +use slab::Slab; +use std::{cell::RefCell, rc::Rc}; +use thiserror::Error; + +/// Tracks what query ids are currently active +pub(crate) struct SharedSlab { + pub slab: Rc>>, +} + +impl Clone for SharedSlab { + fn clone(&self) -> Self { + Self { + slab: self.slab.clone(), + } + } +} + +impl Default for SharedSlab { + fn default() -> Self { + SharedSlab { + slab: Rc::new(RefCell::new(Slab::new())), + } + } +} + +pub(crate) struct QueryEntry { + channel_sender: futures_channel::mpsc::UnboundedSender, + return_sender: Option>>, + pub owner: Option, +} + +/// Handles sending and receiving arbitrary queries from the webview. Queries can be resolved non-sequentially, so we use ids to track them. +#[derive(Clone, Default)] +pub(crate) struct QueryEngine { + pub active_requests: SharedSlab, +} + +impl QueryEngine { + /// Creates a new query and returns a handle to it. The query will be resolved when the webview returns a result with the same id. + pub fn new_query( + &self, + script: &str, + context: DesktopContext, + ) -> Query { + let (tx, rx) = futures_channel::mpsc::unbounded(); + let (return_tx, return_rx) = futures_channel::oneshot::channel(); + let request_id = self.active_requests.slab.borrow_mut().insert(QueryEntry { + channel_sender: tx, + return_sender: Some(return_tx), + owner: None, + }); + + // start the query + // We embed the return of the eval in a function so we can send it back to the main thread + if let Err(err) = context.webview.evaluate_script(&format!( + r#"(function(){{ + let dioxus = window.createQuery({request_id}); + let post_error = function(err) {{ + let returned_value = {{ + "method": "query", + "params": {{ + "id": {request_id}, + "data": {{ + "data": err, + "method": "return_error" + }} + }} + }}; + window.ipc.postMessage( + JSON.stringify(returned_value) + ); + }}; + try {{ + const AsyncFunction = async function () {{}}.constructor; + let promise = (new AsyncFunction("dioxus", {script:?}))(dioxus); + promise + .then((result)=>{{ + let returned_value = {{ + "method": "query", + "params": {{ + "id": {request_id}, + "data": {{ + "data": result, + "method": "return" + }} + }} + }}; + window.ipc.postMessage( + JSON.stringify(returned_value) + ); + }}) + .catch(err => post_error(`Error running JS: ${{err}}`)); + }} catch (error) {{ + post_error(`Invalid JS: ${{error}}`); + }} + }})();"# + )) { + tracing::warn!("Query error: {err}"); + } + + Query { + id: request_id, + receiver: rx, + return_receiver: Some(return_rx), + desktop: context, + phantom: std::marker::PhantomData, + } + } + + /// Send a query channel message to the correct query + pub fn send(&self, data: QueryResult) { + let QueryResult { id, data } = data; + let mut slab = self.active_requests.slab.borrow_mut(); + if let Some(entry) = slab.get_mut(id) { + match data { + QueryResultData::Return { data } => { + if let Some(sender) = entry.return_sender.take() { + let _ = sender.send(Ok(data.unwrap_or_default())); + } + } + QueryResultData::ReturnError { data } => { + if let Some(sender) = entry.return_sender.take() { + let _ = sender.send(Err(data.to_string())); + } + } + QueryResultData::Drop => { + slab.remove(id); + } + QueryResultData::Send { data } => { + let _ = entry.channel_sender.unbounded_send(data); + } + } + } + } +} + +pub(crate) struct Query { + desktop: DesktopContext, + receiver: futures_channel::mpsc::UnboundedReceiver, + return_receiver: Option>>, + pub id: usize, + phantom: std::marker::PhantomData, +} + +impl Query { + /// Resolve the query + pub async fn resolve(mut self) -> Result { + let result = self.result().await?; + V::deserialize(result).map_err(QueryError::Deserialize) + } + + /// Send a message to the query + pub fn send(&self, message: S) -> Result<(), QueryError> { + let queue_id = self.id; + + let data = message.to_string(); + let script = format!(r#"window.getQuery({queue_id}).rustSend({data});"#); + + self.desktop + .webview + .evaluate_script(&script) + .map_err(|e| QueryError::Send(e.to_string()))?; + + Ok(()) + } + + /// Poll the query for a message + pub fn poll_recv( + &mut self, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + self.receiver + .poll_next_unpin(cx) + .map(|result| result.ok_or(QueryError::Recv(String::from("Receive channel closed")))) + } + + /// Receive the result of the query + pub async fn result(&mut self) -> Result { + match self.return_receiver.take() { + Some(receiver) => match receiver.await { + Ok(Ok(data)) => Ok(data), + Ok(Err(err)) => Err(QueryError::Recv(err)), + Err(err) => Err(QueryError::Recv(err.to_string())), + }, + None => Err(QueryError::Finished), + } + } + + /// Poll the query for a result + pub fn poll_result( + &mut self, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + match self.return_receiver.as_mut() { + Some(receiver) => receiver.poll_unpin(cx).map(|result| match result { + Ok(Ok(data)) => Ok(data), + Ok(Err(err)) => Err(QueryError::Recv(err)), + Err(err) => Err(QueryError::Recv(err.to_string())), + }), + None => std::task::Poll::Ready(Err(QueryError::Finished)), + } + } +} + +#[derive(Error, Debug)] +#[non_exhaustive] +pub enum QueryError { + #[error("Error receiving query result: {0}")] + Recv(String), + #[error("Error sending message to query: {0}")] + Send(String), + #[error("Error deserializing query result: {0}")] + Deserialize(serde_json::Error), + #[error("Query has already been resolved")] + Finished, +} + +#[derive(Clone, Debug, Deserialize)] +pub(crate) struct QueryResult { + id: usize, + data: QueryResultData, +} + +#[derive(Clone, Debug, Deserialize)] +#[serde(tag = "method")] +enum QueryResultData { + #[serde(rename = "return")] + Return { data: Option }, + #[serde(rename = "return_error")] + ReturnError { data: Value }, + #[serde(rename = "send")] + Send { data: Value }, + #[serde(rename = "drop")] + Drop, +} diff --git a/packages/desktop/src/webview.rs b/packages/desktop/src/webview.rs index 5e7f57582b..875ee9eff2 100644 --- a/packages/desktop/src/webview.rs +++ b/packages/desktop/src/webview.rs @@ -1,7 +1,6 @@ use crate::document::DesktopDocument; use crate::element::DesktopElement; use crate::file_upload::DesktopFileDragEvent; -use crate::file_upload::NativeFileEngine; use crate::menubar::DioxusMenu; use crate::{ app::SharedContext, @@ -14,7 +13,6 @@ use crate::{ Config, DesktopContext, DesktopService, }; use dioxus_core::{Runtime, ScopeId, VirtualDom}; -use dioxus_document::Document; use dioxus_hooks::to_owned; use dioxus_html::{prelude::Document, HasFileData, HtmlEvent, PlatformEventData}; use futures_util::{pin_mut, FutureExt}; @@ -89,6 +87,7 @@ impl WebviewEdits { return Default::default(); }; + let query = desktop_context.query.clone(); let recent_file = desktop_context.file_hover.clone(); // check for a mounted event placeholder and replace it with a desktop specific element @@ -192,7 +191,7 @@ impl WebviewInstance { let mut web_context = WebContext::new(cfg.data_dir.clone()); let edit_queue = WryQueue::default(); - let asset_handlers = AssetHandlers::new(); + let asset_handlers = AssetHandlerRegistry::new(dom.runtime()); let edits = WebviewEdits::new(dom.runtime(), edit_queue.clone()); let file_hover = NativeFileHover::default(); let headless = !cfg.window.window.visible; @@ -206,16 +205,19 @@ impl WebviewInstance { edits ]; move |request, responder: RequestAsyncResponder| { - protocol::desktop_handler( - request, - asset_handlers.clone(), - responder, - &edits, + // Try to serve the index file first + if let Some(index_bytes) = protocol::index_request( + &request, custom_head.clone(), custom_index.clone(), &root_name, headless, - ) + ) { + return responder.respond(index_bytes); + } + + // Otherwise, try to serve an asset, either from the user or the filesystem + protocol::desktop_handler(request, asset_handlers.clone(), responder, &edits); } }; @@ -333,8 +335,6 @@ impl WebviewInstance { None }; - // The context will function as both the document and the context provider - // But we need to disambiguate the types for rust's TypeId to downcast Rc properly let desktop_context = Rc::from(DesktopService::new( webview, window, @@ -342,14 +342,13 @@ impl WebviewInstance { asset_handlers, file_hover, )); - let as_document: Rc = desktop_context.clone() as Rc; // Provide the desktop context to the virtual dom and edit handler edits.set_desktop_context(desktop_context.clone()); - + let provider: Rc = Rc::new(DesktopDocument::new(desktop_context.clone())); dom.in_runtime(|| { ScopeId::ROOT.provide_context(desktop_context.clone()); - ScopeId::ROOT.provide_context(as_document); + ScopeId::ROOT.provide_context(provider); }); WebviewInstance { diff --git a/packages/devtools/tests/roundtrip.rs b/packages/devtools/tests/roundtrip.rs deleted file mode 100644 index ca779625c9..0000000000 --- a/packages/devtools/tests/roundtrip.rs +++ /dev/null @@ -1,36 +0,0 @@ -use dioxus_devtools::*; -use std::io::{BufWriter, Write}; - -#[test] -fn roundtrip() { - println!("Starting server"); - - let callback = |msg: DevserverMsg| { - println!("msg received! {msg:?}"); - }; - - let bind = std::net::TcpListener::bind("127.0.0.1:8080").expect("Failed to bind port"); - - connect("127.0.0.1:8080".parse().unwrap(), callback); - - let (stream, _addr) = bind.accept().unwrap(); - { - println!("Accepted connection"); - - let mut writer = BufWriter::new(stream); - - for _x in 0..3 { - let line = serde_json::to_string(&DevserverMsg::HotReload(HotReloadMsg { - templates: vec![], - assets: vec!["/asd/bcc".into()], - })) - .unwrap(); - writer.write(line.as_bytes()).unwrap(); - writer.write("\n".as_bytes()).unwrap(); - writer.flush().unwrap(); - std::thread::sleep(std::time::Duration::from_millis(100)); - } - } - - std::thread::sleep(std::time::Duration::from_secs(1)); -} diff --git a/packages/dioxus-lib/Cargo.toml b/packages/dioxus-lib/Cargo.toml index 7c356b2b64..86144a9bfe 100644 --- a/packages/dioxus-lib/Cargo.toml +++ b/packages/dioxus-lib/Cargo.toml @@ -18,7 +18,6 @@ dioxus-config-macro = { workspace = true, optional = true } dioxus-hooks = { workspace = true, optional = true } dioxus-rsx = { workspace = true, optional = true } dioxus-signals = { workspace = true, optional = true } -manganis = { workspace = true, optional = true } [dev-dependencies] dioxus = { workspace = true } diff --git a/packages/dioxus/Cargo.toml b/packages/dioxus/Cargo.toml index 4ecb9e60f2..5db3dda5e3 100644 --- a/packages/dioxus/Cargo.toml +++ b/packages/dioxus/Cargo.toml @@ -12,8 +12,6 @@ rust-version = "1.79.0" [dependencies] dioxus-core = { workspace = true } -dioxus-core-types = { workspace = true } -dioxus-document = { workspace = true } dioxus-html = { workspace = true, default-features = false, optional = true } dioxus-core-macro = { workspace = true, optional = true } dioxus-config-macro = { workspace = true, optional = true } @@ -23,13 +21,11 @@ dioxus-router = { workspace = true, optional = true } dioxus-web = { workspace = true, default-features = false, optional = true } dioxus-mobile = { workspace = true, optional = true } dioxus-desktop = { workspace = true, default-features = true, optional = true } -dioxus-fullstack = { workspace = true, default-features = true, optional = true, features = ["base"] } +dioxus-fullstack = { workspace = true, default-features = true, optional = true } dioxus-static-site-generation = { workspace = true, optional = true } dioxus-liveview = { workspace = true, optional = true } dioxus-ssr = { workspace = true, optional = true } -dioxus-isrg = { workspace = true, optional = true } manganis = { workspace = true, optional = true } -dioxus-devtools = { workspace = true, optional = true } serde = { version = "1.0.136", optional = true } axum = { workspace = true, optional = true } @@ -46,69 +42,36 @@ html = ["dep:dioxus-html"] hooks = ["dep:dioxus-hooks"] devtools = ["dep:dioxus-devtools", "dioxus-web?/devtools", "dioxus-fullstack?/devtools"] mounted = ["dioxus-web?/mounted", "dioxus-html?/mounted"] -file-engine = ["dioxus-web?/file-engine"] -asset = ["dep:manganis"] -document = ["dioxus-web?/document"] +file_engine = ["dioxus-web?/file_engine"] +asset = ["dep:manganis", "dioxus-core/manganis"] +document = ["dioxus-web?/document", "dioxus-html?/document"] launch = ["dep:dioxus-config-macro"] -router = [ - "dep:dioxus-router" -] +router = ["dep:dioxus-router"] # Platforms -fullstack = [ - "dep:dioxus-fullstack", - "dioxus-config-macro/fullstack", - "dep:serde", - # "dioxus-router?/fullstack", -] -desktop = [ - "dep:dioxus-desktop", - # "dioxus-fullstack?/desktop", - "dioxus-config-macro/desktop", -] -mobile = [ - "dep:dioxus-mobile", - # "dioxus-fullstack?/mobile", - "dioxus-config-macro/mobile", -] -web = [ - "dep:dioxus-web", - # "dioxus-fullstack?/web", - "dioxus-static-site-generation?/web", - "dioxus-config-macro/web", - # "dioxus-router?/web", -] -ssr = ["dep:dioxus-ssr", "dioxus-config-macro/ssr"] -# ssr = ["dep:dioxus-ssr", "dioxus-router?/ssr", "dioxus-config-macro/ssr"] -liveview = [ - "dep:dioxus-liveview", - "dioxus-config-macro/liveview", - # "dioxus-router?/liveview", -] -static-generation = [ - "dep:dioxus-static-site-generation", - "dioxus-config-macro/static-generation", -] +fullstack = ["dep:dioxus-fullstack", "dioxus-config-macro/fullstack", "dep:serde", "dioxus-router?/fullstack"] +desktop = ["dep:dioxus-desktop", "dioxus-fullstack?/desktop", "dioxus-config-macro/desktop"] +mobile = ["dep:dioxus-mobile", "dioxus-fullstack?/mobile", "dioxus-config-macro/mobile"] +web = ["dep:dioxus-web", "dioxus-fullstack?/web", "dioxus-static-site-generation?/web", "dioxus-config-macro/web", "dioxus-router?/web"] +ssr = ["dep:dioxus-ssr", "dioxus-router?/ssr", "dioxus-config-macro/ssr"] +liveview = ["dep:dioxus-liveview", "dioxus-config-macro/liveview", "dioxus-router?/liveview"] +static-generation = ["dep:dioxus-static-site-generation", "dioxus-config-macro/static-generation"] axum = ["server"] -server = [ - "dioxus-fullstack?/axum", - "dioxus-fullstack?/server", - "dioxus-static-site-generation?/server", - "ssr", - "dioxus-liveview?/axum", - "dep:axum", -] +server = ["dioxus-fullstack?/axum", "dioxus-fullstack?/server", "dioxus-static-site-generation?/server", "ssr", "dioxus-liveview?/axum", "dep:axum"] + +# This feature just disables the no-renderer-enabled warning +third-party-renderer = [] [dev-dependencies] -criterion = { workspace = true } -dioxus = { workspace = true } -env_logger = { workspace = true } futures-util = { workspace = true } +tracing = { workspace = true } rand = { version = "0.8.4", features = ["small_rng"] } +criterion = { workspace = true } thiserror = { workspace = true } +env_logger = "0.10.0" tokio = { workspace = true, features = ["full"] } -tracing = { workspace = true } +dioxus = { workspace = true } [[bench]] name = "jsframework" @@ -126,5 +89,5 @@ features = [ "html", "liveview", "static-generation", - "server", + "server" ] diff --git a/packages/dioxus/src/launch.rs b/packages/dioxus/src/launch.rs index 534d2ac777..3e08c85780 100644 --- a/packages/dioxus/src/launch.rs +++ b/packages/dioxus/src/launch.rs @@ -6,102 +6,108 @@ use std::any::Any; use crate::prelude::*; -pub fn builder() -> LaunchBuilder { - LaunchBuilder::new() -} - -pub fn launch(app: fn() -> Element) { - #[allow(deprecated)] - LaunchBuilder::new().launch(app) -} - /// A builder for a fullstack app. #[must_use] -pub struct LaunchBuilder { - // launch_fn: LaunchFn, - // contexts: Vec>, - // platform_config: Option, +pub struct LaunchBuilder { + launch_fn: LaunchFn, + contexts: Vec>, + + platform_config: Option, } pub type LaunchFn = fn(fn() -> Element, Vec>, Cfg); +#[cfg(any( + feature = "fullstack", + feature = "static-generation", + feature = "liveview" +))] +type ValidContext = SendContext; + +#[cfg(not(any( + feature = "fullstack", + feature = "static-generation", + feature = "liveview" +)))] +type ValidContext = UnsendContext; + type SendContext = dyn Fn() -> Box + Send + Sync + 'static; + type UnsendContext = dyn Fn() -> Box + 'static; #[allow(clippy::redundant_closure)] // clippy doesnt doesn't understand our coercion to fn impl LaunchBuilder { - // /// Create a new builder for your application. This will create a launch configuration for the current platform based on the features enabled on the `dioxus` crate. - // // If you aren't using a third party renderer and this is not a docs.rs build, generate a warning about no renderer being enabled - // #[cfg_attr( - // all(not(any( - // docsrs, - // feature = "third-party-renderer", - // feature = "liveview", - // feature = "desktop", - // feature = "mobile", - // feature = "web", - // feature = "fullstack", - // feature = "static-generation" - // ))), - // deprecated( - // note = "No renderer is enabled. You must enable a renderer feature on the dioxus crate before calling the launch function.\nAdd `web`, `desktop`, `mobile`, `fullstack`, or `static-generation` to the `features` of dioxus field in your Cargo.toml.\n# Example\n```toml\n# ...\n[dependencies]\ndioxus = { version = \"0.5.0\", features = [\"web\"] }\n# ...\n```" - // ) - // )] - pub fn new() -> LaunchBuilder { - LaunchBuilder {} - // LaunchBuilder { - // launch_fn: |root, contexts, cfg| current_platform::launch(root, contexts, cfg), - // contexts: Vec::new(), - // platform_config: None, - // } + /// Create a new builder for your application. This will create a launch configuration for the current platform based on the features enabled on the `dioxus` crate. + // If you aren't using a third party renderer and this is not a docs.rs build, generate a warning about no renderer being enabled + #[cfg_attr( + all(not(any( + docsrs, + feature = "third-party-renderer", + feature = "liveview", + feature = "desktop", + feature = "mobile", + feature = "web", + feature = "fullstack", + feature = "static-generation" + ))), + deprecated( + note = "No renderer is enabled. You must enable a renderer feature on the dioxus crate before calling the launch function.\nAdd `web`, `desktop`, `mobile`, `fullstack`, or `static-generation` to the `features` of dioxus field in your Cargo.toml.\n# Example\n```toml\n# ...\n[dependencies]\ndioxus = { version = \"0.5.0\", features = [\"web\"] }\n# ...\n```" + ) + )] + pub fn new() -> LaunchBuilder { + LaunchBuilder { + launch_fn: |root, contexts, cfg| current_platform::launch(root, contexts, cfg), + contexts: Vec::new(), + platform_config: None, + } + } + + /// Launch your web application. + #[cfg(feature = "web")] + #[cfg_attr(docsrs, doc(cfg(feature = "web")))] + pub fn web() -> LaunchBuilder { + LaunchBuilder { + launch_fn: dioxus_web::launch::launch, + contexts: Vec::new(), + platform_config: None, + } + } + + /// Launch your desktop application. + #[cfg(feature = "desktop")] + #[cfg_attr(docsrs, doc(cfg(feature = "desktop")))] + pub fn desktop() -> LaunchBuilder { + LaunchBuilder { + launch_fn: |root, contexts, cfg| dioxus_desktop::launch::launch(root, contexts, cfg), + contexts: Vec::new(), + platform_config: None, + } + } + + /// Launch your fullstack application. + #[cfg(feature = "fullstack")] + #[cfg_attr(docsrs, doc(cfg(feature = "fullstack")))] + pub fn fullstack() -> LaunchBuilder { + LaunchBuilder { + launch_fn: |root, contexts, cfg| dioxus_fullstack::launch::launch(root, contexts, cfg), + contexts: Vec::new(), + platform_config: None, + } } - // /// Launch your web application. - // #[cfg(feature = "web")] - // #[cfg_attr(docsrs, doc(cfg(feature = "web")))] - // pub fn web() -> LaunchBuilder { - // LaunchBuilder { - // launch_fn: dioxus_web::launch::launch, - // contexts: Vec::new(), - // platform_config: None, - // } - // } - - // /// Launch your desktop application. - // #[cfg(feature = "desktop")] - // #[cfg_attr(docsrs, doc(cfg(feature = "desktop")))] - // pub fn desktop() -> LaunchBuilder { - // LaunchBuilder { - // launch_fn: |root, contexts, cfg| dioxus_desktop::launch::launch(root, contexts, cfg), - // contexts: Vec::new(), - // platform_config: None, - // } - // } - - // /// Launch your fullstack application. - // #[cfg(feature = "fullstack")] - // #[cfg_attr(docsrs, doc(cfg(feature = "fullstack")))] - // pub fn fullstack() -> LaunchBuilder { - // LaunchBuilder { - // launch_fn: |root, contexts, cfg| dioxus_fullstack::launch::launch(root, contexts, cfg), - // contexts: Vec::new(), - // platform_config: None, - // } - // } - - // /// Launch your static site generation application. - // #[cfg(feature = "static-generation")] - // #[cfg_attr(docsrs, doc(cfg(feature = "static-generation")))] - // pub fn static_generation() -> LaunchBuilder - // { - // LaunchBuilder { - // launch_fn: |root, contexts, cfg| { - // dioxus_static_site_generation::launch::launch(root, contexts, cfg) - // }, - // contexts: Vec::new(), - // platform_config: None, - // } - // } + /// Launch your static site generation application. + #[cfg(feature = "static-generation")] + #[cfg_attr(docsrs, doc(cfg(feature = "static-generation")))] + pub fn static_generation() -> LaunchBuilder + { + LaunchBuilder { + launch_fn: |root, contexts, cfg| { + dioxus_static_site_generation::launch::launch(root, contexts, cfg) + }, + contexts: Vec::new(), + platform_config: None, + } + } /// Launch your fullstack application. #[cfg(feature = "mobile")] @@ -114,16 +120,6 @@ impl LaunchBuilder { } } - // /// Provide a custom launch function for your application. - // /// - // /// Useful for third party renderers to tap into the launch builder API without having to reimplement it. - // pub fn custom(launch_fn: LaunchFn) -> LaunchBuilder { - // LaunchBuilder { - // launch_fn, - // contexts: vec![], - // platform_config: None, - // } - // } /// Provide a custom launch function for your application. /// /// Useful for third party renderers to tap into the launch builder API without having to reimplement it. @@ -151,310 +147,320 @@ impl LaunchBuilder { /// /// LaunchBuilder::custom(my_custom_launcher).launch(app); /// ``` - pub fn custom(launch_fn: LaunchFn) -> LaunchBuilder { + pub fn custom( + launch_fn: LaunchFn, + ) -> LaunchBuilder { LaunchBuilder { - // launch_fn, - // contexts: vec![], - // platform_config: None, + launch_fn, + contexts: vec![], + platform_config: None, } } } -// // Fullstack platform builder -// impl LaunchBuilder { -// /// Inject state into the root component's context that is created on the thread that the app is launched on. -// pub fn with_context_provider(mut self, state: impl Fn() -> Box + 'static) -> Self { -// self.contexts.push(Box::new(state) as Box); -// self -// } - -// /// Inject state into the root component's context. -// pub fn with_context(mut self, state: impl Any + Clone + 'static) -> Self { -// self.contexts -// .push(Box::new(move || Box::new(state.clone()))); -// self -// } -// } +// Fullstack platform builder +impl LaunchBuilder { + /// Inject state into the root component's context that is created on the thread that the app is launched on. + pub fn with_context_provider(mut self, state: impl Fn() -> Box + 'static) -> Self { + self.contexts.push(Box::new(state) as Box); + self + } -impl LaunchBuilder { + /// Inject state into the root component's context. + pub fn with_context(mut self, state: impl Any + Clone + 'static) -> Self { + self.contexts + .push(Box::new(move || Box::new(state.clone()))); + self + } +} + +impl LaunchBuilder { /// Inject state into the root component's context that is created on the thread that the app is launched on. pub fn with_context_provider( mut self, state: impl Fn() -> Box + Send + Sync + 'static, ) -> Self { - // self.contexts.push(Box::new(state) as Box); + self.contexts.push(Box::new(state) as Box); self } /// Inject state into the root component's context. pub fn with_context(mut self, state: impl Any + Clone + Send + Sync + 'static) -> Self { - // self.contexts - // .push(Box::new(move || Box::new(state.clone()))); + self.contexts + .push(Box::new(move || Box::new(state.clone()))); self } } -// // The unit type can be converted into the current platform config. -// // This makes it possible to use the `desktop!`, `web!`, etc macros with the launch API. -pub trait TryIntoConfig { - fn into_config(self, config: &mut Option); +/// A trait for converting a type into a platform-specific config: +/// - A unit value will be converted into `None` +/// - Any config will be converted into `Some(config)` +/// - If the config is for another platform, it will be converted into `None` +pub trait TryIntoConfig { + fn into_config(self, config: &mut Option); } -impl TryIntoConfig for () { - fn into_config(self, config: &mut Option) {} +// A config can always be converted into itself +impl TryIntoConfig for Cfg { + fn into_config(self, config: &mut Option) { + *config = Some(self); + } } -impl LaunchBuilder { +// The unit type can be converted into the current platform config. +// This makes it possible to use the `desktop!`, `web!`, etc macros with the launch API. +#[cfg(any( + feature = "liveview", + feature = "desktop", + feature = "mobile", + feature = "web", + feature = "fullstack" +))] +impl TryIntoConfig for () { + fn into_config(self, config: &mut Option) {} +} + +impl LaunchBuilder { /// Provide a platform-specific config to the builder. - pub fn with_cfg(mut self, config: F) -> Self { - // pub fn with_cfg(mut self, config: impl TryIntoConfig) -> Self { - // config.into_config(&mut self.platform_config); + pub fn with_cfg(mut self, config: impl TryIntoConfig) -> Self { + config.into_config(&mut self.platform_config); self } // Static generation is the only platform that may exit. We can't use the `!` type here - // #[cfg(any(feature = "static-generation", feature = "web"))] + #[cfg(any(feature = "static-generation", feature = "web"))] /// Launch your application. pub fn launch(self, app: fn() -> Element) { - #[cfg(feature = "web")] - { - dioxus_web::launch::launch(app, Default::default(), Default::default()); + let cfg = self.platform_config.unwrap_or_default(); + + (self.launch_fn)(app, self.contexts, cfg) + } + + #[cfg(not(any(feature = "static-generation", feature = "web")))] + /// Launch your application. + pub fn launch(self, app: fn() -> Element) -> ! { + let cfg = self.platform_config.unwrap_or_default(); + + (self.launch_fn)(app, self.contexts, cfg); + unreachable!("Launching an application will never exit") + } +} + +/// Re-export the platform we expect the user wants +/// +/// If multiple platforms are enabled, we use this priority (from highest to lowest): +/// - `fullstack` +/// - `desktop` +/// - `mobile` +/// - `static-generation` +/// - `web` +/// - `liveview` +mod current_platform { + macro_rules! if_else_cfg { + (if $attr:meta { $($then:item)* } else { $($else:item)* }) => { + $( + #[cfg($attr)] + $then + )* + $( + #[cfg(not($attr))] + $else + )* + }; + } + use crate::prelude::TryIntoConfig; + + #[cfg(feature = "fullstack")] + pub use dioxus_fullstack::launch::*; + + #[cfg(all(feature = "fullstack", feature = "axum"))] + impl TryIntoConfig + for ::dioxus_fullstack::prelude::ServeConfigBuilder + { + fn into_config(self, config: &mut Option) { + match config { + Some(config) => config.set_server_cfg(self), + None => { + *config = + Some(crate::launch::current_platform::Config::new().with_server_cfg(self)) + } + } } + } - #[cfg(feature = "liveview")] - { - dioxus_liveview::launch::launch(app, Default::default(), Default::default()); + #[cfg(any(feature = "desktop", feature = "mobile"))] + if_else_cfg! { + if not(feature = "fullstack") { + #[cfg(feature = "desktop")] + pub use dioxus_desktop::launch::*; + #[cfg(not(feature = "desktop"))] + pub use dioxus_mobile::launch::*; + } else { + if_else_cfg! { + if feature = "desktop" { + impl TryIntoConfig for ::dioxus_desktop::Config { + fn into_config(self, config: &mut Option) { + match config { + Some(config) => config.set_desktop_config(self), + None => *config = Some(crate::launch::current_platform::Config::new().with_desktop_config(self)), + } + } + } + } else { + impl TryIntoConfig for ::dioxus_mobile::Config { + fn into_config(self, config: &mut Option) { + match config { + Some(config) => config.set_mobile_cfg(self), + None => *config = Some(crate::launch::current_platform::Config::new().with_mobile_cfg(self)), + } + } + } + } + } } + } - #[cfg(feature = "fullstack")] - { - dioxus_fullstack::launch::launch(app, Default::default(), Default::default()); + #[cfg(feature = "static-generation")] + if_else_cfg! { + if all(not(feature = "fullstack"), not(feature = "desktop"), not(feature = "mobile")) { + pub use dioxus_static_site_generation::launch::*; + } else { + impl TryIntoConfig for ::dioxus_static_site_generation::Config { + fn into_config(self, config: &mut Option) {} + } + } + } + + #[cfg(feature = "web")] + if_else_cfg! { + if not(any(feature = "desktop", feature = "mobile", feature = "fullstack", feature = "static-generation")) { + pub use dioxus_web::launch::*; + } else { + if_else_cfg! { + if feature = "fullstack" { + impl TryIntoConfig for ::dioxus_web::Config { + fn into_config(self, config: &mut Option) { + match config { + Some(config) => config.set_web_config(self), + None => *config = Some(crate::launch::current_platform::Config::new().with_web_config(self)), + } + } + } + } else { + impl TryIntoConfig for ::dioxus_web::Config { + fn into_config(self, config: &mut Option) {} + } + } + } } + } - #[cfg(feature = "desktop")] + #[cfg(feature = "liveview")] + if_else_cfg! { + if + not(any( + feature = "web", + feature = "desktop", + feature = "mobile", + feature = "fullstack", + feature = "static-generation" + )) { - dioxus_desktop::launch::launch(app, Default::default(), Default::default()); + pub use dioxus_liveview::launch::*; + } else { + impl TryIntoConfig for ::dioxus_liveview::Config { + fn into_config(self, config: &mut Option) {} + } } + } - panic!("No platform feature enabled. Please enable one of the following features: liveview, desktop, mobile, web, fullstack to use the launch API.") + #[cfg(not(any( + feature = "liveview", + feature = "desktop", + feature = "mobile", + feature = "web", + feature = "fullstack", + feature = "static-generation" + )))] + pub type Config = (); + + #[cfg(not(any( + feature = "liveview", + feature = "desktop", + feature = "mobile", + feature = "web", + feature = "fullstack", + feature = "static-generation" + )))] + pub fn launch( + root: fn() -> dioxus_core::Element, + contexts: Vec>, + platform_config: (), + ) -> ! { + #[cfg(feature = "third-party-renderer")] + panic!("No first party renderer feature enabled. It looks like you are trying to use a third party renderer. You will need to use the launch function from the third party renderer crate."); + + panic!("No platform feature enabled. Please enable one of the following features: liveview, desktop, mobile, web, tui, fullstack to use the launch API.") } } -// /// Re-export the platform we expect the user wants -// /// -// /// If multiple platforms are enabled, we use this priority (from highest to lowest): -// /// - `fullstack` -// /// - `desktop` -// /// - `mobile` -// /// - `static-generation` -// /// - `web` -// /// - `liveview` -// mod current_platform { -// macro_rules! if_else_cfg { -// (if $attr:meta { $($then:item)* } else { $($else:item)* }) => { -// $( -// #[cfg($attr)] -// $then -// )* -// $( -// #[cfg(not($attr))] -// $else -// )* -// }; -// } -// use crate::prelude::TryIntoConfig; - -// #[cfg(feature = "fullstack")] -// pub use dioxus_fullstack::launch::*; - -// #[cfg(all(feature = "fullstack", feature = "axum"))] -// impl TryIntoConfig -// for ::dioxus_fullstack::prelude::ServeConfigBuilder -// { -// fn into_config(self, config: &mut Option) { -// match config { -// Some(config) => config.set_server_cfg(self), -// None => { -// *config = -// Some(crate::launch::current_platform::Config::new().with_server_cfg(self)) -// } -// } -// } -// } - -// #[cfg(any(feature = "desktop", feature = "mobile"))] -// if_else_cfg! { -// if not(feature = "fullstack") { -// #[cfg(feature = "desktop")] -// pub use dioxus_desktop::launch::*; -// #[cfg(not(feature = "desktop"))] -// pub use dioxus_mobile::launch::*; -// } else { -// if_else_cfg! { -// if feature = "desktop" { -// impl TryIntoConfig for ::dioxus_desktop::Config { -// fn into_config(self, config: &mut Option) { -// match config { -// Some(config) => config.set_desktop_config(self), -// None => *config = Some(crate::launch::current_platform::Config::new().with_desktop_config(self)), -// } -// } -// } -// } else { -// impl TryIntoConfig for ::dioxus_mobile::Config { -// fn into_config(self, config: &mut Option) { -// match config { -// Some(config) => config.set_mobile_cfg(self), -// None => *config = Some(crate::launch::current_platform::Config::new().with_mobile_cfg(self)), -// } -// } -// } -// } -// } -// } -// } - -// #[cfg(feature = "static-generation")] -// if_else_cfg! { -// if all(not(feature = "fullstack"), not(feature = "desktop"), not(feature = "mobile")) { -// pub use dioxus_static_site_generation::launch::*; -// } else { -// impl TryIntoConfig for ::dioxus_static_site_generation::Config { -// fn into_config(self, config: &mut Option) {} -// } -// } -// } - -// #[cfg(feature = "web")] -// if_else_cfg! { -// if not(any(feature = "desktop", feature = "mobile", feature = "fullstack", feature = "static-generation")) { -// pub use dioxus_web::launch::*; -// } else { -// if_else_cfg! { -// if feature = "fullstack" { -// impl TryIntoConfig for ::dioxus_web::Config { -// fn into_config(self, config: &mut Option) { -// match config { -// Some(config) => config.set_web_config(self), -// None => *config = Some(crate::launch::current_platform::Config::new().with_web_config(self)), -// } -// } -// } -// } else { -// impl TryIntoConfig for ::dioxus_web::Config { -// fn into_config(self, config: &mut Option) {} -// } -// } -// } -// } -// } - -// #[cfg(feature = "liveview")] -// if_else_cfg! { -// if -// not(any( -// feature = "web", -// feature = "desktop", -// feature = "mobile", -// feature = "fullstack", -// feature = "static-generation" -// )) -// { -// pub use dioxus_liveview::launch::*; -// } else { -// impl TryIntoConfig for ::dioxus_liveview::Config { -// fn into_config(self, config: &mut Option) {} -// } -// } -// } - -// #[cfg(not(any( -// feature = "liveview", -// feature = "desktop", -// feature = "mobile", -// feature = "web", -// feature = "fullstack", -// feature = "static-generation" -// )))] -// pub type Config = (); - -// #[cfg(not(any( -// feature = "liveview", -// feature = "desktop", -// feature = "mobile", -// feature = "web", -// feature = "fullstack", -// feature = "static-generation" -// )))] -// pub fn launch( -// root: fn() -> dioxus_core::Element, -// contexts: Vec>, -// platform_config: (), -// ) -> ! { -// #[cfg(feature = "third-party-renderer")] -// panic!("No first party renderer feature enabled. It looks like you are trying to use a third party renderer. You will need to use the launch function from the third party renderer crate."); - -// panic!("No platform feature enabled. Please enable one of the following features: liveview, desktop, mobile, web, tui, fullstack to use the launch API.") -// } -// } - -// // ! is unstable, so we can't name the type with an alias. Instead we need to generate different variants of items with macros -// macro_rules! impl_launch { -// ($($return_type:tt),*) => { -// /// Launch your application without any additional configuration. See [`LaunchBuilder`] for more options. -// // If you aren't using a third party renderer and this is not a docs.rs build, generate a warning about no renderer being enabled -// #[cfg_attr( -// all(not(any( -// docsrs, -// feature = "third-party-renderer", -// feature = "liveview", -// feature = "desktop", -// feature = "mobile", -// feature = "web", -// feature = "fullstack", -// feature = "static-generation" -// ))), -// deprecated( -// note = "No renderer is enabled. You must enable a renderer feature on the dioxus crate before calling the launch function.\nAdd `web`, `desktop`, `mobile`, `fullstack`, or `static-generation` to the `features` of dioxus field in your Cargo.toml.\n# Example\n```toml\n# ...\n[dependencies]\ndioxus = { version = \"0.5.0\", features = [\"web\"] }\n# ...\n```" -// ) -// )] -// pub fn launch(app: fn() -> Element) -> $($return_type)* { -// #[allow(deprecated)] -// LaunchBuilder::new().launch(app) -// } -// }; -// } - -// // Static generation is the only platform that may exit. We can't use the `!` type here -// #[cfg(any(feature = "static-generation", feature = "web"))] -// impl_launch!(()); -// #[cfg(not(any(feature = "static-generation", feature = "web")))] -// // impl_launch!(!); - -// #[cfg(feature = "web")] -// #[cfg_attr(docsrs, doc(cfg(feature = "web")))] -// /// Launch your web application without any additional configuration. See [`LaunchBuilder`] for more options. -// pub fn launch_web(app: fn() -> Element) { -// LaunchBuilder::web().launch(app) -// } - -// #[cfg(feature = "desktop")] -// #[cfg_attr(docsrs, doc(cfg(feature = "desktop")))] -// /// Launch your desktop application without any additional configuration. See [`LaunchBuilder`] for more options. -// pub fn launch_desktop(app: fn() -> Element) { -// LaunchBuilder::desktop().launch(app) -// } - -// #[cfg(feature = "fullstack")] -// #[cfg_attr(docsrs, doc(cfg(feature = "fullstack")))] -// /// Launch your fullstack application without any additional configuration. See [`LaunchBuilder`] for more options. -// pub fn launch_fullstack(app: fn() -> Element) { -// LaunchBuilder::new().launch(app) -// } - -// #[cfg(feature = "mobile")] -// #[cfg_attr(docsrs, doc(cfg(feature = "mobile")))] -// /// Launch your mobile application without any additional configuration. See [`LaunchBuilder`] for more options. -// pub fn launch_mobile(app: fn() -> Element) { -// LaunchBuilder::mobile().launch(app) -// } +// ! is unstable, so we can't name the type with an alias. Instead we need to generate different variants of items with macros +macro_rules! impl_launch { + ($($return_type:tt),*) => { + /// Launch your application without any additional configuration. See [`LaunchBuilder`] for more options. + // If you aren't using a third party renderer and this is not a docs.rs build, generate a warning about no renderer being enabled + #[cfg_attr( + all(not(any( + docsrs, + feature = "third-party-renderer", + feature = "liveview", + feature = "desktop", + feature = "mobile", + feature = "web", + feature = "fullstack", + feature = "static-generation" + ))), + deprecated( + note = "No renderer is enabled. You must enable a renderer feature on the dioxus crate before calling the launch function.\nAdd `web`, `desktop`, `mobile`, `fullstack`, or `static-generation` to the `features` of dioxus field in your Cargo.toml.\n# Example\n```toml\n# ...\n[dependencies]\ndioxus = { version = \"0.5.0\", features = [\"web\"] }\n# ...\n```" + ) + )] + pub fn launch(app: fn() -> Element) -> $($return_type)* { + #[allow(deprecated)] + LaunchBuilder::new().launch(app) + } + }; +} + +// Static generation is the only platform that may exit. We can't use the `!` type here +#[cfg(any(feature = "static-generation", feature = "web"))] +impl_launch!(()); +#[cfg(not(any(feature = "static-generation", feature = "web")))] +impl_launch!(!); + +#[cfg(feature = "web")] +#[cfg_attr(docsrs, doc(cfg(feature = "web")))] +/// Launch your web application without any additional configuration. See [`LaunchBuilder`] for more options. +pub fn launch_web(app: fn() -> Element) { + LaunchBuilder::web().launch(app) +} + +#[cfg(feature = "desktop")] +#[cfg_attr(docsrs, doc(cfg(feature = "desktop")))] +/// Launch your desktop application without any additional configuration. See [`LaunchBuilder`] for more options. +pub fn launch_desktop(app: fn() -> Element) { + LaunchBuilder::desktop().launch(app) +} + +#[cfg(feature = "fullstack")] +#[cfg_attr(docsrs, doc(cfg(feature = "fullstack")))] +/// Launch your fullstack application without any additional configuration. See [`LaunchBuilder`] for more options. +pub fn launch_fullstack(app: fn() -> Element) { + LaunchBuilder::fullstack().launch(app) +} + +#[cfg(feature = "mobile")] +#[cfg_attr(docsrs, doc(cfg(feature = "mobile")))] +/// Launch your mobile application without any additional configuration. See [`LaunchBuilder`] for more options. +pub fn launch_mobile(app: fn() -> Element) { + LaunchBuilder::mobile().launch(app) +} diff --git a/packages/dioxus/src/lib.rs b/packages/dioxus/src/lib.rs index 7b4a354dbb..24f231aeca 100644 --- a/packages/dioxus/src/lib.rs +++ b/packages/dioxus/src/lib.rs @@ -30,18 +30,12 @@ pub use dioxus_core::{CapturedError, Ok, Result}; #[cfg(feature = "launch")] #[cfg_attr(docsrs, doc(cfg(feature = "launch")))] -pub mod launch; - -pub mod events { - #[cfg(feature = "html")] - #[cfg_attr(docsrs, doc(cfg(feature = "html")))] - pub use dioxus_html::prelude::*; -} +mod launch; #[cfg(feature = "launch")] #[cfg_attr(docsrs, doc(cfg(feature = "launch")))] #[allow(deprecated)] -pub use launch::{builder, launch}; +pub use launch::launch; #[cfg(feature = "hooks")] #[cfg_attr(docsrs, doc(cfg(feature = "hooks")))] @@ -51,6 +45,12 @@ pub use dioxus_hooks as hooks; #[cfg_attr(docsrs, doc(cfg(feature = "signals")))] pub use dioxus_signals as signals; +pub mod events { + #[cfg(feature = "html")] + #[cfg_attr(docsrs, doc(cfg(feature = "html")))] + pub use dioxus_html::prelude::*; +} + #[cfg(feature = "html")] #[cfg_attr(docsrs, doc(cfg(feature = "html")))] pub use dioxus_html as html; @@ -159,81 +159,3 @@ pub use dioxus_liveview as liveview; #[cfg(feature = "ssr")] #[cfg_attr(docsrs, doc(cfg(feature = "ssr")))] pub use dioxus_ssr as ssr; - -#[cfg(feature = "asset")] -#[cfg_attr(docsrs, doc(cfg(feature = "asset")))] -pub use manganis as assets; - -#[cfg(feature = "asset")] -#[cfg_attr(docsrs, doc(cfg(feature = "asset")))] -pub use manganis; - -pub mod prelude { - // #[cfg(feature = "launch")] - // #[cfg_attr(docsrs, doc(cfg(feature = "launch")))] - // pub use crate::launch::*; - - #[cfg(feature = "launch")] - #[cfg_attr(docsrs, doc(cfg(feature = "launch")))] - pub use dioxus_config_macro::*; - - #[cfg(feature = "hooks")] - #[cfg_attr(docsrs, doc(cfg(feature = "hooks")))] - pub use crate::hooks::*; - - #[cfg(feature = "signals")] - #[cfg_attr(docsrs, doc(cfg(feature = "signals")))] - pub use dioxus_signals::*; - - pub use dioxus_core::prelude::*; - - #[cfg(feature = "macro")] - #[cfg_attr(docsrs, doc(cfg(feature = "macro")))] - #[allow(deprecated)] - pub use dioxus_core_macro::{component, rsx, Props}; - - #[cfg(feature = "html")] - #[cfg_attr(docsrs, doc(cfg(feature = "html")))] - pub use dioxus_html as dioxus_elements; - - #[cfg(feature = "html")] - #[cfg_attr(docsrs, doc(cfg(feature = "html")))] - pub use dioxus_elements::{global_attributes, prelude::*, svg_attributes}; - - pub use dioxus_core; - - #[cfg(feature = "fullstack")] - #[cfg_attr(docsrs, doc(cfg(feature = "fullstack")))] - pub use dioxus_fullstack::prelude::*; - - #[cfg(all(feature = "static-generation", not(feature = "fullstack")))] - #[cfg_attr( - docsrs, - doc(cfg(all(feature = "static-generation", not(feature = "fullstack")))) - )] - pub use dioxus_static_site_generation::prelude::*; - - #[cfg(feature = "router")] - #[cfg_attr(docsrs, doc(cfg(feature = "router")))] - pub use dioxus_router; - - #[cfg(feature = "router")] - #[cfg_attr(docsrs, doc(cfg(feature = "router")))] - pub use dioxus_router::prelude::*; - - #[cfg(feature = "axum")] - #[cfg_attr(docsrs, doc(cfg(feature = "axum")))] - pub use axum; - - #[cfg(feature = "asset")] - #[cfg_attr(docsrs, doc(cfg(feature = "asset")))] - pub use manganis::{self, self as assets, asset, Asset, ImageAsset, ImageType}; - - #[cfg(feature = "document")] - #[cfg_attr(docsrs, doc(cfg(feature = "document")))] - pub use dioxus_document as document; - - #[cfg(feature = "liveview")] - #[cfg_attr(docsrs, doc(cfg(feature = "liveview")))] - pub use dioxus_liveview as liveview; -} diff --git a/packages/fullstack/Cargo.toml b/packages/fullstack/Cargo.toml index 6693dfc9a1..44a88254cf 100644 --- a/packages/fullstack/Cargo.toml +++ b/packages/fullstack/Cargo.toml @@ -12,11 +12,7 @@ resolver = "2" [dependencies] # server functions -server_fn = { version = "0.6.5", features = [ - "json", - "url", - "browser", -], default-features = false } +server_fn = { version = "0.6.5", features = ["json", "url", "browser"], default-features = false } dioxus_server_macro = { workspace = true } # axum @@ -25,7 +21,6 @@ tower-http = { workspace = true, optional = true, features = ["fs"] } dioxus-lib = { workspace = true } generational-box = { workspace = true } -dioxus-runtime-config = { workspace = true } # Dioxus + SSR dioxus-ssr = { workspace = true, optional = true } @@ -34,9 +29,7 @@ hyper = { workspace = true, optional = true } http = { workspace = true, optional = true } # Web Integration -dioxus-web = { workspace = true, features = [ - "hydrate", -], default-features = false, optional = true } +dioxus-web = { workspace = true, features = ["hydrate"], default-features = false, optional = true } dioxus-interpreter-js = { workspace = true, optional = true } # Desktop Integration @@ -51,7 +44,7 @@ once_cell = { workspace = true } tokio-util = { version = "0.7.8", features = ["rt"], optional = true } async-trait = { version = "0.1.58", optional = true } -serde = { workspace = true } +serde = "1.0.159" tokio-stream = { version = "0.1.12", features = ["sync"], optional = true } futures-util = { workspace = true } futures-channel = { workspace = true } @@ -59,8 +52,6 @@ ciborium = { workspace = true } base64 = { workspace = true } rustls = { workspace = true, optional = true } hyper-rustls = { workspace = true, optional = true } -dioxus-document = { workspace = true } -dioxus-core-types = { workspace = true } pin-project = { version = "1.1.2", optional = true } thiserror = { workspace = true, optional = true } @@ -74,7 +65,6 @@ dioxus-cli-config = { workspace = true, optional = true } dioxus-devtools = { workspace = true, optional = true } aws-lc-rs = { version = "1.8.1", optional = true } -dioxus-devtools = { workspace = true, optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] tokio = { workspace = true, features = ["rt", "sync"], optional = true } @@ -90,21 +80,14 @@ default = ["devtools", "panic_hook", "document", "file_engine", "mounted"] panic_hook = ["dioxus-web?/panic_hook"] devtools = ["dioxus-web?/devtools", "dep:dioxus-devtools"] mounted = ["dioxus-web?/mounted"] -devtools = ["dep:dioxus-devtools", "dioxus-web?/devtools"] +file_engine = ["dioxus-web?/file_engine"] document = ["dioxus-web?/document"] -# web = ["dep:dioxus-web", "dep:web-sys"] -# desktop = ["dep:dioxus-desktop", "server_fn/reqwest", "dioxus_server_macro/reqwest", "server_fn/browser"] -# mobile = ["dep:dioxus-mobile"] +web = ["dep:dioxus-web", "dep:web-sys"] +desktop = ["dep:dioxus-desktop", "server_fn/reqwest", "dioxus_server_macro/reqwest"] +mobile = ["dep:dioxus-mobile"] default-tls = ["server_fn/default-tls"] rustls = ["server_fn/rustls", "dep:rustls", "dep:hyper-rustls"] -axum = [ - "dep:axum", - "dep:tower-http", - "server", - "server_fn/axum", - "dioxus_server_macro/axum", - "default-tls", -] +axum = ["dep:axum", "dep:tower-http", "server", "server_fn/axum", "dioxus_server_macro/axum", "default-tls"] server = [ "server_fn/ssr", "dioxus_server_macro/server", @@ -120,6 +103,7 @@ server = [ "dep:tracing-futures", "dep:pin-project", "dep:thiserror", + "dep:dioxus-cli-config", "dep:async-trait", "dep:parking_lot", "dioxus-interpreter-js", diff --git a/packages/fullstack/src/config.rs b/packages/fullstack/src/config.rs index 1fd9b221d8..52d8051119 100644 --- a/packages/fullstack/src/config.rs +++ b/packages/fullstack/src/config.rs @@ -102,11 +102,4 @@ impl Config { pub fn with_mobile_cfg(self, mobile_cfg: dioxus_mobile::Config) -> Self { Self { mobile_cfg, ..self } } - - /// Set the mobile config. - #[cfg(feature = "mobile")] - #[cfg_attr(docsrs, doc(cfg(feature = "mobile")))] - pub fn set_mobile_cfg(&mut self, mobile_cfg: dioxus_mobile::Config) { - self.mobile_cfg = mobile_cfg; - } } diff --git a/packages/fullstack/src/document/mod.rs b/packages/fullstack/src/document/mod.rs index 49b332628a..0c380994f6 100644 --- a/packages/fullstack/src/document/mod.rs +++ b/packages/fullstack/src/document/mod.rs @@ -2,9 +2,7 @@ #[cfg(feature = "server")] pub(crate) mod server; - #[cfg(feature = "server")] pub use server::ServerDocument; - #[cfg(all(feature = "web", feature = "document"))] pub(crate) mod web; diff --git a/packages/fullstack/src/document/server.rs b/packages/fullstack/src/document/server.rs index 8c478b592c..265cb2a510 100644 --- a/packages/fullstack/src/document/server.rs +++ b/packages/fullstack/src/document/server.rs @@ -4,9 +4,9 @@ use std::cell::RefCell; -use dioxus_document::*; -use dioxus_lib::prelude::*; +use dioxus_lib::{html::document::*, prelude::*}; use dioxus_ssr::Renderer; +use generational_box::GenerationalBox; use once_cell::sync::Lazy; use parking_lot::RwLock; @@ -71,6 +71,10 @@ impl ServerDocument { } impl Document for ServerDocument { + fn new_evaluator(&self, js: String) -> GenerationalBox> { + NoOpDocument.new_evaluator(js) + } + fn set_title(&self, title: String) { self.warn_if_streaming(); self.serialize_for_hydration(); @@ -111,7 +115,7 @@ impl Document for ServerDocument { }); } - fn create_link(&self, props: LinkProps) { + fn create_link(&self, props: head::LinkProps) { self.warn_if_streaming(); self.serialize_for_hydration(); self.0.borrow_mut().link.push(rsx! { @@ -137,17 +141,4 @@ impl Document for ServerDocument { fn as_any(&self) -> &dyn std::any::Any { self } - - fn eval(&self, js: String) -> Eval { - todo!("server evalator") - } - - fn create_head_element( - &self, - name: &str, - attributes: Vec<(&str, String)>, - contents: Option, - ) { - todo!() - } } diff --git a/packages/fullstack/src/document/web.rs b/packages/fullstack/src/document/web.rs index 28726284fd..3169fa05a3 100644 --- a/packages/fullstack/src/document/web.rs +++ b/packages/fullstack/src/document/web.rs @@ -1,7 +1,7 @@ #![allow(unused)] //! On the client, we use the [`WebDocument`] implementation to render the head for any elements that were not rendered on the server. -use dioxus_document::{Document, LinkProps, MetaProps, ScriptProps, StyleProps}; +use dioxus_lib::events::Document; use dioxus_web::WebDocument; fn head_element_written_on_server() -> bool { @@ -11,61 +11,52 @@ fn head_element_written_on_server() -> bool { .unwrap_or_default() } -pub(crate) struct FullstackWebDocument {} - -impl FullstackWebDocument { - pub(crate) fn new() -> Self { - Self {} - } -} +pub(crate) struct FullstackWebDocument; impl Document for FullstackWebDocument { - fn create_head_element( + fn new_evaluator( &self, - name: &str, - attributes: Vec<(&str, String)>, - contents: Option, - ) { - if head_element_written_on_server() { - return; - } - - WebDocument::get().create_head_element(name, attributes, contents); + js: String, + ) -> generational_box::GenerationalBox> { + WebDocument.new_evaluator(js) } fn set_title(&self, title: String) { if head_element_written_on_server() { return; } - - WebDocument::get().set_title(title); - } - - fn eval(&self, js: String) -> dioxus_document::Eval { - WebDocument::get().eval(js) - } - - fn as_any(&self) -> &dyn std::any::Any { - self + WebDocument.set_title(title); } - fn current_route(&self) -> String { - todo!() + fn create_meta(&self, props: dioxus_lib::prelude::MetaProps) { + if head_element_written_on_server() { + return; + } + WebDocument.create_meta(props); } - fn go_back(&mut self) { - todo!() + fn create_script(&self, props: dioxus_lib::prelude::ScriptProps) { + if head_element_written_on_server() { + return; + } + WebDocument.create_script(props); } - fn go_forward(&mut self) { - todo!() + fn create_style(&self, props: dioxus_lib::prelude::StyleProps) { + if head_element_written_on_server() { + return; + } + WebDocument.create_style(props); } - fn push_route(&mut self, route: String) { - todo!() + fn create_link(&self, props: dioxus_lib::prelude::head::LinkProps) { + if head_element_written_on_server() { + return; + } + WebDocument.create_link(props); } - fn replace(&mut self, path: String) { - todo!() + fn as_any(&self) -> &dyn std::any::Any { + self } } diff --git a/packages/fullstack/src/html_storage/serialize.rs b/packages/fullstack/src/html_storage/serialize.rs index 1c2528448b..535c368285 100644 --- a/packages/fullstack/src/html_storage/serialize.rs +++ b/packages/fullstack/src/html_storage/serialize.rs @@ -1,5 +1,3 @@ -use base64::engine::general_purpose::STANDARD; -use base64::Engine; use dioxus_lib::prelude::dioxus_core::DynamicNode; use dioxus_lib::prelude::{ has_context, try_consume_context, ErrorContext, ScopeId, SuspenseBoundaryProps, @@ -7,6 +5,9 @@ use dioxus_lib::prelude::{ }; use serde::Serialize; +use base64::engine::general_purpose::STANDARD; +use base64::Engine; + use super::SerializeContext; #[allow(unused)] diff --git a/packages/fullstack/src/launch.rs b/packages/fullstack/src/launch.rs index 93b7a948e4..e590efc41e 100644 --- a/packages/fullstack/src/launch.rs +++ b/packages/fullstack/src/launch.rs @@ -62,8 +62,8 @@ pub fn launch( #[cfg(feature = "document")] let factory = move || { let mut vdom = factory(); - let document = std::rc::Rc::new(crate::document::web::FullstackWebDocument::new()) - as std::rc::Rc; + let document = std::rc::Rc::new(crate::document::web::FullstackWebDocument) + as std::rc::Rc; vdom.provide_root_context(document); vdom }; @@ -160,7 +160,7 @@ async fn launch_server( } let router = router.into_make_service(); - let listener = tokio::net::TcpListener::bind(serve_address).await.unwrap(); + let listener = tokio::net::TcpListener::bind(address).await.unwrap(); axum::serve(listener, router).await.unwrap(); } diff --git a/packages/fullstack/src/render.rs b/packages/fullstack/src/render.rs index 9f4cdbb709..526985108c 100644 --- a/packages/fullstack/src/render.rs +++ b/packages/fullstack/src/render.rs @@ -1,6 +1,5 @@ //! A shared pool of renderers for efficient server side rendering. use crate::streaming::{Mount, StreamingRenderer}; -use dioxus_document::Document; use dioxus_interpreter_js::INITIALIZE_STREAMING_JS; use dioxus_isrg::{CachedRender, RenderFreshness}; use dioxus_ssr::Renderer; @@ -449,7 +448,7 @@ impl FullstackHTMLTemplate { } to.write_str(&index.head_after_title)?; - let document: Option> = + let document: Option> = virtual_dom.in_runtime(|| ScopeId::ROOT.consume_context()); let document: Option<&crate::document::server::ServerDocument> = document .as_ref() diff --git a/packages/fullstack/src/serve_config.rs b/packages/fullstack/src/serve_config.rs index 8bdf2093c3..3f1482b95f 100644 --- a/packages/fullstack/src/serve_config.rs +++ b/packages/fullstack/src/serve_config.rs @@ -77,12 +77,12 @@ impl ServeConfigBuilder { /// Get the path to the public assets directory to serve static files from pub(crate) fn public_path() -> PathBuf { - // The CLI always bundles static assets into the exe/web directory + // The CLI always bundles static assets into the exe/public directory std::env::current_exe() .expect("Failed to get current executable path") .parent() .unwrap() - .join("web") + .join("public") } /// An error that can occur when loading the index.html file diff --git a/packages/html/Cargo.toml b/packages/html/Cargo.toml index 96545c5534..a123602542 100644 --- a/packages/html/Cargo.toml +++ b/packages/html/Cargo.toml @@ -11,11 +11,12 @@ keywords = ["dom", "ui", "gui", "react"] [dependencies] dioxus-core = { workspace = true } +dioxus-core-macro = { workspace = true } dioxus-core-types = { workspace = true } dioxus-rsx = { workspace = true, optional = true } -dioxus-rsx-hotreload = { workspace = true, optional = true } dioxus-html-internal-macro = { workspace = true } - +dioxus-hooks = { workspace = true } +generational-box = { workspace = true } serde = { version = "1", features = ["derive"], optional = true } serde_repr = { version = "0.1", optional = true } js-sys = { version = "0.3.56", optional = true } @@ -23,8 +24,11 @@ euclid = "0.22.7" enumset = "1.1.2" keyboard-types = { version = "0.7", default-features = false } async-trait = { version = "0.1.58", optional = true } +tokio = { workspace = true, features = ["fs", "io-util"], optional = true } +futures-channel = { workspace = true } serde_json = { version = "1", optional = true } tracing.workspace = true +rustversion = "1.0.17" [build-dependencies] lazy-js-bundle = { workspace = true } diff --git a/packages/core-types/src/attributes.rs b/packages/html/assets/script.js similarity index 100% rename from packages/core-types/src/attributes.rs rename to packages/html/assets/script.js diff --git a/packages/core-types/src/core.rs b/packages/html/assets/style.css similarity index 100% rename from packages/core-types/src/core.rs rename to packages/html/assets/style.css diff --git a/packages/document/docs/eval.md b/packages/html/docs/eval.md similarity index 100% rename from packages/document/docs/eval.md rename to packages/html/docs/eval.md diff --git a/packages/document/docs/head.md b/packages/html/docs/head.md similarity index 100% rename from packages/document/docs/head.md rename to packages/html/docs/head.md diff --git a/packages/html/src/document/eval.rs b/packages/html/src/document/eval.rs new file mode 100644 index 0000000000..cd9d2aa0f6 --- /dev/null +++ b/packages/html/src/document/eval.rs @@ -0,0 +1,134 @@ +#![allow(clippy::await_holding_refcell_ref)] +#![doc = include_str!("../../docs/eval.md")] + +use dioxus_core::prelude::*; +use generational_box::GenerationalBox; +use std::error::Error; +use std::fmt::Display; +use std::future::{poll_fn, Future, IntoFuture}; +use std::pin::Pin; +use std::rc::Rc; +use std::task::{Context, Poll}; + +use super::document; + +/// The platform's evaluator. +pub trait Evaluator { + /// Sends a message to the evaluated JavaScript. + fn send(&self, data: serde_json::Value) -> Result<(), EvalError>; + /// Receive any queued messages from the evaluated JavaScript. + fn poll_recv( + &mut self, + context: &mut Context<'_>, + ) -> Poll>; + /// Gets the return value of the JavaScript + fn poll_join( + &mut self, + context: &mut Context<'_>, + ) -> Poll>; +} + +type EvalCreator = Rc UseEval>; + +/// Get a struct that can execute any JavaScript. +/// +/// # Safety +/// +/// Please be very careful with this function. A script with too many dynamic +/// parts is practically asking for a hacker to find an XSS vulnerability in +/// it. **This applies especially to web targets, where the JavaScript context +/// has access to most, if not all of your application data.** +#[must_use] +pub fn eval_provider() -> EvalCreator { + let eval_provider = document(); + + Rc::new(move |script: &str| UseEval::new(eval_provider.new_evaluator(script.to_string()))) + as Rc UseEval> +} + +#[doc = include_str!("../../docs/eval.md")] +#[doc(alias = "javascript")] +pub fn eval(script: &str) -> UseEval { + let document = use_hook(document); + UseEval::new(document.new_evaluator(script.to_string())) +} + +/// A wrapper around the target platform's evaluator that lets you send and receive data from JavaScript spawned by [`eval`]. +/// +#[doc = include_str!("../../docs/eval.md")] +#[derive(Clone, Copy)] +pub struct UseEval { + evaluator: GenerationalBox>, +} + +impl UseEval { + /// Creates a new UseEval + pub fn new(evaluator: GenerationalBox>) -> Self { + Self { evaluator } + } + + /// Sends a [`serde_json::Value`] to the evaluated JavaScript. + pub fn send(&self, data: serde_json::Value) -> Result<(), EvalError> { + match self.evaluator.try_read() { + Ok(evaluator) => evaluator.send(data), + Err(_) => Err(EvalError::Finished), + } + } + + /// Gets an UnboundedReceiver to receive messages from the evaluated JavaScript. + pub async fn recv(&mut self) -> Result { + poll_fn(|cx| match self.evaluator.try_write() { + Ok(mut evaluator) => evaluator.poll_recv(cx), + Err(_) => Poll::Ready(Err(EvalError::Finished)), + }) + .await + } + + /// Gets the return value of the evaluated JavaScript. + pub async fn join(self) -> Result { + poll_fn(|cx| match self.evaluator.try_write() { + Ok(mut evaluator) => evaluator.poll_join(cx), + Err(_) => Poll::Ready(Err(EvalError::Finished)), + }) + .await + } +} + +impl IntoFuture for UseEval { + type Output = Result; + type IntoFuture = Pin>>; + + fn into_future(self) -> Self::IntoFuture { + Box::pin(self.join()) + } +} + +/// Represents an error when evaluating JavaScript +#[derive(Debug)] +#[non_exhaustive] +pub enum EvalError { + /// The platform does not support evaluating JavaScript. + Unsupported, + + /// The provided JavaScript has already been ran. + Finished, + + /// The provided JavaScript is not valid and can't be ran. + InvalidJs(String), + + /// Represents an error communicating between JavaScript and Rust. + Communication(String), +} + +impl Display for EvalError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + EvalError::Unsupported => write!(f, "EvalError::Unsupported - eval is not supported on the current platform"), + EvalError::Finished => write!(f, "EvalError::Finished - eval has already ran"), + EvalError::InvalidJs(_) => write!(f, "EvalError::InvalidJs - the provided javascript is invalid"), + EvalError::Communication(_) => write!(f, "EvalError::Communication - there was an error trying to communicate with between javascript and rust"), + } + } +} + +impl Error for EvalError {} diff --git a/packages/document/src/head.rs b/packages/html/src/document/head.rs similarity index 94% rename from packages/document/src/head.rs rename to packages/html/src/document/head.rs index eff1be2f88..4bd923b24c 100644 --- a/packages/document/src/head.rs +++ b/packages/html/src/document/head.rs @@ -1,4 +1,4 @@ -#![doc = include_str!("../docs/head.md")] +#![doc = include_str!("../../docs/head.md")] use std::{cell::RefCell, collections::HashSet, rc::Rc}; @@ -123,7 +123,7 @@ pub struct MetaProps { } impl MetaProps { - pub fn attributes(&self) -> Vec<(&'static str, String)> { + pub(crate) fn attributes(&self) -> Vec<(&'static str, String)> { let mut attributes = Vec::new(); if let Some(property) = &self.property { attributes.push(("property", property.clone())); @@ -196,7 +196,7 @@ pub struct ScriptProps { } impl ScriptProps { - pub fn attributes(&self) -> Vec<(&'static str, String)> { + pub(crate) fn attributes(&self) -> Vec<(&'static str, String)> { let mut attributes = Vec::new(); if let Some(defer) = &self.defer { attributes.push(("defer", defer.to_string())); @@ -248,7 +248,7 @@ impl ScriptProps { /// rsx! { /// // You can use the Script component to render a script tag into the head of the page /// Script { -/// src: asset!("/assets/script.js"), +/// src: asset!("./assets/script.js"), /// } /// } /// } @@ -289,7 +289,7 @@ pub struct StyleProps { } impl StyleProps { - pub fn attributes(&self) -> Vec<(&'static str, String)> { + pub(crate) fn attributes(&self) -> Vec<(&'static str, String)> { let mut attributes = Vec::new(); if let Some(href) = &self.href { attributes.push(("href", href.clone())); @@ -377,7 +377,7 @@ pub struct LinkProps { } impl LinkProps { - pub fn attributes(&self) -> Vec<(&'static str, String)> { + pub(crate) fn attributes(&self) -> Vec<(&'static str, String)> { let mut attributes = Vec::new(); if let Some(rel) = &self.rel { attributes.push(("rel", rel.clone())); @@ -437,8 +437,8 @@ impl LinkProps { /// rsx! { /// // You can use the meta component to render a meta tag into the head of the page /// // This meta tag will redirect the user to the dioxuslabs homepage in 10 seconds -/// document::Link { -/// href: asset!("/assets/style.css"), +/// head::Link { +/// href: asset!("./assets/style.css"), /// rel: "stylesheet", /// } /// } @@ -468,29 +468,6 @@ pub fn Link(props: LinkProps) -> Element { VNode::empty() } -/// Render a `` element with a `rel="stylesheet"` attribute by default. -/// -/// # Example -/// -/// ```rust, no_run -/// # use dioxus::prelude::*; -/// fn App() -> Element { -/// rsx! { -/// Stylesheet { -/// href: "https://example.com/styles.css", -/// } -/// } -/// } -/// ``` -#[doc(alias = "")] -#[component] -pub fn Stylesheet(props: LinkProps) -> Element { - Link(LinkProps { - rel: Some("stylesheet".to_string()), - ..props - }) -} - fn get_or_insert_root_context() -> T { match ScopeId::ROOT.has_context::() { Some(context) => context, diff --git a/packages/html/src/events/animation.rs b/packages/html/src/events/animation.rs index 5bed6c6f53..23f6219121 100644 --- a/packages/html/src/events/animation.rs +++ b/packages/html/src/events/animation.rs @@ -1,4 +1,4 @@ -use dioxus_core_types::Event; +use dioxus_core::Event; pub type AnimationEvent = Event; diff --git a/packages/html/src/events/clipboard.rs b/packages/html/src/events/clipboard.rs index d159f05908..265a747e9a 100644 --- a/packages/html/src/events/clipboard.rs +++ b/packages/html/src/events/clipboard.rs @@ -1,4 +1,4 @@ -use dioxus_core_types::Event; +use dioxus_core::Event; pub type ClipboardEvent = Event; diff --git a/packages/html/src/events/composition.rs b/packages/html/src/events/composition.rs index 9c57f9642e..4d161436ed 100644 --- a/packages/html/src/events/composition.rs +++ b/packages/html/src/events/composition.rs @@ -1,4 +1,4 @@ -use dioxus_core_types::Event; +use dioxus_core::Event; pub type CompositionEvent = Event; diff --git a/packages/html/src/events/drag.rs b/packages/html/src/events/drag.rs index 4316f6951c..63cdffad8f 100644 --- a/packages/html/src/events/drag.rs +++ b/packages/html/src/events/drag.rs @@ -2,7 +2,7 @@ use crate::geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenP use crate::input_data::{MouseButton, MouseButtonSet}; use crate::prelude::*; -use dioxus_core_types::Event; +use dioxus_core::Event; use keyboard_types::Modifiers; use crate::HasMouseData; diff --git a/packages/html/src/events/focus.rs b/packages/html/src/events/focus.rs index f5335c35ac..c5571e7ffd 100644 --- a/packages/html/src/events/focus.rs +++ b/packages/html/src/events/focus.rs @@ -1,4 +1,4 @@ -use dioxus_core_types::Event; +use dioxus_core::Event; pub type FocusEvent = Event; diff --git a/packages/html/src/events/form.rs b/packages/html/src/events/form.rs index a5469adb58..feaadffe66 100644 --- a/packages/html/src/events/form.rs +++ b/packages/html/src/events/form.rs @@ -1,7 +1,7 @@ use crate::file_data::HasFileData; use std::{collections::HashMap, fmt::Debug, ops::Deref}; -use dioxus_core_types::Event; +use dioxus_core::Event; pub type FormEvent = Event; diff --git a/packages/html/src/events/image.rs b/packages/html/src/events/image.rs index 55a5a3fda3..3c75edbdd1 100644 --- a/packages/html/src/events/image.rs +++ b/packages/html/src/events/image.rs @@ -1,4 +1,4 @@ -use dioxus_core_types::Event; +use dioxus_core::Event; pub type ImageEvent = Event; pub struct ImageData { diff --git a/packages/html/src/events/keyboard.rs b/packages/html/src/events/keyboard.rs index 90db7e4b2e..172991f976 100644 --- a/packages/html/src/events/keyboard.rs +++ b/packages/html/src/events/keyboard.rs @@ -1,4 +1,4 @@ -use dioxus_core_types::Event; +use dioxus_core::Event; use keyboard_types::{Code, Key, Location, Modifiers}; use std::fmt::Debug; diff --git a/packages/html/src/events/media.rs b/packages/html/src/events/media.rs index 1df16d5b58..f7ea3d6b7e 100644 --- a/packages/html/src/events/media.rs +++ b/packages/html/src/events/media.rs @@ -1,4 +1,4 @@ -use dioxus_core_types::Event; +use dioxus_core::Event; pub type MediaEvent = Event; pub struct MediaData { diff --git a/packages/html/src/events/mounted.rs b/packages/html/src/events/mounted.rs index c798ff5e25..ed021b6aed 100644 --- a/packages/html/src/events/mounted.rs +++ b/packages/html/src/events/mounted.rs @@ -10,45 +10,42 @@ use std::{ /// /// Different platforms will have different implementations and different levels of support for this trait. Renderers that do not support specific features will return `None` for those queries. // we can not use async_trait here because it does not create a trait that is object safe -// #[async_trait::async_trait(?Send)] -pub trait MountedElement: std::any::Any { +pub trait RenderedElementBacking: std::any::Any { /// return self as Any fn as_any(&self) -> &dyn std::any::Any; /// Get the number of pixels that an element's content is scrolled - fn get_scroll_offset(&self) -> MountedResult { - // Err(MountedError::NotSupported) - todo!() + fn get_scroll_offset(&self) -> Pin>>> { + Box::pin(async { Err(MountedError::NotSupported) }) } /// Get the size of an element's content, including content not visible on the screen due to overflow - - fn get_scroll_size(&self) -> MountedResult { - // Err(MountedError::NotSupported) - todo!() + #[allow(clippy::type_complexity)] + fn get_scroll_size(&self) -> Pin>>> { + Box::pin(async { Err(MountedError::NotSupported) }) } /// Get the bounding rectangle of the element relative to the viewport (this does not include the scroll position) #[allow(clippy::type_complexity)] - fn get_client_rect(&self) -> MountedResult { - // Err(MountedError::NotSupported) - todo!() + fn get_client_rect(&self) -> Pin>>> { + Box::pin(async { Err(MountedError::NotSupported) }) } /// Scroll to make the element visible - fn scroll_to(&self, _behavior: ScrollBehavior) -> MountedResult<()> { - // Err(MountedError::NotSupported) - todo!() + fn scroll_to( + &self, + _behavior: ScrollBehavior, + ) -> Pin>>> { + Box::pin(async { Err(MountedError::NotSupported) }) } /// Set the focus on the element - fn set_focus(&self, _focus: bool) -> MountedResult<()> { - // Err(MountedError::NotSupported) - todo!() + fn set_focus(&self, _focus: bool) -> Pin>>> { + Box::pin(async { Err(MountedError::NotSupported) }) } } -impl MountedElement for () { +impl RenderedElementBacking for () { fn as_any(&self) -> &dyn std::any::Any { self } @@ -70,7 +67,7 @@ pub enum ScrollBehavior { /// /// Different platforms will have different implementations and different levels of support for this trait. Renderers that do not support specific features will return `None` for those queries. pub struct MountedData { - inner: Box, + inner: Box, } impl Debug for MountedData { @@ -87,7 +84,7 @@ impl From for MountedData { impl MountedData { /// Create a new MountedData - pub fn new(registry: impl MountedElement + 'static) -> Self { + pub fn new(registry: impl RenderedElementBacking + 'static) -> Self { Self { inner: Box::new(registry), } @@ -97,38 +94,36 @@ impl MountedData { #[doc(alias = "scrollTop")] #[doc(alias = "scrollLeft")] pub async fn get_scroll_offset(&self) -> MountedResult { - // self.inner.get_scroll_offset().await - todo!() + self.inner.get_scroll_offset().await } /// Get the size of an element's content, including content not visible on the screen due to overflow #[doc(alias = "scrollWidth")] #[doc(alias = "scrollHeight")] pub async fn get_scroll_size(&self) -> MountedResult { - // self.inner.get_scroll_size().await - todo!() + self.inner.get_scroll_size().await } /// Get the bounding rectangle of the element relative to the viewport (this does not include the scroll position) #[doc(alias = "getBoundingClientRect")] pub async fn get_client_rect(&self) -> MountedResult { - // self.inner.get_client_rect().await - todo!() + self.inner.get_client_rect().await } /// Scroll to make the element visible #[doc(alias = "scrollIntoView")] - pub async fn scroll_to(&self, behavior: ScrollBehavior) -> MountedResult<()> { - // self.inner.scroll_to(behavior).await - todo!() + pub fn scroll_to( + &self, + behavior: ScrollBehavior, + ) -> Pin>>> { + self.inner.scroll_to(behavior) } /// Set the focus on the element #[doc(alias = "focus")] #[doc(alias = "blur")] - pub async fn set_focus(&self, focus: bool) -> MountedResult<()> { - // self.inner.set_focus(focus).await - todo!() + pub fn set_focus(&self, focus: bool) -> Pin>>> { + self.inner.set_focus(focus) } /// Downcast this event to a concrete event type @@ -138,7 +133,7 @@ impl MountedData { } } -use dioxus_core_types::Event; +use dioxus_core::Event; use crate::geometry::{PixelsRect, PixelsSize, PixelsVector2D}; diff --git a/packages/html/src/events/mouse.rs b/packages/html/src/events/mouse.rs index 65d10bd77e..ed50c8ae7c 100644 --- a/packages/html/src/events/mouse.rs +++ b/packages/html/src/events/mouse.rs @@ -1,7 +1,7 @@ use crate::geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint}; use crate::input_data::{MouseButton, MouseButtonSet}; use crate::prelude::*; -use dioxus_core_types::Event; +use dioxus_core::Event; use keyboard_types::Modifiers; pub type MouseEvent = Event; diff --git a/packages/html/src/events/pointer.rs b/packages/html/src/events/pointer.rs index f25df4c2d9..312f14879f 100644 --- a/packages/html/src/events/pointer.rs +++ b/packages/html/src/events/pointer.rs @@ -1,4 +1,4 @@ -use dioxus_core_types::Event; +use dioxus_core::Event; use keyboard_types::Modifiers; use crate::{geometry::*, input_data::*, prelude::*}; diff --git a/packages/html/src/events/resize.rs b/packages/html/src/events/resize.rs index 6995b88437..e489fc9484 100644 --- a/packages/html/src/events/resize.rs +++ b/packages/html/src/events/resize.rs @@ -126,7 +126,7 @@ pub trait HasResizeData: std::any::Any { fn as_any(&self) -> &dyn std::any::Any; } -use dioxus_core_types::Event; +use dioxus_core::Event; use crate::geometry::PixelsSize; diff --git a/packages/html/src/events/scroll.rs b/packages/html/src/events/scroll.rs index ffff928c9e..0ee2a2de72 100644 --- a/packages/html/src/events/scroll.rs +++ b/packages/html/src/events/scroll.rs @@ -1,4 +1,4 @@ -use dioxus_core_types::Event; +use dioxus_core::Event; pub type ScrollEvent = Event; diff --git a/packages/html/src/events/selection.rs b/packages/html/src/events/selection.rs index 97d3af2eec..0f23a4ad2d 100644 --- a/packages/html/src/events/selection.rs +++ b/packages/html/src/events/selection.rs @@ -1,4 +1,4 @@ -use dioxus_core_types::Event; +use dioxus_core::Event; pub type SelectionEvent = Event; diff --git a/packages/html/src/events/toggle.rs b/packages/html/src/events/toggle.rs index 14dcdd1e50..c8d352ed9d 100644 --- a/packages/html/src/events/toggle.rs +++ b/packages/html/src/events/toggle.rs @@ -1,4 +1,4 @@ -use dioxus_core_types::Event; +use dioxus_core::Event; pub type ToggleEvent = Event; diff --git a/packages/html/src/events/touch.rs b/packages/html/src/events/touch.rs index 8a0229f3c1..999c9717bf 100644 --- a/packages/html/src/events/touch.rs +++ b/packages/html/src/events/touch.rs @@ -1,4 +1,4 @@ -use dioxus_core_types::Event; +use dioxus_core::Event; use keyboard_types::Modifiers; use crate::geometry::*; diff --git a/packages/html/src/events/transition.rs b/packages/html/src/events/transition.rs index 04aaf8a5c1..18863318dd 100644 --- a/packages/html/src/events/transition.rs +++ b/packages/html/src/events/transition.rs @@ -1,4 +1,4 @@ -use dioxus_core_types::Event; +use dioxus_core::Event; pub type TransitionEvent = Event; diff --git a/packages/html/src/events/wheel.rs b/packages/html/src/events/wheel.rs index 41f0e026d4..0a51be8176 100644 --- a/packages/html/src/events/wheel.rs +++ b/packages/html/src/events/wheel.rs @@ -1,4 +1,4 @@ -use dioxus_core_types::Event; +use dioxus_core::Event; use std::fmt::Formatter; use crate::geometry::*; diff --git a/packages/html/src/js/head.js b/packages/html/src/js/head.js new file mode 100644 index 0000000000..f4d2c362aa --- /dev/null +++ b/packages/html/src/js/head.js @@ -0,0 +1 @@ +var createElementInHead=function(tag,attributes,children){const element=document.createElement(tag);for(let[key,value]of attributes)element.setAttribute(key,value);if(children)element.appendChild(document.createTextNode(children));document.head.appendChild(element)};window.createElementInHead=createElementInHead; diff --git a/packages/html/src/lib.rs b/packages/html/src/lib.rs index 32b6978181..0067a90683 100644 --- a/packages/html/src/lib.rs +++ b/packages/html/src/lib.rs @@ -17,13 +17,10 @@ //! Currently, we don't validate for structures, but do validate attributes. pub mod elements; - #[cfg(feature = "hot-reload-context")] pub use elements::HtmlCtx; - #[cfg(feature = "html-to-rsx")] pub use elements::{map_html_attribute_to_rsx, map_html_element_to_rsx}; - pub mod events; pub(crate) mod file_data; pub use file_data::*; @@ -42,6 +39,10 @@ pub use transit::*; pub use attribute_groups::*; pub use elements::*; pub use events::*; +pub use render_template::*; + +#[cfg(feature = "document")] +pub mod document; pub mod extensions { pub use crate::attribute_groups::{GlobalAttributesExtension, SvgAttributesExtension}; @@ -50,6 +51,11 @@ pub mod extensions { pub mod prelude { pub use crate::attribute_groups::{GlobalAttributesExtension, SvgAttributesExtension}; + #[cfg(feature = "document")] + pub use crate::document::{ + self, document, eval, head, Document, Meta, MetaProps, Script, ScriptProps, Style, + StyleProps, Title, TitleProps, UseEval, + }; pub use crate::elements::extensions::*; pub use crate::events::*; pub use crate::point_interaction::*; diff --git a/packages/html/src/render_template.rs b/packages/html/src/render_template.rs new file mode 100644 index 0000000000..3705095385 --- /dev/null +++ b/packages/html/src/render_template.rs @@ -0,0 +1,40 @@ +use dioxus_core::{Template, TemplateAttribute, TemplateNode}; +use std::fmt::Write; + +/// Render a template to an HTML string +/// +/// Useful for sending over the wire. Can be used to with innerHtml to create templates with little work +pub fn render_template_to_html(template: &Template) -> String { + let mut out = String::new(); + + for root in template.roots { + render_template_node(root, &mut out).unwrap(); + } + + out +} + +fn render_template_node(node: &TemplateNode, out: &mut String) -> std::fmt::Result { + match node { + TemplateNode::Element { + tag, + attrs, + children, + .. + } => { + write!(out, "<{tag}")?; + for attr in *attrs { + if let TemplateAttribute::Static { name, value, .. } = attr { + write!(out, "{name}=\"{value}\"")?; + } + } + for child in *children { + render_template_node(child, out)?; + } + write!(out, "")?; + } + TemplateNode::Text { text: t } => write!(out, "{t}")?, + TemplateNode::Dynamic { id: _ } => write!(out, "")?, + }; + Ok(()) +} diff --git a/packages/html/src/ts/.gitignore b/packages/html/src/ts/.gitignore new file mode 100644 index 0000000000..68b79b0d33 --- /dev/null +++ b/packages/html/src/ts/.gitignore @@ -0,0 +1,3 @@ +# please dont accidentally run tsc and commit your js in this dir. +*.js + diff --git a/packages/html/src/ts/head.ts b/packages/html/src/ts/head.ts new file mode 100644 index 0000000000..d01c0aaffb --- /dev/null +++ b/packages/html/src/ts/head.ts @@ -0,0 +1,19 @@ +// Helper functions for working with the document head + +function createElementInHead( + tag: string, + attributes: [string, string][], + children: string | null +): void { + const element = document.createElement(tag); + for (const [key, value] of attributes) { + element.setAttribute(key, value); + } + if (children) { + element.appendChild(document.createTextNode(children)); + } + document.head.appendChild(element); +} + +// @ts-ignore +window.createElementInHead = createElementInHead; diff --git a/packages/web/tsconfig.json b/packages/html/tsconfig.json similarity index 100% rename from packages/web/tsconfig.json rename to packages/html/tsconfig.json diff --git a/packages/interpreter/Cargo.toml b/packages/interpreter/Cargo.toml index d0c8086879..3839eb0f11 100644 --- a/packages/interpreter/Cargo.toml +++ b/packages/interpreter/Cargo.toml @@ -13,15 +13,19 @@ keywords = ["dom", "ui", "gui", "react", "wasm"] [dependencies] wasm-bindgen = { workspace = true, optional = true } wasm-bindgen-futures = { workspace = true, optional = true } -js-sys = { workspace = true, optional = true } -web-sys = { workspace = true, optional = true, features = ["Element", "Node"] } -serde = { workspace = true, optional = true, features = ["derive"] } +js-sys = { version = "0.3.56", optional = true } +web-sys = { version = "0.3.56", optional = true, features = [ + "Element", + "Node", +] } +sledgehammer_bindgen = { version = "0.6.0", default-features = false, optional = true } +sledgehammer_utils = { version = "0.3.1", optional = true } +serde = { version = "1.0", features = ["derive"], optional = true } rustc-hash = { workspace = true, optional = true } dioxus-core = { workspace = true, optional = true } dioxus-core-types = { workspace = true, optional = true } -sledgehammer_bindgen = { version = "0.6.0", default-features = false, optional = true } -sledgehammer_utils = { version = "0.3.1", optional = true } +dioxus-html = { workspace = true, optional = true } [build-dependencies] lazy-js-bundle = { workspace = true } @@ -38,5 +42,5 @@ webonly = [ "dep:web-sys", "sledgehammer_bindgen/web", ] -binary-protocol = ["sledgehammer", "dep:dioxus-core", "dep:dioxus-core-types"] +binary-protocol = ["sledgehammer", "dep:dioxus-core", "dep:dioxus-core-types", "dep:dioxus-html"] minimal_bindings = [] diff --git a/packages/interpreter/src/js/common.js b/packages/interpreter/src/js/common.js index 9b3af6cc8c..f0d690b40c 100644 --- a/packages/interpreter/src/js/common.js +++ b/packages/interpreter/src/js/common.js @@ -1 +1 @@ -function setAttributeInner(node,field,value,ns){if(ns==="style"){node.style.setProperty(field,value);return}if(ns){node.setAttributeNS(ns,field,value);return}switch(field){case"value":if(node.value!==value)node.value=value;break;case"initial_value":node.defaultValue=value;break;case"checked":node.checked=truthy(value);break;case"initial_checked":node.defaultChecked=truthy(value);break;case"selected":node.selected=truthy(value);break;case"initial_selected":node.defaultSelected=truthy(value);break;case"dangerous_inner_html":node.innerHTML=value;break;default:if(!truthy(value)&&isBoolAttr(field))node.removeAttribute(field);else node.setAttribute(field,value)}}var truthy=function(val){return val==="true"||val===!0},isBoolAttr=function(field){switch(field){case"allowfullscreen":case"allowpaymentrequest":case"async":case"autofocus":case"autoplay":case"checked":case"controls":case"default":case"defer":case"disabled":case"formnovalidate":case"hidden":case"ismap":case"itemscope":case"loop":case"multiple":case"muted":case"nomodule":case"novalidate":case"open":case"playsinline":case"readonly":case"required":case"reversed":case"selected":case"truespeed":case"webkitdirectory":return!0;default:return!1}};function retrieveFormValues(form){const formData=new FormData(form),contents={};return formData.forEach((value,key)=>{if(contents[key])contents[key].push(value);else contents[key]=[value]}),{valid:form.checkValidity(),values:contents}}export{setAttributeInner,retrieveFormValues}; +function setAttributeInner(node,field,value,ns){if(ns==="style"){node.style.setProperty(field,value);return}if(ns){node.setAttributeNS(ns,field,value);return}switch(field){case"value":if(node.value!==value)node.value=value;break;case"initial_value":node.defaultValue=value;break;case"checked":node.checked=truthy(value);break;case"initial_checked":node.defaultChecked=truthy(value);break;case"selected":node.selected=truthy(value);break;case"initial_selected":node.defaultSelected=truthy(value);break;case"dangerous_inner_html":node.innerHTML=value;break;default:if(!truthy(value)&&isBoolAttr(field))node.removeAttribute(field);else node.setAttribute(field,value)}}function truthy(val){return val==="true"||val===!0}function isBoolAttr(field){switch(field){case"allowfullscreen":case"allowpaymentrequest":case"async":case"autofocus":case"autoplay":case"checked":case"controls":case"default":case"defer":case"disabled":case"formnovalidate":case"hidden":case"ismap":case"itemscope":case"loop":case"multiple":case"muted":case"nomodule":case"novalidate":case"open":case"playsinline":case"readonly":case"required":case"reversed":case"selected":case"truespeed":case"webkitdirectory":return!0;default:return!1}}function retrieveFormValues(form){const formData=new FormData(form),contents={};return formData.forEach((value,key)=>{if(contents[key])contents[key].push(value);else contents[key]=[value]}),{valid:form.checkValidity(),values:contents}}export{setAttributeInner,retrieveFormValues}; diff --git a/packages/interpreter/src/js/core.js b/packages/interpreter/src/js/core.js index 01bc2f8948..377e0ed3fc 100644 --- a/packages/interpreter/src/js/core.js +++ b/packages/interpreter/src/js/core.js @@ -1 +1 @@ -function setAttributeInner(node,field,value,ns){if(ns==="style"){node.style.setProperty(field,value);return}if(ns){node.setAttributeNS(ns,field,value);return}switch(field){case"value":if(node.value!==value)node.value=value;break;case"initial_value":node.defaultValue=value;break;case"checked":node.checked=truthy(value);break;case"initial_checked":node.defaultChecked=truthy(value);break;case"selected":node.selected=truthy(value);break;case"initial_selected":node.defaultSelected=truthy(value);break;case"dangerous_inner_html":node.innerHTML=value;break;default:if(!truthy(value)&&isBoolAttr(field))node.removeAttribute(field);else node.setAttribute(field,value)}}var truthy=function(val){return val==="true"||val===!0},isBoolAttr=function(field){switch(field){case"allowfullscreen":case"allowpaymentrequest":case"async":case"autofocus":case"autoplay":case"checked":case"controls":case"default":case"defer":case"disabled":case"formnovalidate":case"hidden":case"ismap":case"itemscope":case"loop":case"multiple":case"muted":case"nomodule":case"novalidate":case"open":case"playsinline":case"readonly":case"required":case"reversed":case"selected":case"truespeed":case"webkitdirectory":return!0;default:return!1}};class BaseInterpreter{global;local;root;handler;resizeObserver;nodes;stack;templates;m;constructor(){}initialize(root,handler=null){this.global={},this.local={},this.root=root,this.nodes=[root],this.stack=[root],this.templates={},this.handler=handler}handleResizeEvent(entry){const target=entry.target;let event=new CustomEvent("resize",{bubbles:!1,detail:entry});target.dispatchEvent(event)}createObserver(element){if(!this.resizeObserver)this.resizeObserver=new ResizeObserver((entries)=>{for(let entry of entries)this.handleResizeEvent(entry)});this.resizeObserver.observe(element)}removeObserver(element){if(this.resizeObserver)this.resizeObserver.unobserve(element)}createListener(event_name,element,bubbles){if(bubbles)if(this.global[event_name]===void 0)this.global[event_name]={active:1,callback:this.handler},this.root.addEventListener(event_name,this.handler);else this.global[event_name].active++;else{const id=element.getAttribute("data-dioxus-id");if(!this.local[id])this.local[id]={};element.addEventListener(event_name,this.handler)}if(event_name=="resize")this.createObserver(element)}removeListener(element,event_name,bubbles){if(bubbles)this.removeBubblingListener(event_name);else this.removeNonBubblingListener(element,event_name)}removeBubblingListener(event_name){if(this.global[event_name].active--,this.global[event_name].active===0)this.root.removeEventListener(event_name,this.global[event_name].callback),delete this.global[event_name]}removeNonBubblingListener(element,event_name){const id=element.getAttribute("data-dioxus-id");if(delete this.local[id][event_name],Object.keys(this.local[id]).length===0)delete this.local[id];element.removeEventListener(event_name,this.handler)}removeAllNonBubblingListeners(element){const id=element.getAttribute("data-dioxus-id");delete this.local[id]}getNode(id){return this.nodes[id]}pushRoot(node){this.stack.push(node)}appendChildren(id,many){const root=this.nodes[id],els=this.stack.splice(this.stack.length-many);for(let k=0;k0;end--)node=node.nextSibling}return node}saveTemplate(nodes,tmpl_id){this.templates[tmpl_id]=nodes}hydrate_node(hydrateNode,ids){const split=hydrateNode.getAttribute("data-node-hydration").split(","),id=ids[parseInt(split[0])];if(this.nodes[id]=hydrateNode,split.length>1){hydrateNode.listening=split.length-1,hydrateNode.setAttribute("data-dioxus-id",id.toString());for(let j=1;j1){if(this.nodes[ids[parseInt(placeholderSplit[1])]]=currentNode,!treeWalker.nextNode())break;continue}const textNodeSplit=id.split("node-id");if(textNodeSplit.length>1){let next=currentNode.nextSibling;currentNode.remove();let commentAfterText,textNode;if(next.nodeType===Node.COMMENT_NODE){const newText=next.parentElement.insertBefore(document.createTextNode(""),next);commentAfterText=next,textNode=newText}else textNode=next,commentAfterText=textNode.nextSibling;treeWalker.currentNode=commentAfterText,this.nodes[ids[parseInt(textNodeSplit[1])]]=textNode;let exit=!treeWalker.nextNode();if(commentAfterText.remove(),exit)break;continue}}if(!treeWalker.nextNode())break}}}setAttributeInner(node,field,value,ns){setAttributeInner(node,field,value,ns)}}export{BaseInterpreter}; +function setAttributeInner(node,field,value,ns){if(ns==="style"){node.style.setProperty(field,value);return}if(ns){node.setAttributeNS(ns,field,value);return}switch(field){case"value":if(node.value!==value)node.value=value;break;case"initial_value":node.defaultValue=value;break;case"checked":node.checked=truthy(value);break;case"initial_checked":node.defaultChecked=truthy(value);break;case"selected":node.selected=truthy(value);break;case"initial_selected":node.defaultSelected=truthy(value);break;case"dangerous_inner_html":node.innerHTML=value;break;default:if(!truthy(value)&&isBoolAttr(field))node.removeAttribute(field);else node.setAttribute(field,value)}}function truthy(val){return val==="true"||val===!0}function isBoolAttr(field){switch(field){case"allowfullscreen":case"allowpaymentrequest":case"async":case"autofocus":case"autoplay":case"checked":case"controls":case"default":case"defer":case"disabled":case"formnovalidate":case"hidden":case"ismap":case"itemscope":case"loop":case"multiple":case"muted":case"nomodule":case"novalidate":case"open":case"playsinline":case"readonly":case"required":case"reversed":case"selected":case"truespeed":case"webkitdirectory":return!0;default:return!1}}class BaseInterpreter{global;local;root;handler;resizeObserver;nodes;stack;templates;m;constructor(){}initialize(root,handler=null){this.global={},this.local={},this.root=root,this.nodes=[root],this.stack=[root],this.templates={},this.handler=handler}handleResizeEvent(entry){const target=entry.target;let event=new CustomEvent("resize",{bubbles:!1,detail:entry});target.dispatchEvent(event)}createObserver(element){if(!this.resizeObserver)this.resizeObserver=new ResizeObserver((entries)=>{for(let entry of entries)this.handleResizeEvent(entry)});this.resizeObserver.observe(element)}removeObserver(element){if(this.resizeObserver)this.resizeObserver.unobserve(element)}createListener(event_name,element,bubbles){if(bubbles)if(this.global[event_name]===void 0)this.global[event_name]={active:1,callback:this.handler},this.root.addEventListener(event_name,this.handler);else this.global[event_name].active++;else{const id=element.getAttribute("data-dioxus-id");if(!this.local[id])this.local[id]={};element.addEventListener(event_name,this.handler)}if(event_name=="resize")this.createObserver(element)}removeListener(element,event_name,bubbles){if(bubbles)this.removeBubblingListener(event_name);else this.removeNonBubblingListener(element,event_name)}removeBubblingListener(event_name){if(this.global[event_name].active--,this.global[event_name].active===0)this.root.removeEventListener(event_name,this.global[event_name].callback),delete this.global[event_name]}removeNonBubblingListener(element,event_name){const id=element.getAttribute("data-dioxus-id");if(delete this.local[id][event_name],Object.keys(this.local[id]).length===0)delete this.local[id];element.removeEventListener(event_name,this.handler)}removeAllNonBubblingListeners(element){const id=element.getAttribute("data-dioxus-id");delete this.local[id]}getNode(id){return this.nodes[id]}pushRoot(node){this.stack.push(node)}appendChildren(id,many){const root=this.nodes[id],els=this.stack.splice(this.stack.length-many);for(let k=0;k0;end--)node=node.nextSibling}return node}saveTemplate(nodes,tmpl_id){this.templates[tmpl_id]=nodes}hydrate_node(hydrateNode,ids){const split=hydrateNode.getAttribute("data-node-hydration").split(","),id=ids[parseInt(split[0])];if(this.nodes[id]=hydrateNode,split.length>1){hydrateNode.listening=split.length-1,hydrateNode.setAttribute("data-dioxus-id",id.toString());for(let j=1;j1){if(this.nodes[ids[parseInt(placeholderSplit[1])]]=currentNode,!treeWalker.nextNode())break;continue}const textNodeSplit=id.split("node-id");if(textNodeSplit.length>1){let next=currentNode.nextSibling;currentNode.remove();let commentAfterText,textNode;if(next.nodeType===Node.COMMENT_NODE){const newText=next.parentElement.insertBefore(document.createTextNode(""),next);commentAfterText=next,textNode=newText}else textNode=next,commentAfterText=textNode.nextSibling;treeWalker.currentNode=commentAfterText,this.nodes[ids[parseInt(textNodeSplit[1])]]=textNode;let exit=!treeWalker.nextNode();if(commentAfterText.remove(),exit)break;continue}}if(!treeWalker.nextNode())break}}}setAttributeInner(node,field,value,ns){setAttributeInner(node,field,value,ns)}}export{BaseInterpreter}; diff --git a/packages/interpreter/src/js/hash.txt b/packages/interpreter/src/js/hash.txt index fe5b307750..2220fd98e4 100644 --- a/packages/interpreter/src/js/hash.txt +++ b/packages/interpreter/src/js/hash.txt @@ -1 +1 @@ -[6449103750905854967, 4461869229701639737, 13069001215487072322, 8716623267269178440, 5336385715226370016, 14456089431355876478, 3201860079408422767, 5052021921702764563, 17534315583914394253, 5638004933879392817] \ No newline at end of file +[6449103750905854967, 4461869229701639737, 13069001215487072322, 8716623267269178440, 5336385715226370016, 14456089431355876478, 11703635120274352436, 5052021921702764563, 17534315583914394253, 5638004933879392817] \ No newline at end of file diff --git a/packages/interpreter/src/js/native.js b/packages/interpreter/src/js/native.js index ff50e65fa8..33767fde80 100644 --- a/packages/interpreter/src/js/native.js +++ b/packages/interpreter/src/js/native.js @@ -1 +1 @@ -function retrieveValues(event,target){let contents={values:{}},form=target.closest("form");if(form){if(event.type==="input"||event.type==="change"||event.type==="submit"||event.type==="reset"||event.type==="click")contents=retrieveFormValues(form)}return contents}function retrieveFormValues(form){const formData=new FormData(form),contents={};return formData.forEach((value,key)=>{if(contents[key])contents[key].push(value);else contents[key]=[value]}),{valid:form.checkValidity(),values:contents}}function retrieveSelectValue(target){let options=target.selectedOptions,values=[];for(let i=0;icontents={...contents,...obj};if(event instanceof WheelEvent)extend(serializeWheelEvent(event));if(event instanceof MouseEvent)extend(serializeMouseEvent(event));if(event instanceof KeyboardEvent)extend(serializeKeyboardEvent(event));if(event instanceof InputEvent)extend(serializeInputEvent(event,target));if(event instanceof PointerEvent)extend(serializePointerEvent(event));if(event instanceof AnimationEvent)extend(serializeAnimationEvent(event));if(event instanceof TransitionEvent)extend({property_name:event.propertyName,elapsed_time:event.elapsedTime,pseudo_element:event.pseudoElement});if(event instanceof CompositionEvent)extend({data:event.data});if(event instanceof DragEvent)extend(serializeDragEvent(event));if(event instanceof FocusEvent)extend({});if(event instanceof ClipboardEvent)extend({});if(event instanceof CustomEvent){const detail=event.detail;if(detail instanceof ResizeObserverEntry)extend(serializeResizeEventDetail(detail))}if(typeof TouchEvent!=="undefined"&&event instanceof TouchEvent)extend(serializeTouchEvent(event));if(event.type==="submit"||event.type==="reset"||event.type==="click"||event.type==="change"||event.type==="input")extend(serializeInputEvent(event,target));if(event instanceof DragEvent);return contents}var toSerializableResizeObserverSize=function(size,is_inline_width){return[is_inline_width?size.inlineSize:size.blockSize,is_inline_width?size.blockSize:size.inlineSize]};function serializeResizeEventDetail(detail){let is_inline_width=!0;if(detail.target instanceof HTMLElement){if(window.getComputedStyle(detail.target).getPropertyValue("writing-mode")!=="horizontal-tb")is_inline_width=!1}return{border_box_size:detail.borderBoxSize!==void 0?toSerializableResizeObserverSize(detail.borderBoxSize[0],is_inline_width):detail.contentRect,content_box_size:detail.contentBoxSize!==void 0?toSerializableResizeObserverSize(detail.contentBoxSize[0],is_inline_width):detail.contentRect,content_rect:detail.contentRect}}var serializeInputEvent=function(event,target){let contents={};if(target instanceof HTMLElement){let values=retrieveValues(event,target);contents.values=values.values,contents.valid=values.valid}if(event.target instanceof HTMLInputElement){let target2=event.target,value=target2.value??target2.textContent??"";if(target2.type==="checkbox")value=target2.checked?"true":"false";else if(target2.type==="radio")value=target2.value;contents.value=value}if(event.target instanceof HTMLTextAreaElement)contents.value=event.target.value;if(event.target instanceof HTMLSelectElement)contents.value=retrieveSelectValue(event.target).join(",");if(contents.value===void 0)contents.value="";return contents},serializeWheelEvent=function(event){return{delta_x:event.deltaX,delta_y:event.deltaY,delta_z:event.deltaZ,delta_mode:event.deltaMode}},serializeTouchEvent=function(event){return{alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,shift_key:event.shiftKey,changed_touches:event.changedTouches,target_touches:event.targetTouches,touches:event.touches}},serializePointerEvent=function(event){return{alt_key:event.altKey,button:event.button,buttons:event.buttons,client_x:event.clientX,client_y:event.clientY,ctrl_key:event.ctrlKey,meta_key:event.metaKey,page_x:event.pageX,page_y:event.pageY,screen_x:event.screenX,screen_y:event.screenY,shift_key:event.shiftKey,pointer_id:event.pointerId,width:event.width,height:event.height,pressure:event.pressure,tangential_pressure:event.tangentialPressure,tilt_x:event.tiltX,tilt_y:event.tiltY,twist:event.twist,pointer_type:event.pointerType,is_primary:event.isPrimary}},serializeMouseEvent=function(event){return{alt_key:event.altKey,button:event.button,buttons:event.buttons,client_x:event.clientX,client_y:event.clientY,ctrl_key:event.ctrlKey,meta_key:event.metaKey,offset_x:event.offsetX,offset_y:event.offsetY,page_x:event.pageX,page_y:event.pageY,screen_x:event.screenX,screen_y:event.screenY,shift_key:event.shiftKey}},serializeKeyboardEvent=function(event){return{char_code:event.charCode,is_composing:event.isComposing,key:event.key,alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,key_code:event.keyCode,shift_key:event.shiftKey,location:event.location,repeat:event.repeat,which:event.which,code:event.code}},serializeAnimationEvent=function(event){return{animation_name:event.animationName,elapsed_time:event.elapsedTime,pseudo_element:event.pseudoElement}},serializeDragEvent=function(event){let files=void 0;if(event.dataTransfer&&event.dataTransfer.files&&event.dataTransfer.files.length>0)files={files:{placeholder:[]}};return{mouse:{alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,shift_key:event.shiftKey,...serializeMouseEvent(event)},files}};var handleVirtualdomEventSync=function(endpoint,contents){const xhr=new XMLHttpRequest;return xhr.open("POST",endpoint,!1),xhr.setRequestHeader("Content-Type","application/json"),xhr.send(contents),JSON.parse(xhr.responseText)},getTargetId=function(target){if(!(target instanceof Node))return null;let ourTarget=target,realId=null;while(realId==null){if(ourTarget===null)return null;if(ourTarget instanceof Element)realId=ourTarget.getAttribute("data-dioxus-id");ourTarget=ourTarget.parentNode}return parseInt(realId)},JSChannel_;if(RawInterpreter!==void 0&&RawInterpreter!==null)JSChannel_=RawInterpreter;class NativeInterpreter extends JSChannel_{intercept_link_redirects;ipc;editsPath;eventsPath;kickStylesheets;queuedBytes=[];liveview;constructor(editsPath,eventsPath){super();this.editsPath=editsPath,this.eventsPath=eventsPath,this.kickStylesheets=!1}initialize(root){this.intercept_link_redirects=!0,this.liveview=!1,window.addEventListener("dragover",function(e){if(e.target instanceof Element&&e.target.tagName!="INPUT")e.preventDefault()},!1),window.addEventListener("drop",function(e){if(!(e.target instanceof Element))return;e.preventDefault()},!1),window.addEventListener("click",(event)=>{const target=event.target;if(target instanceof HTMLInputElement&&target.getAttribute("type")==="file"){let target_id=getTargetId(target);if(target_id!==null){const message=this.serializeIpcMessage("file_dialog",{event:"change&input",accept:target.getAttribute("accept"),directory:target.getAttribute("webkitdirectory")==="true",multiple:target.hasAttribute("multiple"),target:target_id,bubbles:event.bubbles});this.ipc.postMessage(message),event.preventDefault()}}}),this.ipc=window.ipc;const handler=(event)=>this.handleEvent(event,event.type,!0);super.initialize(root,handler)}serializeIpcMessage(method,params={}){return JSON.stringify({method,params})}scrollTo(id,behavior){const node=this.nodes[id];if(node instanceof HTMLElement)node.scrollIntoView({behavior})}getScrollHeight(id){const node=this.nodes[id];if(node instanceof HTMLElement)return node.scrollHeight}getScrollLeft(id){const node=this.nodes[id];if(node instanceof HTMLElement)return node.scrollLeft}getScrollTop(id){const node=this.nodes[id];if(node instanceof HTMLElement)return node.scrollTop}getScrollWidth(id){const node=this.nodes[id];if(node instanceof HTMLElement)return node.scrollWidth}getClientRect(id){const node=this.nodes[id];if(node instanceof HTMLElement){const rect=node.getBoundingClientRect();return{type:"GetClientRect",origin:[rect.x,rect.y],size:[rect.width,rect.height]}}}setFocus(id,focus){const node=this.nodes[id];if(node instanceof HTMLElement)if(focus)node.focus();else node.blur()}loadChild(array){let node=this.stack[this.stack.length-1];for(let i=0;i0;end--)node=node.nextSibling}return node}appendChildren(id,many){const root=this.nodes[id],els=this.stack.splice(this.stack.length-many);for(let k=0;k{this.flushQueuedBytes(),this.waitForRequest(headless)})}waitForRequest(headless){fetch(new Request(this.editsPath)).then((response)=>response.arrayBuffer()).then((bytes)=>{this.rafEdits(headless,bytes)})}kickAllStylesheetsOnPage(){let stylesheets=document.querySelectorAll("link[rel=stylesheet]");for(let i=0;i{let entropy_string=Math.random().toString().substring(0,10),[href]=sheet.href.split("?entropy=");sheet.href=href+"?entropy="+entropy_string})}this.kickCachedLinks()}kickCachedLinks(){Object.values(this.templates).forEach((template)=>{template.forEach((node)=>{let walker=document.createTreeWalker(node,NodeFilter.SHOW_ELEMENT);while(walker.nextNode()){let element=walker.currentNode;if(element.tagName==="LINK"){let attr=element.getAttribute("href");if(attr){let entropy_string=Math.random().toString().substring(0,10),[href]=attr.split("?entropy=");element.setAttribute("href",href+"?entropy="+entropy_string)}}}})})}async readFiles(target,contents,bubbles,realId,name){let files=target.files,file_contents={};for(let i=0;i{if(contents[key])contents[key].push(value);else contents[key]=[value]}),{valid:form.checkValidity(),values:contents}}function retrieveSelectValue(target){let options=target.selectedOptions,values=[];for(let i=0;icontents={...contents,...obj};if(event instanceof WheelEvent)extend(serializeWheelEvent(event));if(event instanceof MouseEvent)extend(serializeMouseEvent(event));if(event instanceof KeyboardEvent)extend(serializeKeyboardEvent(event));if(event instanceof InputEvent)extend(serializeInputEvent(event,target));if(event instanceof PointerEvent)extend(serializePointerEvent(event));if(event instanceof AnimationEvent)extend(serializeAnimationEvent(event));if(event instanceof TransitionEvent)extend({property_name:event.propertyName,elapsed_time:event.elapsedTime,pseudo_element:event.pseudoElement});if(event instanceof CompositionEvent)extend({data:event.data});if(event instanceof DragEvent)extend(serializeDragEvent(event));if(event instanceof FocusEvent)extend({});if(event instanceof ClipboardEvent)extend({});if(event instanceof CustomEvent){const detail=event.detail;if(detail instanceof ResizeObserverEntry)extend(serializeResizeEventDetail(detail))}if(typeof TouchEvent!=="undefined"&&event instanceof TouchEvent)extend(serializeTouchEvent(event));if(event.type==="submit"||event.type==="reset"||event.type==="click"||event.type==="change"||event.type==="input")extend(serializeInputEvent(event,target));if(event instanceof DragEvent);return contents}function toSerializableResizeObserverSize(size,is_inline_width){return[is_inline_width?size.inlineSize:size.blockSize,is_inline_width?size.blockSize:size.inlineSize]}function serializeResizeEventDetail(detail){let is_inline_width=!0;if(detail.target instanceof HTMLElement){if(window.getComputedStyle(detail.target).getPropertyValue("writing-mode")!=="horizontal-tb")is_inline_width=!1}return{border_box_size:detail.borderBoxSize!==void 0?toSerializableResizeObserverSize(detail.borderBoxSize[0],is_inline_width):detail.contentRect,content_box_size:detail.contentBoxSize!==void 0?toSerializableResizeObserverSize(detail.contentBoxSize[0],is_inline_width):detail.contentRect,content_rect:detail.contentRect}}function serializeInputEvent(event,target){let contents={};if(target instanceof HTMLElement){let values=retrieveValues(event,target);contents.values=values.values,contents.valid=values.valid}if(event.target instanceof HTMLInputElement){let target2=event.target,value=target2.value??target2.textContent??"";if(target2.type==="checkbox")value=target2.checked?"true":"false";else if(target2.type==="radio")value=target2.value;contents.value=value}if(event.target instanceof HTMLTextAreaElement)contents.value=event.target.value;if(event.target instanceof HTMLSelectElement)contents.value=retrieveSelectValue(event.target).join(",");if(contents.value===void 0)contents.value="";return contents}function serializeWheelEvent(event){return{delta_x:event.deltaX,delta_y:event.deltaY,delta_z:event.deltaZ,delta_mode:event.deltaMode}}function serializeTouchEvent(event){return{alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,shift_key:event.shiftKey,changed_touches:event.changedTouches,target_touches:event.targetTouches,touches:event.touches}}function serializePointerEvent(event){return{alt_key:event.altKey,button:event.button,buttons:event.buttons,client_x:event.clientX,client_y:event.clientY,ctrl_key:event.ctrlKey,meta_key:event.metaKey,page_x:event.pageX,page_y:event.pageY,screen_x:event.screenX,screen_y:event.screenY,shift_key:event.shiftKey,pointer_id:event.pointerId,width:event.width,height:event.height,pressure:event.pressure,tangential_pressure:event.tangentialPressure,tilt_x:event.tiltX,tilt_y:event.tiltY,twist:event.twist,pointer_type:event.pointerType,is_primary:event.isPrimary}}function serializeMouseEvent(event){return{alt_key:event.altKey,button:event.button,buttons:event.buttons,client_x:event.clientX,client_y:event.clientY,ctrl_key:event.ctrlKey,meta_key:event.metaKey,offset_x:event.offsetX,offset_y:event.offsetY,page_x:event.pageX,page_y:event.pageY,screen_x:event.screenX,screen_y:event.screenY,shift_key:event.shiftKey}}function serializeKeyboardEvent(event){return{char_code:event.charCode,is_composing:event.isComposing,key:event.key,alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,key_code:event.keyCode,shift_key:event.shiftKey,location:event.location,repeat:event.repeat,which:event.which,code:event.code}}function serializeAnimationEvent(event){return{animation_name:event.animationName,elapsed_time:event.elapsedTime,pseudo_element:event.pseudoElement}}function serializeDragEvent(event){let files=void 0;if(event.dataTransfer&&event.dataTransfer.files&&event.dataTransfer.files.length>0)files={files:{placeholder:[]}};return{mouse:{alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,shift_key:event.shiftKey,...serializeMouseEvent(event)},files}}function handleVirtualdomEventSync(endpoint,contents){const xhr=new XMLHttpRequest;return xhr.open("POST",endpoint,!1),xhr.setRequestHeader("Content-Type","application/json"),xhr.send(contents),JSON.parse(xhr.responseText)}function getTargetId(target){if(!(target instanceof Node))return null;let ourTarget=target,realId=null;while(realId==null){if(ourTarget===null)return null;if(ourTarget instanceof Element)realId=ourTarget.getAttribute("data-dioxus-id");ourTarget=ourTarget.parentNode}return parseInt(realId)}var JSChannel_;if(RawInterpreter!==void 0&&RawInterpreter!==null)JSChannel_=RawInterpreter;class NativeInterpreter extends JSChannel_{intercept_link_redirects;ipc;editsPath;eventsPath;kickStylesheets;queuedBytes=[];liveview;constructor(editsPath,eventsPath){super();this.editsPath=editsPath,this.eventsPath=eventsPath,this.kickStylesheets=!1}initialize(root){this.intercept_link_redirects=!0,this.liveview=!1,window.addEventListener("dragover",function(e){if(e.target instanceof Element&&e.target.tagName!="INPUT")e.preventDefault()},!1),window.addEventListener("drop",function(e){if(!(e.target instanceof Element))return;e.preventDefault()},!1),window.addEventListener("click",(event)=>{const target=event.target;if(target instanceof HTMLInputElement&&target.getAttribute("type")==="file"){let target_id=getTargetId(target);if(target_id!==null){const message=this.serializeIpcMessage("file_dialog",{event:"change&input",accept:target.getAttribute("accept"),directory:target.getAttribute("webkitdirectory")==="true",multiple:target.hasAttribute("multiple"),target:target_id,bubbles:event.bubbles});this.ipc.postMessage(message),event.preventDefault()}}}),this.ipc=window.ipc;const handler=(event)=>this.handleEvent(event,event.type,!0);super.initialize(root,handler)}serializeIpcMessage(method,params={}){return JSON.stringify({method,params})}scrollTo(id,behavior){const node=this.nodes[id];if(node instanceof HTMLElement)node.scrollIntoView({behavior})}getScrollHeight(id){const node=this.nodes[id];if(node instanceof HTMLElement)return node.scrollHeight}getScrollLeft(id){const node=this.nodes[id];if(node instanceof HTMLElement)return node.scrollLeft}getScrollTop(id){const node=this.nodes[id];if(node instanceof HTMLElement)return node.scrollTop}getScrollWidth(id){const node=this.nodes[id];if(node instanceof HTMLElement)return node.scrollWidth}getClientRect(id){const node=this.nodes[id];if(node instanceof HTMLElement){const rect=node.getBoundingClientRect();return{type:"GetClientRect",origin:[rect.x,rect.y],size:[rect.width,rect.height]}}}setFocus(id,focus){const node=this.nodes[id];if(node instanceof HTMLElement)if(focus)node.focus();else node.blur()}loadChild(array){let node=this.stack[this.stack.length-1];for(let i=0;i0;end--)node=node.nextSibling}return node}appendChildren(id,many){const root=this.nodes[id],els=this.stack.splice(this.stack.length-many);for(let k=0;k{this.flushQueuedBytes(),this.waitForRequest(headless)})}waitForRequest(headless){fetch(new Request(this.editsPath)).then((response)=>response.arrayBuffer()).then((bytes)=>{this.rafEdits(headless,bytes)})}kickAllStylesheetsOnPage(){let stylesheets=document.querySelectorAll("link[rel=stylesheet]");for(let i=0;i{sheet.href=sheet.href+"?"+Math.random()})}}async readFiles(target,contents,bubbles,realId,name){let files=target.files,file_contents={};for(let i=0;i { - // Change the href invalidate parameter to a random number - let entropy = Math.random(); - - // Cap the entropy to_string length to 10 characters - let entropy_string = entropy.toString().substring(0, 10); - - // Split the href into the original and the query string - let [href] = sheet.href.split("?entropy="); - sheet.href = href + "?entropy=" + entropy_string; + sheet.href = sheet.href + "?" + Math.random(); }); } - - // Modify the templates' links to include a random entropy parameter - this.kickCachedLinks(); - } - - // modify all the links in our saved templates to include a random entropy parameter - // ensures that templates, when rendered again, have some entropy - kickCachedLinks() { - Object.values(this.templates).forEach((template) => { - template.forEach((node) => { - let walker = document.createTreeWalker(node, NodeFilter.SHOW_ELEMENT); - while (walker.nextNode()) { - let element = walker.currentNode as HTMLElement; - if (element.tagName === "LINK") { - // if it has a href attribute, change it - let attr = element.getAttribute("href"); - if (attr) { - // Change the href invalidate parameter to a random number - let entropy = Math.random(); - - // Cap the entropy to_string length to 10 characters - let entropy_string = entropy.toString().substring(0, 10); - - // Split the href into the original and the query string - let [href] = attr.split("?entropy="); - element.setAttribute("href", href + "?entropy=" + entropy_string); - } - } - } - }); - }); } // A liveview only function @@ -448,7 +407,7 @@ function handleVirtualdomEventSync( } function getTargetId(target: EventTarget): NodeId | null { - // Ensure that the target is a node, sometimes it's not + // Ensure that the target is a node, sometimes it's nota if (!(target instanceof Node)) { return null; } @@ -484,7 +443,7 @@ function getTargetId(target: EventTarget): NodeId | null { // let target_id = find_real_id(target); // if (target_id !== null) { // const send = (event_name) => { -// const message = window.interpreter.serializeIpcMessage("file_dialog", { accept: target.getAttribute("accept"), directory: target.getAttribute("webkitdirectory") === "true", multiple: target.hasAttribute("multiple"), target: parseInt(target_id), bubbles: event_bubbles(event_name), event: event_name }); +// const message = window.interpreter.serializeIpcMessage("file_diolog", { accept: target.getAttribute("accept"), directory: target.getAttribute("webkitdirectory") === "true", multiple: target.hasAttribute("multiple"), target: parseInt(target_id), bubbles: event_bubbles(event_name), event: event_name }); // window.ipc.postMessage(message); // }; // send("change&input"); diff --git a/packages/liveview/Cargo.toml b/packages/liveview/Cargo.toml index dd2df7daf4..8659443957 100644 --- a/packages/liveview/Cargo.toml +++ b/packages/liveview/Cargo.toml @@ -22,13 +22,9 @@ tokio-stream = { version = "0.1.11", features = ["net"] } tokio-util = { version = "0.7.4", features = ["rt"] } serde = { version = "1.0.151", features = ["derive"] } serde_json = "1.0.91" -dioxus-html = { workspace = true, features = ["serialize", "mounted"] } +dioxus-html = { workspace = true, features = ["serialize", "document", "mounted"] } rustc-hash = { workspace = true } - dioxus-core = { workspace = true, features = ["serialize"] } -dioxus-core-types = { workspace = true } -dioxus-document = { workspace = true } -dioxus-devtools = { workspace = true, optional = true } dioxus-interpreter-js = { workspace = true, features = ["binary-protocol"] } dioxus-devtools = { workspace = true, optional = true } dioxus-cli-config = { workspace = true } @@ -38,15 +34,12 @@ generational-box = { workspace = true } axum = { workspace = true, optional = true, features = ["ws"] } [dev-dependencies] -env_logger = { workspace = true } +pretty_env_logger = { version = "0.5.0" } tokio = { workspace = true, features = ["full"] } axum = { workspace = true, features = ["ws"] } tower = { workspace = true } dioxus = { workspace = true } -[build-dependencies] -lazy-js-bundle = { workspace = true } - [features] default = ["devtools", "multi-thread"] axum = ["dep:axum"] diff --git a/packages/liveview/build.rs b/packages/liveview/build.rs deleted file mode 100644 index d218f25627..0000000000 --- a/packages/liveview/build.rs +++ /dev/null @@ -1,7 +0,0 @@ -fn main() { - // If any TS files change, re-run the build script - lazy_js_bundle::LazyTypeScriptBindings::new() - .with_watching("./src/ts/") - .with_binding("./src/ts/main.ts", "./src/js/main.js") - .run(); -} diff --git a/packages/liveview/examples/axum.rs b/packages/liveview/examples/axum.rs index 6790af90d1..d015259e9f 100644 --- a/packages/liveview/examples/axum.rs +++ b/packages/liveview/examples/axum.rs @@ -15,7 +15,7 @@ fn app() -> Element { #[tokio::main] async fn main() { - env_logger::init(); + pretty_env_logger::init(); let addr: std::net::SocketAddr = ([127, 0, 0, 1], 3030).into(); diff --git a/packages/liveview/examples/axum_stress.rs b/packages/liveview/examples/axum_stress.rs index 6714ad30d3..80e146eed8 100644 --- a/packages/liveview/examples/axum_stress.rs +++ b/packages/liveview/examples/axum_stress.rs @@ -21,7 +21,7 @@ fn app() -> Element { #[tokio::main] async fn main() { - env_logger::init(); + pretty_env_logger::init(); let addr: std::net::SocketAddr = ([127, 0, 0, 1], 3030).into(); diff --git a/packages/liveview/src/adapters/axum_adapter.rs b/packages/liveview/src/adapters/axum_adapter.rs index c085dbaf6d..638e6cd9cf 100644 --- a/packages/liveview/src/adapters/axum_adapter.rs +++ b/packages/liveview/src/adapters/axum_adapter.rs @@ -46,13 +46,14 @@ impl LiveviewRouter for Router { let view = crate::LiveViewPool::new(); let ws_path = format!("{}/ws", route.trim_start_matches('/')); + let title = crate::app_title(); let index_page_with_glue = move |glue: &str| { Html(format!( r#" - + {title}
      {glue} diff --git a/packages/liveview/src/config.rs b/packages/liveview/src/config.rs index 92a9286c4b..0f22eba8be 100644 --- a/packages/liveview/src/config.rs +++ b/packages/liveview/src/config.rs @@ -53,6 +53,7 @@ impl Config { /// Launch the LiveView server. pub async fn launch(self) { + println!("{} started on http://{}", app_title(), self.address); self.router.start(self.address).await } } diff --git a/packages/liveview/src/document.rs b/packages/liveview/src/document.rs deleted file mode 100644 index 6a0d66f167..0000000000 --- a/packages/liveview/src/document.rs +++ /dev/null @@ -1,112 +0,0 @@ -use crate::query::{Query, QueryEngine}; -use dioxus_core::ScopeId; -use dioxus_document::{Document, EvalError}; -use std::{ - collections::BTreeMap, - rc::Rc, - sync::{Arc, RwLock}, -}; - -/// Provides the LiveviewDocument through [`ScopeId::provide_context`]. -pub fn init_eval() { - let query = ScopeId::ROOT.consume_context::().unwrap(); - let provider: Rc = Rc::new(LiveviewDocument { - query, - action_tx: todo!(), - updater_callback: todo!(), - current_index: todo!(), - routes: todo!(), - }); - ScopeId::ROOT.provide_context(provider); -} - -// The document impl for LiveView -pub struct LiveviewDocument { - query: QueryEngine, - action_tx: tokio::sync::mpsc::UnboundedSender, - updater_callback: Arc>>, - current_index: usize, - routes: BTreeMap, -} - -impl Document for LiveviewDocument { - fn eval(&self, js: String) -> dioxus_document::Eval { - todo!() - } - - fn set_title(&self, title: String) { - _ = self.eval(format!("window.document.title = '{}';", title)); - } - - fn create_head_element( - &self, - name: &str, - attributes: Vec<(&str, String)>, - contents: Option, - ) { - todo!() - } - - fn go_back(&self) { - let _ = self.action_tx.send(Action::GoBack); - } - - fn go_forward(&self) { - let _ = self.action_tx.send(Action::GoForward); - } - - fn push_route(&self, route: String) { - let _ = self.action_tx.send(Action::Push(route)); - } - - fn replace_route(&self, route: String) { - let _ = self.action_tx.send(Action::Replace(route)); - } - - fn navigate_external(&self, url: String) -> bool { - let _ = self.action_tx.send(Action::External(url)); - true - } - - fn current_route(&self) -> String { - self.routes[&self.current_index].clone() - } - - fn can_go_back(&self) -> bool { - // Check if the one before is contiguous (i.e., not an external page) - let visited_indices: Vec = self.routes.keys().cloned().collect(); - visited_indices - .iter() - .position(|&rhs| self.current_index == rhs) - .map_or(false, |index| { - index > 0 && visited_indices[index - 1] == self.current_index - 1 - }) - } - - fn can_go_forward(&self) -> bool { - // let timeline = self.timeline.lock().expect("unpoisoned mutex"); - // Check if the one after is contiguous (i.e., not an external page) - let visited_indices: Vec = self.routes.keys().cloned().collect(); - visited_indices - .iter() - .rposition(|&rhs| self.current_index == rhs) - .map_or(false, |index| { - index < visited_indices.len() - 1 - && visited_indices[index + 1] == self.current_index + 1 - }) - } - - fn updater(&self, callback: Arc) { - todo!() - // let mut updater_callback = self.updater_callback.write().unwrap(); - // *updater_callback = callback; - } -} - -enum Action { - GoBack, - GoForward, - Push(String), - Replace(String), - External(String), -} diff --git a/packages/liveview/src/element.rs b/packages/liveview/src/element.rs index 3befe6dc25..a81bb1a078 100644 --- a/packages/liveview/src/element.rs +++ b/packages/liveview/src/element.rs @@ -1,7 +1,7 @@ use dioxus_core::ElementId; use dioxus_html::{ geometry::{PixelsRect, PixelsSize, PixelsVector2D}, - MountedResult, MountedElement, + MountedResult, RenderedElementBacking, }; use crate::query::QueryEngine; @@ -47,76 +47,76 @@ macro_rules! scripted_getter { }; } -impl MountedElement for LiveviewElement { +impl RenderedElementBacking for LiveviewElement { fn as_any(&self) -> &dyn std::any::Any { self } - // scripted_getter!( - // get_scroll_offset, - // "return [window.interpreter.getScrollLeft({id}), window.interpreter.getScrollTop({id})]", - // PixelsVector2D - // ); - - // scripted_getter!( - // get_scroll_size, - // "return [window.interpreter.getScrollWidth({id}), window.interpreter.getScrollHeight({id})]", - // PixelsSize - // ); - - // scripted_getter!( - // get_client_rect, - // "return window.interpreter.getClientRect({id});", - // PixelsRect - // ); - - // fn scroll_to( - // &self, - // behavior: dioxus_html::ScrollBehavior, - // ) -> std::pin::Pin>>> { - // let script = format!( - // "return window.interpreter.scrollTo({}, {});", - // self.id.0, - // serde_json::to_string(&behavior).expect("Failed to serialize ScrollBehavior") - // ); - - // let fut = self.query.new_query::(&script).resolve(); - // Box::pin(async move { - // match fut.await { - // Ok(true) => Ok(()), - // Ok(false) => MountedResult::Err(dioxus_html::MountedError::OperationFailed( - // Box::new(DesktopQueryError::FailedToQuery), - // )), - // Err(err) => { - // MountedResult::Err(dioxus_html::MountedError::OperationFailed(Box::new(err))) - // } - // } - // }) - // } - - // fn set_focus( - // &self, - // focus: bool, - // ) -> std::pin::Pin>>> { - // let script = format!( - // "return window.interpreter.setFocus({}, {});", - // self.id.0, focus - // ); - - // let fut = self.query.new_query::(&script).resolve(); - - // Box::pin(async move { - // match fut.await { - // Ok(true) => Ok(()), - // Ok(false) => MountedResult::Err(dioxus_html::MountedError::OperationFailed( - // Box::new(DesktopQueryError::FailedToQuery), - // )), - // Err(err) => { - // MountedResult::Err(dioxus_html::MountedError::OperationFailed(Box::new(err))) - // } - // } - // }) - // } + scripted_getter!( + get_scroll_offset, + "return [window.interpreter.getScrollLeft({id}), window.interpreter.getScrollTop({id})]", + PixelsVector2D + ); + + scripted_getter!( + get_scroll_size, + "return [window.interpreter.getScrollWidth({id}), window.interpreter.getScrollHeight({id})]", + PixelsSize + ); + + scripted_getter!( + get_client_rect, + "return window.interpreter.getClientRect({id});", + PixelsRect + ); + + fn scroll_to( + &self, + behavior: dioxus_html::ScrollBehavior, + ) -> std::pin::Pin>>> { + let script = format!( + "return window.interpreter.scrollTo({}, {});", + self.id.0, + serde_json::to_string(&behavior).expect("Failed to serialize ScrollBehavior") + ); + + let fut = self.query.new_query::(&script).resolve(); + Box::pin(async move { + match fut.await { + Ok(true) => Ok(()), + Ok(false) => MountedResult::Err(dioxus_html::MountedError::OperationFailed( + Box::new(DesktopQueryError::FailedToQuery), + )), + Err(err) => { + MountedResult::Err(dioxus_html::MountedError::OperationFailed(Box::new(err))) + } + } + }) + } + + fn set_focus( + &self, + focus: bool, + ) -> std::pin::Pin>>> { + let script = format!( + "return window.interpreter.setFocus({}, {});", + self.id.0, focus + ); + + let fut = self.query.new_query::(&script).resolve(); + + Box::pin(async move { + match fut.await { + Ok(true) => Ok(()), + Ok(false) => MountedResult::Err(dioxus_html::MountedError::OperationFailed( + Box::new(DesktopQueryError::FailedToQuery), + )), + Err(err) => { + MountedResult::Err(dioxus_html::MountedError::OperationFailed(Box::new(err))) + } + } + }) + } } #[derive(Debug)] diff --git a/packages/liveview/src/eval.rs b/packages/liveview/src/eval.rs new file mode 100644 index 0000000000..6821d6502f --- /dev/null +++ b/packages/liveview/src/eval.rs @@ -0,0 +1,81 @@ +use dioxus_core::ScopeId; +use dioxus_html::document::{Document, EvalError, Evaluator}; +use generational_box::{AnyStorage, GenerationalBox, UnsyncStorage}; +use std::rc::Rc; + +use crate::query::{Query, QueryEngine}; + +/// Provides the LiveviewDocument through [`ScopeId::provide_context`]. +pub fn init_eval() { + let query = ScopeId::ROOT.consume_context::().unwrap(); + let provider: Rc = Rc::new(LiveviewDocument { query }); + ScopeId::ROOT.provide_context(provider); +} + +/// Reprints the liveview-target's provider of evaluators. +pub struct LiveviewDocument { + query: QueryEngine, +} + +impl Document for LiveviewDocument { + fn new_evaluator(&self, js: String) -> GenerationalBox> { + LiveviewEvaluator::create(self.query.clone(), js) + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +/// Represents a liveview-target's JavaScript evaluator. +pub(crate) struct LiveviewEvaluator { + query: Query, +} + +impl LiveviewEvaluator { + /// Creates a new evaluator for liveview-based targets. + pub fn create(query_engine: QueryEngine, js: String) -> GenerationalBox> { + let query = query_engine.new_query(&js); + // We create a generational box that is owned by the query slot so that when we drop the query slot, the generational box is also dropped. + let owner = UnsyncStorage::owner(); + let query_id = query.id; + let query = owner.insert(Box::new(LiveviewEvaluator { query }) as Box); + query_engine.active_requests.slab.borrow_mut()[query_id].owner = Some(owner); + + query + } +} + +impl Evaluator for LiveviewEvaluator { + /// # Panics + /// This will panic if the query is currently being awaited. + fn poll_join( + &mut self, + context: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + self.query + .poll_result(context) + .map_err(|e| EvalError::Communication(e.to_string())) + } + + /// Sends a message to the evaluated JavaScript. + fn send(&self, data: serde_json::Value) -> Result<(), EvalError> { + if let Err(e) = self.query.send(data) { + return Err(EvalError::Communication(e.to_string())); + } + Ok(()) + } + + /// Gets an UnboundedReceiver to receive messages from the evaluated JavaScript. + /// + /// # Panics + /// This will panic if the query is currently being awaited. + fn poll_recv( + &mut self, + context: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + self.query + .poll_recv(context) + .map_err(|e| EvalError::Communication(e.to_string())) + } +} diff --git a/packages/liveview/src/js/hash.txt b/packages/liveview/src/js/hash.txt deleted file mode 100644 index cf43478351..0000000000 --- a/packages/liveview/src/js/hash.txt +++ /dev/null @@ -1 +0,0 @@ -[9100446950368490988] \ No newline at end of file diff --git a/packages/liveview/src/js/main.js b/packages/liveview/src/js/main.js deleted file mode 100644 index 6bd9d2ed54..0000000000 --- a/packages/liveview/src/js/main.js +++ /dev/null @@ -1 +0,0 @@ -function main(addr){let root=window.document.getElementById("main");if(root!=null)window.ipc=new IPC(root,addr)}class IPC{ws;constructor(root,addr){window.interpreter.initialize(root),window.interpreter.liveview=!0,window.interpreter.ipc=this;const ws=new WebSocket(addr);ws.binaryType="arraybuffer";function ping(){ws.send("__ping__")}ws.onopen=()=>{setInterval(ping,30000),ws.send(window.interpreter.serializeIpcMessage("initialize"))},ws.onerror=(err)=>{},ws.onmessage=(message)=>{const binaryFrame=new Uint8Array(message.data)[0]==1,messageData=message.data.slice(1);if(binaryFrame)window.interpreter.run_from_bytes(messageData);else{let str=new TextDecoder("utf-8").decode(messageData);if(str!="__pong__"){const event=JSON.parse(str);switch(event.type){case"query":Function("Eval",`"use strict";${event.data};`)();break}}}},this.ws=ws}postMessage(msg){this.ws.send(msg)}}export{main}; diff --git a/packages/liveview/src/launch.rs b/packages/liveview/src/launch.rs index eb96e23229..766a7a25fe 100644 --- a/packages/liveview/src/launch.rs +++ b/packages/liveview/src/launch.rs @@ -11,7 +11,6 @@ pub fn launch( ) -> ! { #[cfg(feature = "multi-thread")] let mut builder = tokio::runtime::Builder::new_multi_thread(); - #[cfg(not(feature = "multi-thread"))] let mut builder = tokio::runtime::Builder::new_current_thread(); diff --git a/packages/liveview/src/lib.rs b/packages/liveview/src/lib.rs index 1f95324c82..ebe0c03578 100644 --- a/packages/liveview/src/lib.rs +++ b/packages/liveview/src/lib.rs @@ -3,20 +3,19 @@ #![doc(html_favicon_url = "https://avatars.githubusercontent.com/u/79236386")] mod adapters; -mod config; -mod document; -mod element; -mod events; -mod pool; -mod query; - +#[allow(unused_imports)] pub use adapters::*; -pub use config::*; -pub use pool::*; +mod element; +pub mod pool; +mod query; use dioxus_interpreter_js::NATIVE_JS; use futures_util::{SinkExt, StreamExt}; - +pub use pool::*; +mod config; +mod eval; +mod events; +pub use config::*; #[cfg(feature = "axum")] pub mod launch; @@ -91,7 +90,7 @@ fn handle_edits_code() -> String { .unwrap_or_else(|| interpreter.len()); interpreter.replace_range(import_start..import_end, ""); } - let main_js = include_str!("./js/main.js"); + let main_js = include_str!("./main.js"); let js = format!("{interpreter}\n{main_js}"); js } diff --git a/packages/liveview/src/ts/main.ts b/packages/liveview/src/main.js similarity index 78% rename from packages/liveview/src/ts/main.ts rename to packages/liveview/src/main.js index f67282ad6e..e8f5c58338 100644 --- a/packages/liveview/src/ts/main.ts +++ b/packages/liveview/src/main.js @@ -1,23 +1,19 @@ -export { }; +const intercept_link_redirects = false; -declare global { - interface Window { - ipc: IPC; - interpreter: NativeInterpreter; +function main() { + let root = window.document.getElementById("main"); + if (root != null) { + window.ipc = new IPC(root); } } -type NativeInterpreter = any; class IPC { - ws: WebSocket; - - constructor(root: Element, addr: string) { - // window.interpreter = new NativeInterpreter(); + constructor(root) { + window.interpreter = new NativeInterpreter(); window.interpreter.initialize(root); window.interpreter.liveview = true; window.interpreter.ipc = this; - - const ws = new WebSocket(addr); + const ws = new WebSocket(WS_ADDR); ws.binaryType = "arraybuffer"; function ping() { @@ -38,19 +34,18 @@ class IPC { const u8view = new Uint8Array(message.data); const binaryFrame = u8view[0] == 1; const messageData = message.data.slice(1); - // The first byte tells the shim if this is a binary of text frame if (binaryFrame) { // binary frame window.interpreter.run_from_bytes(messageData); } else { // text frame + let decoder = new TextDecoder("utf-8"); // Using decode method to get string output let str = decoder.decode(messageData); // Ignore pongs - if (str != "__pong__") { const event = JSON.parse(str); switch (event.type) { @@ -65,15 +60,9 @@ class IPC { this.ws = ws; } - postMessage(msg: string) { + postMessage(msg) { this.ws.send(msg); } } -export function main(addr: string) { - let root = window.document.getElementById("main"); - - if (root != null) { - window.ipc = new IPC(root, addr); - } -} +main(); diff --git a/packages/liveview/src/pool.rs b/packages/liveview/src/pool.rs index 1721fe87d0..dc5f7ad41f 100644 --- a/packages/liveview/src/pool.rs +++ b/packages/liveview/src/pool.rs @@ -1,6 +1,6 @@ use crate::{ - document::init_eval, element::LiveviewElement, + eval::init_eval, events::SerializedHtmlEventConverter, query::{QueryEngine, QueryResult}, LiveViewError, @@ -234,7 +234,9 @@ pub async fn run(mut vdom: VirtualDom, ws: impl LiveViewSocket) -> Result<(), Li } } + // wait for suspense to resolve in a 10ms window tokio::select! { + _ = tokio::time::sleep(Duration::from_millis(10)) => {} _ = vdom.wait_for_suspense() => {} } diff --git a/packages/liveview/tsconfig.json b/packages/liveview/tsconfig.json deleted file mode 100644 index 82dceb08ec..0000000000 --- a/packages/liveview/tsconfig.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "compilerOptions": { - "module": "CommonJS", - "lib": [ - "ES2015", - "DOM", - "dom", - "dom.iterable", - "ESNext" - ], - "noImplicitAny": true, - "removeComments": true, - "preserveConstEnums": true, - "typeRoots": [".src/ts/types"] - }, - "exclude": [ - "**/*.spec.ts" - ] -} diff --git a/packages/playwright-tests/fullstack/src/main.rs b/packages/playwright-tests/fullstack/src/main.rs index e52b07c5cd..59edd973ec 100644 --- a/packages/playwright-tests/fullstack/src/main.rs +++ b/packages/playwright-tests/fullstack/src/main.rs @@ -8,7 +8,7 @@ use dioxus::{prelude::*, CapturedError}; fn main() { - launch(app); + LaunchBuilder::fullstack().launch(app); } fn app() -> Element { @@ -16,8 +16,8 @@ fn app() -> Element { let mut text = use_signal(|| "...".to_string()); rsx! { - document::Title { "hello axum! {count}" } h1 { "hello axum! {count}" } + Title { "hello axum! {count}" } button { class: "increment-button", onclick: move |_| count += 1, "Increment" } button { class: "server-button", diff --git a/packages/playwright-tests/nested-suspense/src/main.rs b/packages/playwright-tests/nested-suspense/src/main.rs index 0570313090..c8e275ff96 100644 --- a/packages/playwright-tests/nested-suspense/src/main.rs +++ b/packages/playwright-tests/nested-suspense/src/main.rs @@ -12,7 +12,7 @@ use dioxus::prelude::*; use serde::{Deserialize, Serialize}; fn main() { - launch(app); + LaunchBuilder::fullstack().launch(app); } fn app() -> Element { @@ -44,7 +44,7 @@ fn LoadTitle() -> Element { .unwrap(); rsx! { - document::Title { "{title.title}" } + Title { "{title.title}" } } } diff --git a/packages/playwright-tests/static-generation/src/main.rs b/packages/playwright-tests/static-generation/src/main.rs index f4e870fb1c..06a98c2219 100644 --- a/packages/playwright-tests/static-generation/src/main.rs +++ b/packages/playwright-tests/static-generation/src/main.rs @@ -8,7 +8,7 @@ use dioxus::prelude::*; fn main() { - launch(app); + LaunchBuilder::static_generation().launch(app); } fn app() -> Element { diff --git a/packages/playwright-tests/suspense-carousel/Cargo.toml b/packages/playwright-tests/suspense-carousel/Cargo.toml index 062c396057..c71c29fe1c 100644 --- a/packages/playwright-tests/suspense-carousel/Cargo.toml +++ b/packages/playwright-tests/suspense-carousel/Cargo.toml @@ -5,15 +5,10 @@ version.workspace = true publish = false [dependencies] +async-std = "1.12.0" dioxus = { workspace = true, features = ["fullstack"] } serde = "1.0.159" -[target.'cfg(not(target_arch = "wasm32"))'.dependencies] -tokio = { workspace = true, features = ["full"] } - -[target.'cfg(target_arch = "wasm32")'.dependencies] -gloo-timers = { workspace = true } - [features] default = [] server = ["dioxus/axum"] diff --git a/packages/playwright-tests/suspense-carousel/src/main.rs b/packages/playwright-tests/suspense-carousel/src/main.rs index b3661abe61..6d304ba88a 100644 --- a/packages/playwright-tests/suspense-carousel/src/main.rs +++ b/packages/playwright-tests/suspense-carousel/src/main.rs @@ -58,7 +58,7 @@ impl ResolvedOn { #[component] fn SuspendedComponent(id: i32) -> Element { let resolved_on = use_server_future(move || async move { - sleep(1000).await; + async_std::task::sleep(std::time::Duration::from_secs(1)).await; ResolvedOn::CURRENT })?() .unwrap(); @@ -89,7 +89,7 @@ fn SuspendedComponent(id: i32) -> Element { #[component] fn NestedSuspendedComponent(id: i32) -> Element { let resolved_on = use_server_future(move || async move { - sleep(1000).await; + async_std::task::sleep(std::time::Duration::from_secs(1)).await; ResolvedOn::CURRENT })?() .unwrap(); @@ -106,14 +106,6 @@ fn NestedSuspendedComponent(id: i32) -> Element { } } -async fn sleep(millis: u64) { - #[cfg(target_arch = "wasm32")] - gloo_timers::future::sleep(std::time::Duration::from_millis(millis)).await; - - #[cfg(not(target_arch = "wasm32"))] - tokio::time::sleep(std::time::Duration::from_millis(millis)).await; -} - fn main() { launch(app); } diff --git a/packages/playwright-tests/web/src/main.rs b/packages/playwright-tests/web/src/main.rs index bbcb9bb57d..126f729e4c 100644 --- a/packages/playwright-tests/web/src/main.rs +++ b/packages/playwright-tests/web/src/main.rs @@ -8,8 +8,8 @@ fn app() -> Element { rsx! { div { - document::Title { "hello axum! {num}" } "hello axum! {num}" + Title { "hello axum! {num}" } button { class: "increment-button", onclick: move |_| num += 1, "Increment" } } svg { circle { cx: 50, cy: 50, r: 40, stroke: "green", fill: "yellow" } } @@ -24,7 +24,7 @@ fn app() -> Element { button { class: "eval-button", onclick: move |_| async move { - let mut eval = document::eval( + let mut eval = eval( r#" window.document.title = 'Hello from Dioxus Eval!'; // Receive and multiply 10 numbers @@ -36,18 +36,17 @@ fn app() -> Element { "#, ); - todo!("no more dioxus evaluator thing anymore - just a plain-old evail") - // // Send 10 numbers - // for i in 0..10 { - // eval.send(serde_json::Value::from(i)).unwrap(); - // let value = eval.recv().await.unwrap(); - // assert_eq!(value, serde_json::Value::from(i * 2)); - // } + // Send 10 numbers + for i in 0..10 { + eval.send(serde_json::Value::from(i)).unwrap(); + let value = eval.recv().await.unwrap(); + assert_eq!(value, serde_json::Value::from(i * 2)); + } - // let result = eval.recv().await; - // if let Ok(serde_json::Value::String(string)) = result { - // eval_result.set(string); - // } + let result = eval.recv().await; + if let Ok(serde_json::Value::String(string)) = result { + eval_result.set(string); + } }, "Eval" } diff --git a/packages/router-macro/src/lib.rs b/packages/router-macro/src/lib.rs index 359493a339..5d7bd03e65 100644 --- a/packages/router-macro/src/lib.rs +++ b/packages/router-macro/src/lib.rs @@ -752,14 +752,6 @@ impl RouteEnum { _ => VNode::empty() } } - - fn serialize(&self) -> String { - self.to_string() - } - - fn deserialize(input: &str) -> Result> { - std::str::FromStr::from_str(input).map_err(|err| Box::new(err) as _) - } } } } diff --git a/packages/router/Cargo.toml b/packages/router/Cargo.toml index cd8e9df053..2cbe5d6cff 100644 --- a/packages/router/Cargo.toml +++ b/packages/router/Cargo.toml @@ -11,19 +11,19 @@ keywords = ["dom", "ui", "gui", "react", "wasm"] [dependencies] dioxus-lib = { workspace = true } -dioxus-document = { workspace = true } dioxus-router-macro = { workspace = true } -gloo = { workspace = true, optional = true } +gloo = { version = "0.8.0", optional = true } tracing = { workspace = true } -urlencoding = { workspace = true } -serde = { workspace = true, features = ["derive"], optional = true } -serde_json = { workspace = true, optional = true } +urlencoding = "2.1.3" +serde = { version = "1", features = ["derive"], optional = true } +serde_json = { version = "1.0.91", optional = true } url = "2.3.1" - wasm-bindgen = { workspace = true, optional = true } -web-sys = { version = "0.3.60", optional = true, features = ["ScrollRestoration"] } +web-sys = { version = "0.3.60", optional = true, features = [ + "ScrollRestoration", +] } js-sys = { version = "0.3.63", optional = true } -gloo-utils = { workspace = true, optional = true } +gloo-utils = { version = "0.1.6", optional = true } dioxus-liveview = { workspace = true, optional = true } dioxus-ssr = { workspace = true, optional = true } http = { workspace = true, optional = true } @@ -36,19 +36,18 @@ rustversion = "1.0.17" # dev-dependncey crates [target.'cfg(target_family = "wasm")'.dev-dependencies] console_error_panic_hook = "0.1.7" -gloo = { workspace = true } -wasm-bindgen-test = "0.3.33" -# dioxus-router = { workspace = true } -# dioxus-router = { workspace = true, features = ["web"] } +dioxus-router = { workspace = true, features = ["web"] } # dioxus-web = { workspace = true } +gloo = "0.8.0" +wasm-bindgen-test = "0.3.33" [features] default = [] -# ssr = [] -# wasm_test = [] -# liveview = ["dioxus-liveview", "dep:tokio", "dep:serde", "dep:serde_json"] -# web = ["dep:gloo", "dep:web-sys", "dep:wasm-bindgen", "dep:gloo-utils", "dep:js-sys", "dioxus-router-macro/web"] -# fullstack = ["dep:dioxus-fullstack"] +ssr = [] +liveview = ["dioxus-liveview", "dep:tokio", "dep:serde", "dep:serde_json"] +wasm_test = [] +web = ["dep:gloo", "dep:web-sys", "dep:wasm-bindgen", "dep:gloo-utils", "dep:js-sys", "dioxus-router-macro/web"] +fullstack = ["dep:dioxus-fullstack"] [dev-dependencies] axum = { workspace = true, features = ["ws"] } diff --git a/packages/router/examples/manual.rs b/packages/router/examples/manual.rs deleted file mode 100644 index de3c581cd6..0000000000 --- a/packages/router/examples/manual.rs +++ /dev/null @@ -1,68 +0,0 @@ -use std::num::ParseIntError; - -use dioxus::prelude::*; - -fn main() { - launch(|| { - rsx! { - Router:: {} - } - }); -} - -#[derive(Clone, PartialEq, Debug)] -enum Route { - Home, - About, - Blog { id: usize }, -} - -impl Routable for Route { - const SITE_MAP: &'static [SiteMapSegment] = &[]; - - fn render(&self, _level: usize) -> Element { - rsx! { - div { - nav { - Link { to: Route::Home, "Home" } - Link { to: Route::About, "About" } - Link { to: "/home", "About" } - Link { to: "https://example.com/about", "Other about" } - } - match self { - Route::Home => rsx! { - h1 { "Home" } - }, - Route::About => rsx! { - h1 { "About" } - }, - Route::Blog { id } => rsx! { - h1 { "Blog" } - p { "Id: {id}" } - }, - } - } - } - } - - fn serialize(&self) -> String { - match self { - Route::Home => "/".to_string(), - Route::About => "/about".to_string(), - Route::Blog { id } => format!("/blog/{id}"), - } - } - - fn deserialize(route: &str) -> Result> { - match route { - "/" => Ok(Route::Home), - "/about" => Ok(Route::About), - blah if blah.starts_with("/blog/") => blah - .strip_prefix("/blog/") - .ok_or_else(|| format!("Failed to parse route: {}", route).into()) - .and_then(|s| s.parse().map_err(|e: ParseIntError| e.to_string().into())) - .map(|id| Route::Blog { id }), - _ => Err(format!("Failed to parse route: {}", route).into()), - } - } -} diff --git a/packages/router/examples/simple_routes.rs b/packages/router/examples/simple_routes.rs new file mode 100644 index 0000000000..3ee695dee6 --- /dev/null +++ b/packages/router/examples/simple_routes.rs @@ -0,0 +1,204 @@ +use dioxus::prelude::*; +use std::str::FromStr; + +#[cfg(feature = "liveview")] +#[tokio::main] +async fn main() { + use axum::{extract::ws::WebSocketUpgrade, response::Html, routing::get, Router}; + + let listen_address: std::net::SocketAddr = ([127, 0, 0, 1], 3030).into(); + let view = dioxus_liveview::LiveViewPool::new(); + let app = Router::new() + .fallback(get(move || async move { + Html(format!( + r#" + + + +
      + {glue} + + "#, + glue = dioxus_liveview::interpreter_glue(&format!("ws://{listen_address}/ws")) + )) + })) + .route( + "/ws", + get(move |ws: WebSocketUpgrade| async move { + ws.on_upgrade(move |socket| async move { + _ = view.launch(dioxus_liveview::axum_socket(socket), app).await; + }) + }), + ); + + println!("Listening on http://{listen_address}"); + + let listener = tokio::net::TcpListener::bind(&listen_address) + .await + .unwrap(); + + axum::serve(listener, app.into_make_service()) + .await + .unwrap(); +} + +#[cfg(not(feature = "liveview"))] +fn main() { + launch(app) +} + +fn app() -> Element { + rsx! { + Router:: {} + } +} + +#[component] +fn UserFrame(user_id: usize) -> Element { + rsx! { + pre { "UserFrame{{\n\tuser_id:{user_id}\n}}" } + div { background_color: "rgba(0,0,0,50%)", + "children:" + Outlet:: {} + } + } +} + +#[component] +fn Route1(user_id: usize, dynamic: usize, query: String) -> Element { + rsx! { + pre { + "Route1{{\n\tuser_id:{user_id},\n\tdynamic:{dynamic},\n\tquery:{query}\n}}" + } + Link { + to: Route::Route1 { + user_id, + dynamic, + query: String::new(), + }, + "Route1 with extra+\".\"" + } + p { "Footer" } + Link { + to: Route::Route3 { + dynamic: String::new(), + }, + "Home" + } + } +} + +#[component] +fn Route2(user_id: usize) -> Element { + rsx! { + pre { "Route2{{\n\tuser_id:{user_id}\n}}" } + {(0..user_id).map(|i| rsx! { p { "{i}" } })} + p { "Footer" } + Link { + to: Route::Route3 { + dynamic: String::new(), + }, + "Home" + } + } +} + +#[component] +fn Route3(dynamic: String) -> Element { + let mut current_route_str = use_signal(String::new); + + let current_route = use_route(); + let parsed = Route::from_str(¤t_route_str.read()); + + let site_map = Route::SITE_MAP + .iter() + .flat_map(|seg| seg.flatten().into_iter()) + .collect::>(); + + let navigator = use_navigator(); + + rsx! { + input { + oninput: move |evt: FormEvent| { + *current_route_str.write() = evt.value(); + }, + value: "{current_route_str}" + } + "dynamic: {dynamic}" + Link { to: Route::Route2 { user_id: 8888 }, "hello world link" } + button { + disabled: !navigator.can_go_back(), + onclick: move |_| { + navigator.go_back(); + }, + "go back" + } + button { + disabled: !navigator.can_go_forward(), + onclick: move |_| { + navigator.go_forward(); + }, + "go forward" + } + button { + onclick: move |_| { + navigator.push("https://www.google.com"); + }, + "google link" + } + p { "Site Map" } + pre { "{site_map:#?}" } + p { "Dynamic link" } + match parsed { + Ok(route) => { + if route != current_route { + rsx! { + Link { + to: route.clone(), + "{route}" + } + } + } + else { + VNode::empty() + } + } + Err(err) => { + rsx! { + pre { + color: "red", + "Invalid route:\n{err}" + } + } + } + } + } +} + +#[rustfmt::skip] +#[derive(Clone, Debug, PartialEq, Routable)] +enum Route { + #[nest("/test")] + // Nests with parameters have types taken from child routes + #[nest("/user/:user_id")] + // Everything inside the nest has the added parameter `user_id: usize` + // UserFrame is a layout component that will receive the `user_id: usize` parameter + #[layout(UserFrame)] + #[route("/:dynamic?:query")] + Route1 { + // The type is taken from the first instance of the dynamic parameter + user_id: usize, + dynamic: usize, + query: String, + }, + #[route("/hello_world")] + // You can opt out of the layout by using the `!` prefix + #[layout(!UserFrame)] + Route2 { user_id: usize }, + #[end_layout] + #[end_nest] + #[end_nest] + #[redirect("/:id/user", |id: usize| Route::Route3 { dynamic: id.to_string()})] + #[route("/:dynamic")] + Route3 { dynamic: String }, +} diff --git a/packages/router/src/components/link.rs b/packages/router/src/components/link.rs index 6fa428a4bd..c8054cfcde 100644 --- a/packages/router/src/components/link.rs +++ b/packages/router/src/components/link.rs @@ -1,12 +1,83 @@ #![allow(clippy::type_complexity)] -use crate::prelude::Routable; -use crate::utils::use_router_internal::use_router_internal; -use crate::{navigation::NavigationTarget, prelude::IntoRoutable}; -use dioxus_lib::prelude::*; +use std::any::Any; use std::fmt::Debug; +use std::rc::Rc; + +use dioxus_lib::prelude::*; + use tracing::error; +use crate::navigation::NavigationTarget; +use crate::prelude::Routable; +use crate::utils::use_router_internal::use_router_internal; + +use url::Url; + +/// Something that can be converted into a [`NavigationTarget`]. +#[derive(Clone)] +pub enum IntoRoutable { + /// A raw string target. + FromStr(String), + /// A internal target. + Route(Rc), +} + +impl PartialEq for IntoRoutable { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (IntoRoutable::FromStr(a), IntoRoutable::FromStr(b)) => a == b, + (IntoRoutable::Route(a), IntoRoutable::Route(b)) => Rc::ptr_eq(a, b), + _ => false, + } + } +} + +impl From for IntoRoutable { + fn from(value: R) -> Self { + IntoRoutable::Route(Rc::new(value) as Rc) + } +} + +impl From> for IntoRoutable { + fn from(value: NavigationTarget) -> Self { + match value { + NavigationTarget::Internal(route) => IntoRoutable::Route(Rc::new(route) as Rc), + NavigationTarget::External(url) => IntoRoutable::FromStr(url), + } + } +} + +impl From for IntoRoutable { + fn from(value: String) -> Self { + IntoRoutable::FromStr(value) + } +} + +impl From<&String> for IntoRoutable { + fn from(value: &String) -> Self { + IntoRoutable::FromStr(value.to_string()) + } +} + +impl From<&str> for IntoRoutable { + fn from(value: &str) -> Self { + IntoRoutable::FromStr(value.to_string()) + } +} + +impl From for IntoRoutable { + fn from(url: Url) -> Self { + IntoRoutable::FromStr(url.to_string()) + } +} + +impl From<&Url> for IntoRoutable { + fn from(url: &Url) -> Self { + IntoRoutable::FromStr(url.to_string()) + } +} + /// The properties for a [`Link`]. #[derive(Props, Clone, PartialEq)] pub struct LinkProps { @@ -157,14 +228,11 @@ pub fn Link(props: LinkProps) -> Element { }; let current_url = router.current_route_string(); - let href: String = todo!(); - // let parsed_route: NavigationTarget<_> = todo!(); - // let href = router.any_route_to_string(&to); - // let href = match &to { - // // IntoRoutable::FromStr(url) => url.to_string(), - // // IntoRoutable::Route(route) => router.any_route_to_string(&**route), - // }; - // let parsed_route: NavigationTarget = router.resolve_into_routable(to.clone()); + let href = match &to { + IntoRoutable::FromStr(url) => url.to_string(), + IntoRoutable::Route(route) => router.any_route_to_string(&**route), + }; + let parsed_route: NavigationTarget> = router.resolve_into_routable(to.clone()); let mut class_ = String::new(); if let Some(c) = class { @@ -189,8 +257,7 @@ pub fn Link(props: LinkProps) -> Element { let tag_target = new_tab.then_some("_blank"); - let is_external = to.is_external(); - // let is_external = matches!(parsed_route, NavigationTarget::External(_)); + let is_external = matches!(parsed_route, NavigationTarget::External(_)); let is_router_nav = !is_external && !new_tab; let rel = rel.or_else(|| is_external.then_some("noopener noreferrer".to_string())); @@ -207,10 +274,9 @@ pub fn Link(props: LinkProps) -> Element { } event.prevent_default(); - todo!(); - // if do_default && is_router_nav { - // router.push_any(router.resolve_into_routable(to.clone())); - // } + if do_default && is_router_nav { + router.push_any(router.resolve_into_routable(to.clone())); + } if let Some(handler) = onclick { handler.call(event); @@ -229,8 +295,8 @@ pub fn Link(props: LinkProps) -> Element { let liveview_prevent_default = { // If the event is a click with the left mouse button and no modifiers, prevent the default action // and navigate to the href with client side routing - router.is_synchronous().then_some( - "if (event.button === 0 && !event.ctrlKey && !event.metaKey && !event.shiftKey && !event.altKey) { event.preventDefault() }" + router.is_liveview().then_some( + "if (event.button === 0 && !event.ctrlKey && !event.metaKey && !event.shiftKey && !event.altKey) { event.preventDefault() }" ) }; @@ -239,7 +305,7 @@ pub fn Link(props: LinkProps) -> Element { onclick: action, "onclick": liveview_prevent_default, href, - onmounted, + onmounted: onmounted, class, rel, target: tag_target, diff --git a/packages/router/src/components/mod.rs b/packages/router/src/components/mod.rs deleted file mode 100644 index e13972810a..0000000000 --- a/packages/router/src/components/mod.rs +++ /dev/null @@ -1,15 +0,0 @@ -/// Components interacting with the router. -mod default_errors; -pub use default_errors::*; - -mod history_buttons; -pub use history_buttons::*; - -mod link; -pub use link::*; - -mod outlet; -pub use outlet::*; - -mod router; -pub use router::*; diff --git a/packages/router/src/components/outlet.rs b/packages/router/src/components/outlet.rs index e79ab00d31..e7ffd9c9a1 100644 --- a/packages/router/src/components/outlet.rs +++ b/packages/router/src/components/outlet.rs @@ -68,6 +68,6 @@ use dioxus_lib::prelude::*; /// # vdom.rebuild_in_place(); /// # assert_eq!(dioxus_ssr::render(&vdom), "

      App

      Child

      "); /// ``` -pub fn Outlet() -> Element { +pub fn Outlet() -> Element { OutletContext::::render() } diff --git a/packages/router/src/components/router.rs b/packages/router/src/components/router.rs index 231e3d6281..1a8c066fa6 100644 --- a/packages/router/src/components/router.rs +++ b/packages/router/src/components/router.rs @@ -1,26 +1,29 @@ use dioxus_lib::prelude::*; +use std::str::FromStr; + use crate::{ - prelude::{Outlet, RouterContext}, + prelude::{provide_router_context, Outlet}, routable::Routable, router_cfg::RouterConfig, }; /// The props for [`Router`]. #[derive(Props)] -pub struct RouterProps { +pub struct RouterProps { #[props(default, into)] config: Callback<(), RouterConfig>, } -impl Clone for RouterProps { +impl Clone for RouterProps { fn clone(&self) -> Self { *self } } -impl Copy for RouterProps {} -impl Default for RouterProps { +impl Copy for RouterProps {} + +impl Default for RouterProps { fn default() -> Self { Self { config: Callback::new(|_| RouterConfig::default()), @@ -28,7 +31,7 @@ impl Default for RouterProps { } } -impl PartialEq for RouterProps { +impl PartialEq for RouterProps { fn eq(&self, _: &Self) -> bool { // prevent the router from re-rendering when the initial url or config changes true @@ -36,18 +39,17 @@ impl PartialEq for RouterProps { } /// A component that renders the current route. -pub fn Router(props: RouterProps) -> Element { +pub fn Router(props: RouterProps) -> Element +where + ::Err: std::fmt::Display, +{ use crate::prelude::{outlet::OutletContext, RouterContext}; use_hook(|| { - let ctx = RouterContext::new(props.config.call(())); - if root_router().is_none() { - ScopeId::ROOT.provide_context(RootRouterContext(Signal::new_in_scope( - Some(ctx), - ScopeId::ROOT, - ))); - } - provide_context(ctx); + provide_router_context(RouterContext::new( + props.config.call(()), + schedule_update_any(), + )); provide_context(OutletContext:: { current_level: 0, @@ -55,23 +57,5 @@ pub fn Router(props: RouterProps) -> Element { }); }); - rsx! { - Outlet:: {} - } -} - -/// This context is set in the root of the virtual dom if there is a router present. -#[derive(Clone, Copy)] -pub(crate) struct RootRouterContext(pub(crate) Signal>); - -/// Try to get the router that was created closest to the root of the virtual dom. This may be called outside of the router. -/// -/// This will return `None` if there is no router present or the router has not been created yet. -pub fn root_router() -> Option { - if let Some(ctx) = ScopeId::ROOT.consume_context::() { - ctx.0.cloned() - } else { - ScopeId::ROOT.provide_context(RootRouterContext(Signal::new_in_scope(None, ScopeId::ROOT))); - None - } + rsx! { Outlet:: {} } } diff --git a/packages/router/src/contexts/generic_router.rs b/packages/router/src/contexts/generic_router.rs deleted file mode 100644 index 26e9e085d8..0000000000 --- a/packages/router/src/contexts/generic_router.rs +++ /dev/null @@ -1,102 +0,0 @@ -use core::panic; -use std::{ - any::Any, - collections::HashSet, - rc::Rc, - sync::{Arc, RwLock}, -}; - -use dioxus_document::DocumentContext; -use dioxus_lib::prelude::*; - -use crate::{ - navigation::NavigationTarget, - prelude::{IntoRoutable, SiteMapSegment}, - routable::Routable, - router_cfg::RouterConfig, -}; - -use super::{ExternalNavigationFailure, RouterContext}; - -pub struct GenericRouterContext { - inner: RouterContext, - _marker: std::marker::PhantomData, -} - -impl GenericRouterContext { - /// Check whether there is a previous page to navigate back to. - #[must_use] - pub fn can_go_back(&self) -> bool { - self.inner.can_go_back() - } - - /// Check whether there is a future page to navigate forward to. - #[must_use] - pub fn can_go_forward(&self) -> bool { - self.inner.can_go_forward() - } - - /// Go back to the previous location. - /// - /// Will fail silently if there is no previous location to go to. - pub fn go_back(&self) { - self.inner.go_back(); - } - - /// Go back to the next location. - /// - /// Will fail silently if there is no next location to go to. - pub fn go_forward(&self) { - self.inner.go_forward(); - } - - /// Push a new location. - /// - /// The previous location will be available to go back to. - pub fn push( - &self, - target: impl Into>, - ) -> Option { - todo!() - // self.inner.push(target.into()) - } - - /// Replace the current location. - /// - /// The previous location will **not** be available to go back to. - pub fn replace( - &self, - target: impl Into>, - ) -> Option { - todo!() - // self.inner.replace(target.into()) - } - - /// The route that is currently active. - pub fn current(&self) -> R - where - R: Clone, - { - self.inner.current() - } - - /// The prefix that is currently active. - pub fn prefix(&self) -> Option { - self.inner.prefix() - } - - /// Manually subscribe to the current route - pub fn subscribe(&self, id: ScopeId) { - self.inner.subscribe(id) - } - - /// Manually unsubscribe from the current route - pub fn unsubscribe(&self, id: ScopeId) { - self.inner.unsubscribe(id) - } - - /// Clear any unresolved errors - pub fn clear_error(&self) { - self.inner.clear_error() - } -} diff --git a/packages/router/src/contexts/outlet.rs b/packages/router/src/contexts/outlet.rs index c64d0bbfc7..664ea19f5b 100644 --- a/packages/router/src/contexts/outlet.rs +++ b/packages/router/src/contexts/outlet.rs @@ -28,7 +28,7 @@ pub(crate) fn use_outlet_context() -> OutletContext { impl OutletContext { pub(crate) fn render() -> Element where - R: Routable, + R: Routable + Clone, { let router = use_router_internal().expect("Outlet must be inside of a router"); let outlet: OutletContext = use_outlet_context(); diff --git a/packages/router/src/contexts/router.rs b/packages/router/src/contexts/router.rs index f404d6362f..26341b07e2 100644 --- a/packages/router/src/contexts/router.rs +++ b/packages/router/src/contexts/router.rs @@ -1,100 +1,161 @@ use std::{ + any::Any, collections::HashSet, rc::Rc, sync::{Arc, RwLock}, }; -use dioxus_document::DocumentContext; use dioxus_lib::prelude::*; use crate::{ navigation::NavigationTarget, - prelude::{IntoRoutable, SiteMapSegment}, + prelude::{AnyHistoryProvider, IntoRoutable, SiteMapSegment}, routable::Routable, router_cfg::RouterConfig, }; -use super::generic_router::GenericRouterContext; - -/// A collection of router data that manages all routing functionality. +/// This context is set in the root of the virtual dom if there is a router present. #[derive(Clone, Copy)] -pub struct RouterContext { - inner: CopyValue, +struct RootRouterContext(Signal>); + +/// Try to get the router that was created closest to the root of the virtual dom. This may be called outside of the router. +/// +/// This will return `None` if there is no router present or the router has not been created yet. +pub fn root_router() -> Option { + if let Some(ctx) = ScopeId::ROOT.consume_context::() { + ctx.0.cloned() + } else { + ScopeId::ROOT.provide_context(RootRouterContext(Signal::new_in_scope(None, ScopeId::ROOT))); + None + } } -struct RouterContextInner { - basepath: Option, +pub(crate) fn provide_router_context(ctx: RouterContext) { + if root_router().is_none() { + ScopeId::ROOT.provide_context(RootRouterContext(Signal::new_in_scope( + Some(ctx), + ScopeId::ROOT, + ))); + } + provide_context(ctx); +} + +/// An error that can occur when navigating. +#[derive(Debug, Clone)] +pub struct ExternalNavigationFailure(pub String); + +/// A function the router will call after every routing update. +pub(crate) type RoutingCallback = + Arc) -> Option>>; +pub(crate) type AnyRoutingCallback = + Arc Option>>>; - runtime: Rc, +struct RouterContextInner { + /// The current prefix. + prefix: Option, - document: DocumentContext, + history: Box, unresolved_error: Option, subscribers: Arc>>, + subscriber_update: Arc, + routing_callback: Option, failure_external_navigation: fn() -> Element, + any_route_to_string: fn(&dyn Any) -> String, + site_map: &'static [SiteMapSegment], - // any_route_to_string: fn(&dyn Any) -> String, - // routing_callback: Option, +} + +impl RouterContextInner { + fn update_subscribers(&self) { + let update = &self.subscriber_update; + for &id in self.subscribers.read().unwrap().iter() { + update(id); + } + } + + fn external(&mut self, external: String) -> Option { + match self.history.external(external.clone()) { + true => None, + false => { + let failure = ExternalNavigationFailure(external); + self.unresolved_error = Some(failure.clone()); + + self.update_subscribers(); + + Some(failure) + } + } + } +} + +/// A collection of router data that manages all routing functionality. +#[derive(Clone, Copy)] +pub struct RouterContext { + inner: CopyValue, } impl RouterContext { - pub(crate) fn new(mut cfg: RouterConfig) -> Self { + pub(crate) fn new( + mut cfg: RouterConfig, + mark_dirty: Arc, + ) -> Self + where + ::Err: std::fmt::Display, + { + let subscriber_update = mark_dirty.clone(); let subscribers = Arc::new(RwLock::new(HashSet::new())); let mut myself = RouterContextInner { - basepath: Default::default(), + prefix: Default::default(), + history: cfg.take_history(), unresolved_error: None, subscribers: subscribers.clone(), - document: todo!(), + subscriber_update, + + routing_callback: cfg.on_update.map(|update| { + Arc::new(move |ctx| { + let ctx = GenericRouterContext { + inner: ctx, + _marker: std::marker::PhantomData, + }; + update(ctx).map(|t| match t { + NavigationTarget::Internal(r) => { + NavigationTarget::Internal(Rc::new(r) as Rc) + } + NavigationTarget::External(s) => NavigationTarget::External(s), + }) + }) + as Arc Option>>> + }), + failure_external_navigation: cfg.failure_external_navigation, - site_map: todo!(), - runtime: todo!(), - // history: cfg.take_history(), - - // subscriber_update, - - // routing_callback: cfg.on_update.map(|update| { - // Arc::new(move |ctx| { - // let ctx = GenericRouterContext { - // inner: ctx, - // _marker: std::marker::PhantomData, - // }; - // update(ctx).map(|t| match t { - // NavigationTarget::Internal(r) => { - // NavigationTarget::Internal(Rc::new(r) as Rc) - // } - // NavigationTarget::External(s) => NavigationTarget::External(s), - // }) - // }) - // as Arc Option>>> - // }), - // any_route_to_string: |route| { - // todo!() - // route - // .downcast_ref::() - // .unwrap_or_else(|| { - // panic!( - // "Route is not of the expected type: {}\n found typeid: {:?}\n expected typeid: {:?}", - // std::any::type_name::(), - // route.type_id(), - // std::any::TypeId::of::() - // ) - // }) - // .to_string() - // }, - // site_map: R::SITE_MAP, - // routing_callback: todo!(), + + any_route_to_string: |route| { + route + .downcast_ref::() + .unwrap_or_else(|| { + panic!( + "Route is not of the expected type: {}\n found typeid: {:?}\n expected typeid: {:?}", + std::any::type_name::(), + route.type_id(), + std::any::TypeId::of::() + ) + }) + .to_string() + }, + + site_map: R::SITE_MAP, }; // set the updater { - let rt = myself.runtime.clone(); - myself.document.updater(Arc::new(move || { + myself.history.updater(Arc::new(move || { for &id in subscribers.read().unwrap().iter() { - rt.mark_dirty(id); + (mark_dirty)(id); } })); } @@ -106,25 +167,31 @@ impl RouterContext { /// Check if the router is running in a liveview context /// We do some slightly weird things for liveview because of the network boundary - pub fn is_synchronous(&self) -> bool { - self.inner.read().document.is_synchronous() + pub fn is_liveview(&self) -> bool { + #[cfg(feature = "liveview")] + { + self.inner.read().history.is_liveview() + } + #[cfg(not(feature = "liveview"))] + { + false + } } - pub(crate) fn route_from_str(&self, route: &str) -> Result { - todo!() - // self.inner.read().history.parse_route(route) + pub(crate) fn route_from_str(&self, route: &str) -> Result, String> { + self.inner.read().history.parse_route(route) } /// Check whether there is a previous page to navigate back to. #[must_use] pub fn can_go_back(&self) -> bool { - self.inner.read().document.can_go_back() + self.inner.read().history.can_go_back() } /// Check whether there is a future page to navigate forward to. #[must_use] pub fn can_go_forward(&self) -> bool { - self.inner.read().document.can_go_forward() + self.inner.read().history.can_go_forward() } /// Go back to the previous location. @@ -132,7 +199,7 @@ impl RouterContext { /// Will fail silently if there is no previous location to go to. pub fn go_back(&self) { { - self.inner.write_unchecked().document.go_back(); + self.inner.write_unchecked().history.go_back(); } self.change_route(); @@ -143,23 +210,22 @@ impl RouterContext { /// Will fail silently if there is no next location to go to. pub fn go_forward(&self) { { - self.inner.write_unchecked().document.go_forward(); + self.inner.write_unchecked().history.go_forward(); } self.change_route(); } - pub(crate) fn push_any( + pub(crate) fn push_any( &self, - target: NavigationTarget, + target: NavigationTarget>, ) -> Option { - match target { - NavigationTarget::Internal(p) => self - .inner - .write_unchecked() - .document - .push_route(p.serialize()), - NavigationTarget::External(e) => return self.navigate_external(e), + { + let mut write = self.inner.write_unchecked(); + match target { + NavigationTarget::Internal(p) => write.history.push(p), + NavigationTarget::External(e) => return write.external(e), + } } self.change_route() @@ -169,77 +235,75 @@ impl RouterContext { /// /// The previous location will be available to go back to. pub fn push(&self, target: impl Into) -> Option { - todo!() - // let target = self.resolve_into_routable(target.into()); - // { - // let mut write = self.inner.write_unchecked(); - // match target { - // NavigationTarget::Internal(p) => write.history.push_route(p), - // NavigationTarget::External(e) => return write.external(e), - // } - // } + let target = self.resolve_into_routable(target.into()); + { + let mut write = self.inner.write_unchecked(); + match target { + NavigationTarget::Internal(p) => write.history.push(p), + NavigationTarget::External(e) => return write.external(e), + } + } - // self.change_route() + self.change_route() } /// Replace the current location. /// /// The previous location will **not** be available to go back to. pub fn replace(&self, target: impl Into) -> Option { - todo!() - // let target = self.resolve_into_routable(target.into()); + let target = self.resolve_into_routable(target.into()); - // { - // let mut state = self.inner.write_unchecked(); - // match target { - // NavigationTarget::Internal(p) => state.history.replace_route(p), - // NavigationTarget::External(e) => return state.external(e), - // } - // } + { + let mut state = self.inner.write_unchecked(); + match target { + NavigationTarget::Internal(p) => state.history.replace(p), + NavigationTarget::External(e) => return state.external(e), + } + } - // self.change_route() + self.change_route() } /// The route that is currently active. pub fn current(&self) -> R { - todo!() - // self.inner - // .read() - // .history - // .current_route() - // .parse() - // .unwrap_or_else(|err| panic!("Failed to parse route")) + self.inner + .read() + .history + .current_route() + .downcast::() + .unwrap() + .as_ref() + .clone() } /// The route that is currently active. pub fn current_route_string(&self) -> String { - self.inner.read_unchecked().document.current_route() + self.any_route_to_string(&*self.inner.read().history.current_route()) } - // pub(crate) fn any_route_to_string(&self, route: &dyn Any) -> String { - // (self.inner.read().any_route_to_string)(route) - // } + pub(crate) fn any_route_to_string(&self, route: &dyn Any) -> String { + (self.inner.read().any_route_to_string)(route) + } - pub(crate) fn resolve_into_routable( + pub(crate) fn resolve_into_routable( &self, into_routable: IntoRoutable, - ) -> NavigationTarget { - todo!() - // match into_routable { - // IntoRoutable::FromStr(url) => { - // let parsed_route: NavigationTarget> = match self.route_from_str(&url) { - // Ok(route) => NavigationTarget::Internal(route), - // Err(_) => NavigationTarget::External(url), - // }; - // parsed_route - // } - // IntoRoutable::Route(route) => NavigationTarget::Internal(route), - // } + ) -> NavigationTarget> { + match into_routable { + IntoRoutable::FromStr(url) => { + let parsed_route: NavigationTarget> = match self.route_from_str(&url) { + Ok(route) => NavigationTarget::Internal(route), + Err(_) => NavigationTarget::External(url), + }; + parsed_route + } + IntoRoutable::Route(route) => NavigationTarget::Internal(route), + } } /// The prefix that is currently active. pub fn prefix(&self) -> Option { - self.inner.read().basepath.clone() + self.inner.read().prefix.clone() } /// Manually subscribe to the current route @@ -254,11 +318,10 @@ impl RouterContext { /// Clear any unresolved errors pub fn clear_error(&self) { - { - let mut write_inner = self.inner.write_unchecked(); - write_inner.unresolved_error = None; - } - self.update_subscribers(); + let mut write_inner = self.inner.write_unchecked(); + write_inner.unresolved_error = None; + + write_inner.update_subscribers(); } /// Get the site map of the router. @@ -275,61 +338,106 @@ impl RouterContext { } fn change_route(&self) -> Option { - todo!() - // let self_read = self.inner.read(); - // if let Some(callback) = &self_read.routing_callback { - // let myself = *self; - // let callback = callback.clone(); - // drop(self_read); - // if let Some(new) = callback(myself) { - // let mut self_write = self.inner.write_unchecked(); - // match new { - // NavigationTarget::Internal(p) => self_write.history.replace_route(p), - // NavigationTarget::External(e) => return self_write.external(e), - // } - // } - // } - - // self.inner.read().update_subscribers(); - - // None + let self_read = self.inner.read(); + if let Some(callback) = &self_read.routing_callback { + let myself = *self; + let callback = callback.clone(); + drop(self_read); + if let Some(new) = callback(myself) { + let mut self_write = self.inner.write_unchecked(); + match new { + NavigationTarget::Internal(p) => self_write.history.replace(p), + NavigationTarget::External(e) => return self_write.external(e), + } + } + } + + self.inner.read().update_subscribers(); + + None } +} - fn update_subscribers(&self) { - let inner = self.inner.read_unchecked(); - for &id in inner.subscribers.read().unwrap().iter() { - inner.runtime.mark_dirty(id) - } +pub struct GenericRouterContext { + inner: RouterContext, + _marker: std::marker::PhantomData, +} + +impl GenericRouterContext +where + R: Routable, +{ + /// Check whether there is a previous page to navigate back to. + #[must_use] + pub fn can_go_back(&self) -> bool { + self.inner.can_go_back() } - fn navigate_external(&self, external: String) -> Option { - let failure = { - let mut myself = self.inner.write_unchecked(); - match myself.document.navigate_external(external.clone()) { - true => None, - false => { - let failure = ExternalNavigationFailure(external); - myself.unresolved_error = Some(failure.clone()); - Some(failure) - } - } - }; + /// Check whether there is a future page to navigate forward to. + #[must_use] + pub fn can_go_forward(&self) -> bool { + self.inner.can_go_forward() + } - if failure.is_some() { - self.update_subscribers(); - } + /// Go back to the previous location. + /// + /// Will fail silently if there is no previous location to go to. + pub fn go_back(&self) { + self.inner.go_back(); + } - failure + /// Go back to the next location. + /// + /// Will fail silently if there is no next location to go to. + pub fn go_forward(&self) { + self.inner.go_forward(); } -} -/// An error that can occur when navigating. -#[derive(Debug, Clone)] -pub struct ExternalNavigationFailure(pub String); + /// Push a new location. + /// + /// The previous location will be available to go back to. + pub fn push( + &self, + target: impl Into>, + ) -> Option { + self.inner.push(target.into()) + } -/// A function the router will call after every routing update. -pub(crate) type RoutingCallback = - Arc) -> Option>>; + /// Replace the current location. + /// + /// The previous location will **not** be available to go back to. + pub fn replace( + &self, + target: impl Into>, + ) -> Option { + self.inner.replace(target.into()) + } -pub(crate) type AnyRoutingCallback = Arc Option>>; -// Arc Option>>>; + /// The route that is currently active. + pub fn current(&self) -> R + where + R: Clone, + { + self.inner.current() + } + + /// The prefix that is currently active. + pub fn prefix(&self) -> Option { + self.inner.prefix() + } + + /// Manually subscribe to the current route + pub fn subscribe(&self, id: ScopeId) { + self.inner.subscribe(id) + } + + /// Manually unsubscribe from the current route + pub fn unsubscribe(&self, id: ScopeId) { + self.inner.unsubscribe(id) + } + + /// Clear any unresolved errors + pub fn clear_error(&self) { + self.inner.clear_error() + } +} diff --git a/packages/liveview/src/history.rs b/packages/router/src/history/liveview.rs similarity index 99% rename from packages/liveview/src/history.rs rename to packages/router/src/history/liveview.rs index fe200beed7..790b9d98e9 100644 --- a/packages/liveview/src/history.rs +++ b/packages/router/src/history/liveview.rs @@ -1,6 +1,7 @@ -// use crate::routable::Routable; -// use dioxus_lib::prelude::*; -// use document::UseEval; +use super::HistoryProvider; +use crate::routable::Routable; +use dioxus_lib::prelude::*; +use document::UseEval; use serde::{Deserialize, Serialize}; use std::sync::{Mutex, RwLock}; use std::{collections::BTreeMap, rc::Rc, str::FromStr, sync::Arc}; diff --git a/packages/router/src/history/memory.rs b/packages/router/src/history/memory.rs index 61f7f4eb42..fd4b43dd5d 100644 --- a/packages/router/src/history/memory.rs +++ b/packages/router/src/history/memory.rs @@ -1,5 +1,9 @@ +use std::str::FromStr; + use crate::routable::Routable; +use super::HistoryProvider; + /// A [`HistoryProvider`] that stores all navigation information in memory. pub struct MemoryHistory { current: R, @@ -7,7 +11,10 @@ pub struct MemoryHistory { future: Vec, } -impl MemoryHistory { +impl MemoryHistory +where + ::Err: std::fmt::Display, +{ /// Create a [`MemoryHistory`] starting at `path`. /// /// ```rust @@ -36,36 +43,53 @@ impl MemoryHistory { future: Vec::new(), } } +} - pub fn current_route(&self) -> R { +impl Default for MemoryHistory +where + ::Err: std::fmt::Display, +{ + fn default() -> Self { + Self { + current: "/".parse().unwrap_or_else(|err| { + panic!("index route does not exist:\n{err}\n use MemoryHistory::with_initial_path to set a custom path") + }), + history: Vec::new(), + future: Vec::new(), + } + } +} + +impl HistoryProvider for MemoryHistory { + fn current_route(&self) -> R { self.current.clone() } - pub fn can_go_back(&self) -> bool { + fn can_go_back(&self) -> bool { !self.history.is_empty() } - pub fn go_back(&mut self) { + fn go_back(&mut self) { if let Some(last) = self.history.pop() { let old = std::mem::replace(&mut self.current, last); self.future.push(old); } } - pub fn can_go_forward(&self) -> bool { + fn can_go_forward(&self) -> bool { !self.future.is_empty() } - pub fn go_forward(&mut self) { + fn go_forward(&mut self) { if let Some(next) = self.future.pop() { let old = std::mem::replace(&mut self.current, next); self.history.push(old); } } - pub fn push(&mut self, new: R) { + fn push(&mut self, new: R) { // don't push the same route twice - if self.current.serialize() == new.serialize() { + if self.current.to_string() == new.to_string() { return; } let old = std::mem::replace(&mut self.current, new); @@ -73,19 +97,7 @@ impl MemoryHistory { self.future.clear(); } - pub fn replace(&mut self, path: R) { + fn replace(&mut self, path: R) { self.current = path; } } - -impl Default for MemoryHistory { - fn default() -> Self { - Self { - current: R::deserialize("/").unwrap_or_else(|err| { - panic!("index route does not exist:\n{err}\n use MemoryHistory::with_initial_path to set a custom path") - }), - history: Vec::new(), - future: Vec::new(), - } - } -} diff --git a/packages/router/src/history/mod.rs b/packages/router/src/history/mod.rs index e498b36558..1b5d186126 100644 --- a/packages/router/src/history/mod.rs +++ b/packages/router/src/history/mod.rs @@ -10,5 +10,393 @@ //! 1) [`MemoryHistory`] for desktop/mobile/ssr platforms //! 2) [`WebHistory`] for web platforms +use std::{any::Any, rc::Rc, sync::Arc}; + mod memory; pub use memory::*; + +#[cfg(feature = "web")] +mod web; +#[cfg(feature = "web")] +pub use web::*; +#[cfg(feature = "web")] +pub(crate) mod web_history; + +#[cfg(feature = "liveview")] +mod liveview; +#[cfg(feature = "liveview")] +pub use liveview::*; + +// #[cfg(feature = "web")] +// mod web_hash; +// #[cfg(feature = "web")] +// pub use web_hash::*; + +use crate::routable::Routable; + +#[cfg(feature = "web")] +pub(crate) mod web_scroll; + +/// An integration with some kind of navigation history. +/// +/// Depending on your use case, your implementation may deviate from the described procedure. This +/// is fine, as long as both `current_route` and `current_query` match the described format. +/// +/// However, you should document all deviations. Also, make sure the navigation is user-friendly. +/// The described behaviors are designed to mimic a web browser, which most users should already +/// know. Deviations might confuse them. +pub trait HistoryProvider { + /// Get the path of the current URL. + /// + /// **Must start** with `/`. **Must _not_ contain** the prefix. + /// + /// ```rust + /// # use dioxus_router::prelude::*; + /// # use dioxus::prelude::*; + /// # #[component] + /// # fn Index() -> Element { VNode::empty() } + /// # #[component] + /// # fn OtherPage() -> Element { VNode::empty() } + /// #[derive(Clone, Routable, Debug, PartialEq)] + /// enum Route { + /// #[route("/")] + /// Index {}, + /// #[route("/some-other-page")] + /// OtherPage {}, + /// } + /// let mut history = MemoryHistory::::default(); + /// assert_eq!(history.current_route().to_string(), "/"); + /// + /// history.push(Route::OtherPage {}); + /// assert_eq!(history.current_route().to_string(), "/some-other-page"); + /// ``` + #[must_use] + fn current_route(&self) -> R; + + /// Get the current path prefix of the URL. + /// + /// Not all [`HistoryProvider`]s need a prefix feature. It is meant for environments where a + /// dioxus-router-core-routed application is not running on `/`. The [`HistoryProvider`] is responsible + /// for removing the prefix from the dioxus-router-core-internal path, and also for adding it back in + /// during navigation. This functions value is only used for creating `href`s (e.g. for SSR or + /// display (but not navigation) in a web app). + fn current_prefix(&self) -> Option { + None + } + + /// Check whether there is a previous page to navigate back to. + /// + /// If a [`HistoryProvider`] cannot know this, it should return [`true`]. + /// + /// ```rust + /// # use dioxus_router::prelude::*; + /// # use dioxus::prelude::*; + /// # #[component] + /// # fn Index() -> Element { VNode::empty() } + /// # fn Other() -> Element { VNode::empty() } + /// #[derive(Clone, Routable, Debug, PartialEq)] + /// enum Route { + /// #[route("/")] + /// Index {}, + /// #[route("/other")] + /// Other {}, + /// } + /// let mut history = MemoryHistory::::default(); + /// assert_eq!(history.can_go_back(), false); + /// + /// history.push(Route::Other {}); + /// assert_eq!(history.can_go_back(), true); + /// ``` + #[must_use] + fn can_go_back(&self) -> bool { + true + } + + /// Go back to a previous page. + /// + /// If a [`HistoryProvider`] cannot go to a previous page, it should do nothing. This method + /// might be called, even if `can_go_back` returns [`false`]. + /// + /// ```rust + /// # use dioxus_router::prelude::*; + /// # use dioxus::prelude::*; + /// # #[component] + /// # fn Index() -> Element { VNode::empty() } + /// # #[component] + /// # fn OtherPage() -> Element { VNode::empty() } + /// #[derive(Clone, Routable, Debug, PartialEq)] + /// enum Route { + /// #[route("/")] + /// Index {}, + /// #[route("/some-other-page")] + /// OtherPage {}, + /// } + /// let mut history = MemoryHistory::::default(); + /// assert_eq!(history.current_route().to_string(), "/"); + /// + /// history.go_back(); + /// assert_eq!(history.current_route().to_string(), "/"); + /// + /// history.push(Route::OtherPage {}); + /// assert_eq!(history.current_route().to_string(), "/some-other-page"); + /// + /// history.go_back(); + /// assert_eq!(history.current_route().to_string(), "/"); + /// ``` + fn go_back(&mut self); + + /// Check whether there is a future page to navigate forward to. + /// + /// If a [`HistoryProvider`] cannot know this, it should return [`true`]. + /// + /// ```rust + /// # use dioxus_router::prelude::*; + /// # use dioxus::prelude::*; + /// # #[component] + /// # fn Index() -> Element { VNode::empty() } + /// # #[component] + /// # fn OtherPage() -> Element { VNode::empty() } + /// #[derive(Clone, Routable, Debug, PartialEq)] + /// enum Route { + /// #[route("/")] + /// Index {}, + /// #[route("/some-other-page")] + /// OtherPage {}, + /// } + /// let mut history = MemoryHistory::::default(); + /// assert_eq!(history.can_go_forward(), false); + /// + /// history.push(Route::OtherPage {}); + /// assert_eq!(history.can_go_forward(), false); + /// + /// history.go_back(); + /// assert_eq!(history.can_go_forward(), true); + /// ``` + #[must_use] + fn can_go_forward(&self) -> bool { + true + } + + /// Go forward to a future page. + /// + /// If a [`HistoryProvider`] cannot go to a previous page, it should do nothing. This method + /// might be called, even if `can_go_forward` returns [`false`]. + /// + /// ```rust + /// # use dioxus_router::prelude::*; + /// # use dioxus::prelude::*; + /// # #[component] + /// # fn Index() -> Element { VNode::empty() } + /// # #[component] + /// # fn OtherPage() -> Element { VNode::empty() } + /// #[derive(Clone, Routable, Debug, PartialEq)] + /// enum Route { + /// #[route("/")] + /// Index {}, + /// #[route("/some-other-page")] + /// OtherPage {}, + /// } + /// let mut history = MemoryHistory::::default(); + /// history.push(Route::OtherPage {}); + /// assert_eq!(history.current_route(), Route::OtherPage {}); + /// + /// history.go_back(); + /// assert_eq!(history.current_route(), Route::Index {}); + /// + /// history.go_forward(); + /// assert_eq!(history.current_route(), Route::OtherPage {}); + /// ``` + fn go_forward(&mut self); + + /// Go to another page. + /// + /// This should do three things: + /// 1. Merge the current URL with the `path` parameter (which may also include a query part). + /// 2. Remove the previous URL to the navigation history. + /// 3. Clear the navigation future. + /// + /// ```rust + /// # use dioxus_router::prelude::*; + /// # use dioxus::prelude::*; + /// # #[component] + /// # fn Index() -> Element { VNode::empty() } + /// # #[component] + /// # fn OtherPage() -> Element { VNode::empty() } + /// #[derive(Clone, Routable, Debug, PartialEq)] + /// enum Route { + /// #[route("/")] + /// Index {}, + /// #[route("/some-other-page")] + /// OtherPage {}, + /// } + /// let mut history = MemoryHistory::::default(); + /// assert_eq!(history.current_route(), Route::Index {}); + /// + /// history.push(Route::OtherPage {}); + /// assert_eq!(history.current_route(), Route::OtherPage {}); + /// assert!(history.can_go_back()); + /// ``` + fn push(&mut self, route: R); + + /// Replace the current page with another one. + /// + /// This should merge the current URL with the `path` parameter (which may also include a query + /// part). In contrast to the `push` function, the navigation history and future should stay + /// untouched. + /// + /// ```rust + /// # use dioxus_router::prelude::*; + /// # use dioxus::prelude::*; + /// # #[component] + /// # fn Index() -> Element { VNode::empty() } + /// # #[component] + /// # fn OtherPage() -> Element { VNode::empty() } + /// #[derive(Clone, Routable, Debug, PartialEq)] + /// enum Route { + /// #[route("/")] + /// Index {}, + /// #[route("/some-other-page")] + /// OtherPage {}, + /// } + /// let mut history = MemoryHistory::::default(); + /// assert_eq!(history.current_route(), Route::Index {}); + /// + /// history.replace(Route::OtherPage {}); + /// assert_eq!(history.current_route(), Route::OtherPage {}); + /// assert!(!history.can_go_back()); + /// ``` + fn replace(&mut self, path: R); + + /// Navigate to an external URL. + /// + /// This should navigate to an external URL, which isn't controlled by the router. If a + /// [`HistoryProvider`] cannot do that, it should return [`false`], otherwise [`true`]. + /// + /// Returning [`false`] will cause the router to handle the external navigation failure. + #[allow(unused_variables)] + fn external(&mut self, url: String) -> bool { + false + } + + /// Provide the [`HistoryProvider`] with an update callback. + /// + /// Some [`HistoryProvider`]s may receive URL updates from outside the router. When such + /// updates are received, they should call `callback`, which will cause the router to update. + #[allow(unused_variables)] + fn updater(&mut self, callback: Arc) {} +} + +pub(crate) trait AnyHistoryProvider { + fn parse_route(&self, route: &str) -> Result, String>; + + #[must_use] + fn current_route(&self) -> Rc; + + #[must_use] + fn can_go_back(&self) -> bool { + true + } + + fn go_back(&mut self); + + #[must_use] + fn can_go_forward(&self) -> bool { + true + } + + fn go_forward(&mut self); + + fn push(&mut self, route: Rc); + + fn replace(&mut self, path: Rc); + + #[allow(unused_variables)] + fn external(&mut self, url: String) -> bool { + false + } + + #[allow(unused_variables)] + fn updater(&mut self, callback: Arc) {} + + #[cfg(feature = "liveview")] + fn is_liveview(&self) -> bool; +} + +pub(crate) struct AnyHistoryProviderImplWrapper { + inner: H, + _marker: std::marker::PhantomData, +} + +impl AnyHistoryProviderImplWrapper { + pub fn new(inner: H) -> Self { + Self { + inner, + _marker: std::marker::PhantomData, + } + } +} + +impl Default for AnyHistoryProviderImplWrapper { + fn default() -> Self { + Self::new(H::default()) + } +} + +impl AnyHistoryProvider for AnyHistoryProviderImplWrapper +where + R: Routable, + ::Err: std::fmt::Display, + H: HistoryProvider, +{ + fn parse_route(&self, route: &str) -> Result, String> { + R::from_str(route) + .map_err(|err| err.to_string()) + .map(|route| Rc::new(route) as Rc) + } + + fn current_route(&self) -> Rc { + let route = self.inner.current_route(); + Rc::new(route) + } + + fn can_go_back(&self) -> bool { + self.inner.can_go_back() + } + + fn go_back(&mut self) { + self.inner.go_back() + } + + fn can_go_forward(&self) -> bool { + self.inner.can_go_forward() + } + + fn go_forward(&mut self) { + self.inner.go_forward() + } + + fn push(&mut self, route: Rc) { + self.inner + .push(route.downcast::().unwrap().as_ref().clone()) + } + + fn replace(&mut self, route: Rc) { + self.inner + .replace(route.downcast::().unwrap().as_ref().clone()) + } + + fn external(&mut self, url: String) -> bool { + self.inner.external(url) + } + + fn updater(&mut self, callback: Arc) { + self.inner.updater(callback) + } + + #[cfg(feature = "liveview")] + fn is_liveview(&self) -> bool { + use std::any::TypeId; + + TypeId::of::() == TypeId::of::>() + } +} diff --git a/packages/web/src/history.rs b/packages/router/src/history/web.rs similarity index 88% rename from packages/web/src/history.rs rename to packages/router/src/history/web.rs index 57908448e4..443e7feaba 100644 --- a/packages/web/src/history.rs +++ b/packages/router/src/history/web.rs @@ -16,49 +16,6 @@ use super::{ HistoryProvider, }; -use gloo::console::error; -use wasm_bindgen::JsValue; -use web_sys::History; - -pub(crate) fn replace_state_with_url( - history: &History, - value: &[f64; 2], - url: Option<&str>, -) -> Result<(), JsValue> { - let position = js_sys::Array::new(); - position.push(&JsValue::from(value[0])); - position.push(&JsValue::from(value[1])); - - history.replace_state_with_url(&position, "", url) -} - -pub(crate) fn push_state_and_url( - history: &History, - value: &[f64; 2], - url: String, -) -> Result<(), JsValue> { - let position = js_sys::Array::new(); - position.push(&JsValue::from(value[0])); - position.push(&JsValue::from(value[1])); - - history.push_state_with_url(&position, "", Some(&url)) -} - -pub(crate) fn get_current(history: &History) -> Option<[f64; 2]> { - use wasm_bindgen::JsCast; - - let state = history.state(); - if let Err(err) = &state { - error!(err); - } - state.ok().and_then(|state| { - let state = state.dyn_into::().ok()?; - let x = state.get(0).as_f64()?; - let y = state.get(1).as_f64()?; - Some([x, y]) - }) -} - #[allow(dead_code)] fn base_path() -> Option { tracing::trace!( diff --git a/packages/web/src/history/web_hash.rs b/packages/router/src/history/web_hash.rs similarity index 100% rename from packages/web/src/history/web_hash.rs rename to packages/router/src/history/web_hash.rs diff --git a/packages/router/src/history/web_history.rs b/packages/router/src/history/web_history.rs new file mode 100644 index 0000000000..df52198743 --- /dev/null +++ b/packages/router/src/history/web_history.rs @@ -0,0 +1,42 @@ +use gloo::console::error; +use wasm_bindgen::JsValue; +use web_sys::History; + +pub(crate) fn replace_state_with_url( + history: &History, + value: &[f64; 2], + url: Option<&str>, +) -> Result<(), JsValue> { + let position = js_sys::Array::new(); + position.push(&JsValue::from(value[0])); + position.push(&JsValue::from(value[1])); + + history.replace_state_with_url(&position, "", url) +} + +pub(crate) fn push_state_and_url( + history: &History, + value: &[f64; 2], + url: String, +) -> Result<(), JsValue> { + let position = js_sys::Array::new(); + position.push(&JsValue::from(value[0])); + position.push(&JsValue::from(value[1])); + + history.push_state_with_url(&position, "", Some(&url)) +} + +pub(crate) fn get_current(history: &History) -> Option<[f64; 2]> { + use wasm_bindgen::JsCast; + + let state = history.state(); + if let Err(err) = &state { + error!(err); + } + state.ok().and_then(|state| { + let state = state.dyn_into::().ok()?; + let x = state.get(0).as_f64()?; + let y = state.get(1).as_f64()?; + Some([x, y]) + }) +} diff --git a/packages/web/src/history/web_scroll.rs b/packages/router/src/history/web_scroll.rs similarity index 100% rename from packages/web/src/history/web_scroll.rs rename to packages/router/src/history/web_scroll.rs diff --git a/packages/router/src/hooks/use_route.rs b/packages/router/src/hooks/use_route.rs index 4fcf1a8af5..3df71c3c07 100644 --- a/packages/router/src/hooks/use_route.rs +++ b/packages/router/src/hooks/use_route.rs @@ -42,7 +42,7 @@ use crate::utils::use_router_internal::use_router_internal; /// ``` #[doc(alias = "use_url")] #[must_use] -pub fn use_route() -> R { +pub fn use_route() -> R { match use_router_internal() { Some(r) => r.current(), None => { diff --git a/packages/router/src/into_routable.rs b/packages/router/src/into_routable.rs deleted file mode 100644 index 5f063ddf4e..0000000000 --- a/packages/router/src/into_routable.rs +++ /dev/null @@ -1,76 +0,0 @@ -use url::Url; - -use crate::prelude::Routable; - -/// Something that can be converted into a [`NavigationTarget`]. -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct IntoRoutable(String); - -impl IntoRoutable { - fn FromStr(value: String) -> Self { - Self(value) - } - - fn Route(value: R) -> Self { - Self(value.serialize()) - } - - pub(crate) fn is_external(&self) -> bool { - todo!() - } -} - -impl From for IntoRoutable { - fn from(value: R) -> Self { - IntoRoutable::Route(value) - } -} - -// impl From> for IntoRoutable { -// fn from(value: NavigationTarget) -> Self { -// match value { -// NavigationTarget::Internal(route) => IntoRoutable::Route(Rc::new(route) as Rc), -// NavigationTarget::External(url) => IntoRoutable::FromStr(url), -// } -// } -// } - -// impl PartialEq for IntoRoutable { -// fn eq(&self, other: &Self) -> bool { -// match (self, other) { -// (IntoRoutable::FromStr(a), IntoRoutable::FromStr(b)) => a == b, -// (IntoRoutable::Route(a), IntoRoutable::Route(b)) => Rc::ptr_eq(a, b), -// _ => false, -// } -// } -// } - -impl From for IntoRoutable { - fn from(value: String) -> Self { - IntoRoutable::FromStr(value) - } -} - -impl From<&String> for IntoRoutable { - fn from(value: &String) -> Self { - IntoRoutable::FromStr(value.to_string()) - } -} - -impl From<&str> for IntoRoutable { - fn from(value: &str) -> Self { - IntoRoutable::FromStr(value.to_string()) - } -} - -impl From for IntoRoutable { - fn from(url: Url) -> Self { - IntoRoutable::FromStr(url.to_string()) - } -} - -impl From<&Url> for IntoRoutable { - fn from(url: &Url) -> Self { - IntoRoutable::FromStr(url.to_string()) - } -} diff --git a/packages/router/src/lib.rs b/packages/router/src/lib.rs index dc077a99e1..47cf12d918 100644 --- a/packages/router/src/lib.rs +++ b/packages/router/src/lib.rs @@ -2,22 +2,37 @@ #![doc(html_logo_url = "https://avatars.githubusercontent.com/u/79236386")] #![doc(html_favicon_url = "https://avatars.githubusercontent.com/u/79236386")] // cannot use forbid, because props derive macro generates #[allow(missing_docs)] -// #![deny(missing_docs)] +#![deny(missing_docs)] #![allow(non_snake_case)] -pub mod components; -pub mod into_routable; pub mod navigation; pub mod routable; +/// Components interacting with the router. +pub mod components { + mod default_errors; + pub use default_errors::*; + + mod history_buttons; + pub use history_buttons::*; + + mod link; + pub use link::*; + + mod outlet; + pub use outlet::*; + + mod router; + pub use router::*; +} + mod contexts { - pub(crate) mod generic_router; pub(crate) mod navigator; pub(crate) mod outlet; pub(crate) mod router; pub use navigator::*; pub(crate) use router::*; - pub use router::*; + pub use router::{root_router, RouterContext}; } mod router_cfg; @@ -44,7 +59,6 @@ pub mod prelude { pub use crate::contexts::*; pub use crate::history::*; pub use crate::hooks::*; - pub use crate::into_routable::*; pub use crate::navigation::*; pub use crate::routable::*; pub use crate::router_cfg::RouterConfig; diff --git a/packages/router/src/navigation.rs b/packages/router/src/navigation.rs index bf902dafb4..3c8a8e28de 100644 --- a/packages/router/src/navigation.rs +++ b/packages/router/src/navigation.rs @@ -11,7 +11,7 @@ use crate::routable::Routable; /// A target for the router to navigate to. #[derive(Clone, PartialEq, Eq, Debug)] -pub enum NavigationTarget { +pub enum NavigationTarget { /// An internal path that the router can navigate to by itself. /// /// ```rust @@ -32,7 +32,6 @@ pub enum NavigationTarget { /// assert_eq!(explicit, implicit); /// ``` Internal(R), - /// An external target that the router doesn't control. /// /// ```rust @@ -57,8 +56,8 @@ pub enum NavigationTarget { impl From<&str> for NavigationTarget { fn from(value: &str) -> Self { - R::deserialize(value) - .map(|route| Self::Internal(route)) + value + .parse() .unwrap_or_else(|_| Self::External(value.to_string())) } } @@ -84,44 +83,43 @@ impl From for NavigationTarget { impl Display for NavigationTarget { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - NavigationTarget::Internal(r) => write!(f, "{}", r.serialize()), + NavigationTarget::Internal(r) => write!(f, "{}", r), NavigationTarget::External(s) => write!(f, "{}", s), } } } -// /// An error that can occur when parsing a [`NavigationTarget`]. -// pub enum NavigationTargetParseError { -// /// A URL that is not valid. -// InvalidUrl(ParseError), - -// /// An internal URL that is not valid. -// InvalidInternalURL(::Err), -// } +/// An error that can occur when parsing a [`NavigationTarget`]. +pub enum NavigationTargetParseError { + /// A URL that is not valid. + InvalidUrl(ParseError), + /// An internal URL that is not valid. + InvalidInternalURL(::Err), +} -// impl Debug for NavigationTargetParseError { -// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { -// match self { -// NavigationTargetParseError::InvalidUrl(e) => write!(f, "Invalid URL: {}", e), -// NavigationTargetParseError::InvalidInternalURL(_) => { -// write!(f, "Invalid internal URL") -// } -// } -// } -// } +impl Debug for NavigationTargetParseError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + NavigationTargetParseError::InvalidUrl(e) => write!(f, "Invalid URL: {}", e), + NavigationTargetParseError::InvalidInternalURL(_) => { + write!(f, "Invalid internal URL") + } + } + } +} -// impl FromStr for NavigationTarget { -// type Err = NavigationTargetParseError; +impl FromStr for NavigationTarget { + type Err = NavigationTargetParseError; -// fn from_str(s: &str) -> Result { -// match Url::parse(s) { -// Ok(_) => Ok(Self::External(s.to_string())), -// Err(ParseError::RelativeUrlWithoutBase) => { -// Ok(Self::Internal(R::from_str(s).map_err(|e| { -// NavigationTargetParseError::InvalidInternalURL(e) -// })?)) -// } -// Err(e) => Err(NavigationTargetParseError::InvalidUrl(e)), -// } -// } -// } + fn from_str(s: &str) -> Result { + match Url::parse(s) { + Ok(_) => Ok(Self::External(s.to_string())), + Err(ParseError::RelativeUrlWithoutBase) => { + Ok(Self::Internal(R::from_str(s).map_err(|e| { + NavigationTargetParseError::InvalidInternalURL(e) + })?)) + } + Err(e) => Err(NavigationTargetParseError::InvalidUrl(e)), + } + } +} diff --git a/packages/router/src/routable.rs b/packages/router/src/routable.rs index be44a118b5..fc7acc86cf 100644 --- a/packages/router/src/routable.rs +++ b/packages/router/src/routable.rs @@ -3,18 +3,520 @@ #![allow(non_snake_case)] use dioxus_lib::prelude::*; +use std::iter::FlatMap; use std::slice::Iter; -use std::{fmt::Debug, iter::FlatMap}; use std::{fmt::Display, str::FromStr}; -mod hash; -mod query; -mod segments; -mod sitemap; -pub use hash::*; -pub use query::*; -pub use segments::*; -pub use sitemap::*; +/// An error that occurs when parsing a route. +#[derive(Debug, PartialEq)] +pub struct RouteParseError { + /// The attempted routes that failed to match. + pub attempted_routes: Vec, +} + +impl Display for RouteParseError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Route did not match:\nAttempted Matches:\n")?; + for (i, route) in self.attempted_routes.iter().enumerate() { + writeln!(f, "{}) {route}", i + 1)?; + } + Ok(()) + } +} + +/// Something that can be created from an entire query string. This trait must be implemented for any type that is spread into the query segment like `#[route("/?:..query")]`. +/// +/// +/// **This trait is automatically implemented for any types that implement `From<&str>`.** +/// +/// ```rust +/// use dioxus::prelude::*; +/// +/// #[derive(Routable, Clone, PartialEq, Debug)] +/// enum Route { +/// // FromQuery must be implemented for any types you spread into the query segment +/// #[route("/?:..query")] +/// Home { +/// query: CustomQuery +/// }, +/// } +/// +/// #[derive(Default, Clone, PartialEq, Debug)] +/// struct CustomQuery { +/// count: i32, +/// } +/// +/// // We implement From<&str> for CustomQuery so that FromQuery is implemented automatically +/// impl From<&str> for CustomQuery { +/// fn from(query: &str) -> Self { +/// CustomQuery { +/// count: query.parse().unwrap_or(0), +/// } +/// } +/// } +/// +/// // We also need to implement Display for CustomQuery which will be used to format the query string into the URL +/// impl std::fmt::Display for CustomQuery { +/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +/// write!(f, "{}", self.count) +/// } +/// } +/// +/// # #[component] +/// # fn Home(query: CustomQuery) -> Element { +/// # unimplemented!() +/// # } +/// ``` +#[rustversion::attr( + since(1.78.0), + diagnostic::on_unimplemented( + message = "`FromQuery` is not implemented for `{Self}`", + label = "spread query", + note = "FromQuery is automatically implemented for types that implement `From<&str>`. You need to either implement From<&str> or implement FromQuery manually." + ) +)] +pub trait FromQuery { + /// Create an instance of `Self` from a query string. + fn from_query(query: &str) -> Self; +} + +impl From<&'a str>> FromQuery for T { + fn from_query(query: &str) -> Self { + T::from(query) + } +} + +/// Something that can be created from a query argument. This trait must be implemented for any type that is used as a query argument like `#[route("/?:query")]`. +/// +/// **This trait is automatically implemented for any types that implement `FromStr` and `Default`.** +/// +/// ```rust +/// use dioxus::prelude::*; +/// +/// #[derive(Routable, Clone, PartialEq, Debug)] +/// enum Route { +/// // FromQuerySegment must be implemented for any types you use in the query segment +/// // When you don't spread the query, you can parse multiple values form the query +/// // This url will be in the format `/?query=123&other=456` +/// #[route("/?:query&:other")] +/// Home { +/// query: CustomQuery, +/// other: i32, +/// }, +/// } +/// +/// // We can derive Default for CustomQuery +/// // If the router fails to parse the query value, it will use the default value instead +/// #[derive(Default, Clone, PartialEq, Debug)] +/// struct CustomQuery { +/// count: i32, +/// } +/// +/// // We implement FromStr for CustomQuery so that FromQuerySegment is implemented automatically +/// impl std::str::FromStr for CustomQuery { +/// type Err = ::Err; +/// +/// fn from_str(query: &str) -> Result { +/// Ok(CustomQuery { +/// count: query.parse()?, +/// }) +/// } +/// } +/// +/// // We also need to implement Display for CustomQuery which will be used to format the query string into the URL +/// impl std::fmt::Display for CustomQuery { +/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +/// write!(f, "{}", self.count) +/// } +/// } +/// +/// # #[component] +/// # fn Home(query: CustomQuery, other: i32) -> Element { +/// # unimplemented!() +/// # } +/// ``` +#[rustversion::attr( + since(1.78.0), + diagnostic::on_unimplemented( + message = "`FromQueryArgument` is not implemented for `{Self}`", + label = "query argument", + note = "FromQueryArgument is automatically implemented for types that implement `FromStr` and `Default`. You need to either implement FromStr and Default or implement FromQueryArgument manually." + ) +)] +pub trait FromQueryArgument: Default { + /// The error that can occur when parsing a query argument. + type Err; + + /// Create an instance of `Self` from a query string. + fn from_query_argument(argument: &str) -> Result; +} + +impl FromQueryArgument for T +where + ::Err: Display, +{ + type Err = ::Err; + + fn from_query_argument(argument: &str) -> Result { + match T::from_str(argument) { + Ok(result) => Ok(result), + Err(err) => { + tracing::error!("Failed to parse query argument: {}", err); + Err(err) + } + } + } +} + +/// Something that can be created from an entire hash fragment. This must be implemented for any type that is used as a hash fragment like `#[route("/#:hash_fragment")]`. +/// +/// +/// **This trait is automatically implemented for any types that implement `FromStr` and `Default`.** +/// +/// # Example +/// +/// ```rust +/// use dioxus::prelude::*; +/// +/// #[derive(Routable, Clone)] +/// #[rustfmt::skip] +/// enum Route { +/// // State is stored in the url hash +/// #[route("/#:url_hash")] +/// Home { +/// url_hash: State, +/// }, +/// } +/// +/// #[component] +/// fn Home(url_hash: State) -> Element { +/// unimplemented!() +/// } +/// +/// +/// #[derive(Clone, PartialEq, Default)] +/// struct State { +/// count: usize, +/// other_count: usize +/// } +/// +/// // The hash segment will be displayed as a string (this will be url encoded automatically) +/// impl std::fmt::Display for State { +/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +/// write!(f, "{}-{}", self.count, self.other_count) +/// } +/// } +/// +/// // We need to parse the hash fragment into a struct from the string (this will be url decoded automatically) +/// impl FromHashFragment for State { +/// fn from_hash_fragment(hash: &str) -> Self { +/// let Some((first, second)) = hash.split_once('-') else { +/// // URL fragment parsing shouldn't fail. You can return a default value if you want +/// return Default::default(); +/// }; +/// +/// let first = first.parse().unwrap(); +/// let second = second.parse().unwrap(); +/// +/// State { +/// count: first, +/// other_count: second, +/// } +/// } +/// } +/// ``` +#[rustversion::attr( + since(1.78.0), + diagnostic::on_unimplemented( + message = "`FromHashFragment` is not implemented for `{Self}`", + label = "hash fragment", + note = "FromHashFragment is automatically implemented for types that implement `FromStr` and `Default`. You need to either implement FromStr and Default or implement FromHashFragment manually." + ) +)] +pub trait FromHashFragment { + /// Create an instance of `Self` from a hash fragment. + fn from_hash_fragment(hash: &str) -> Self; +} + +impl FromHashFragment for T +where + T: FromStr + Default, + T::Err: std::fmt::Display, +{ + fn from_hash_fragment(hash: &str) -> Self { + match T::from_str(hash) { + Ok(value) => value, + Err(err) => { + tracing::error!("Failed to parse hash fragment: {}", err); + Default::default() + } + } + } +} + +/// Something that can be created from a single route segment. This must be implemented for any type that is used as a route segment like `#[route("/:route_segment")]`. +/// +/// +/// **This trait is automatically implemented for any types that implement `FromStr` and `Default`.** +/// +/// ```rust +/// use dioxus::prelude::*; +/// +/// #[derive(Routable, Clone, PartialEq, Debug)] +/// enum Route { +/// // FromRouteSegment must be implemented for any types you use in the route segment +/// // When you don't spread the route, you can parse multiple values from the route +/// // This url will be in the format `/123/456` +/// #[route("/:route_segment_one/:route_segment_two")] +/// Home { +/// route_segment_one: CustomRouteSegment, +/// route_segment_two: i32, +/// }, +/// } +/// +/// // We can derive Default for CustomRouteSegment +/// // If the router fails to parse the route segment, it will use the default value instead +/// #[derive(Default, PartialEq, Clone, Debug)] +/// struct CustomRouteSegment { +/// count: i32, +/// } +/// +/// // We implement FromStr for CustomRouteSegment so that FromRouteSegment is implemented automatically +/// impl std::str::FromStr for CustomRouteSegment { +/// type Err = ::Err; +/// +/// fn from_str(route_segment: &str) -> Result { +/// Ok(CustomRouteSegment { +/// count: route_segment.parse()?, +/// }) +/// } +/// } +/// +/// // We also need to implement Display for CustomRouteSegment which will be used to format the route segment into the URL +/// impl std::fmt::Display for CustomRouteSegment { +/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +/// write!(f, "{}", self.count) +/// } +/// } +/// +/// # #[component] +/// # fn Home(route_segment_one: CustomRouteSegment, route_segment_two: i32) -> Element { +/// # unimplemented!() +/// # } +/// ``` +#[rustversion::attr( + since(1.78.0), + diagnostic::on_unimplemented( + message = "`FromRouteSegment` is not implemented for `{Self}`", + label = "route segment", + note = "FromRouteSegment is automatically implemented for types that implement `FromStr` and `Default`. You need to either implement FromStr and Default or implement FromRouteSegment manually." + ) +)] +pub trait FromRouteSegment: Sized { + /// The error that can occur when parsing a route segment. + type Err; + + /// Create an instance of `Self` from a route segment. + fn from_route_segment(route: &str) -> Result; +} + +impl FromRouteSegment for T +where + ::Err: Display, +{ + type Err = ::Err; + + fn from_route_segment(route: &str) -> Result { + T::from_str(route) + } +} + +#[test] +fn full_circle() { + let route = "testing 1234 hello world"; + assert_eq!(String::from_route_segment(route).unwrap(), route); +} + +/// Something that can be converted into multiple route segments. This must be implemented for any type that is spread into the route segment like `#[route("/:..route_segments")]`. +/// +/// +/// **This trait is automatically implemented for any types that implement `IntoIterator`.** +/// +/// ```rust +/// use dioxus::prelude::*; +/// +/// #[derive(Routable, Clone, PartialEq, Debug)] +/// enum Route { +/// // FromRouteSegments must be implemented for any types you use in the route segment +/// // When you spread the route, you can parse multiple values from the route +/// // This url will be in the format `/123/456/789` +/// #[route("/:..numeric_route_segments")] +/// Home { +/// numeric_route_segments: NumericRouteSegments, +/// }, +/// } +/// +/// // We can derive Default for NumericRouteSegments +/// // If the router fails to parse the route segment, it will use the default value instead +/// #[derive(Default, PartialEq, Clone, Debug)] +/// struct NumericRouteSegments { +/// numbers: Vec, +/// } +/// +/// // Implement ToRouteSegments for NumericRouteSegments so that we can display the route segments +/// impl ToRouteSegments for NumericRouteSegments { +/// fn display_route_segments(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +/// for number in &self.numbers { +/// write!(f, "/{}", number)?; +/// } +/// Ok(()) +/// } +/// } +/// +/// // We also need to parse the route segments with `FromRouteSegments` +/// impl FromRouteSegments for NumericRouteSegments { +/// type Err = ::Err; +/// +/// fn from_route_segments(segments: &[&str]) -> Result { +/// let mut numbers = Vec::new(); +/// for segment in segments { +/// numbers.push(segment.parse()?); +/// } +/// Ok(NumericRouteSegments { numbers }) +/// } +/// } +/// +/// # #[component] +/// # fn Home(numeric_route_segments: NumericRouteSegments) -> Element { +/// # unimplemented!() +/// # } +/// ``` +pub trait ToRouteSegments { + /// Display the route segments with each route segment separated by a `/`. This should not start with a `/`. + /// + fn display_route_segments(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result; +} + +// Implement ToRouteSegments for any type that can turn &self into an iterator of &T where T: Display +impl ToRouteSegments for I +where + for<'a> &'a I: IntoIterator, +{ + fn display_route_segments(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for segment in self { + write!(f, "/")?; + let segment = segment.to_string(); + let encoded = urlencoding::encode(&segment); + write!(f, "{}", encoded)?; + } + Ok(()) + } +} + +#[test] +fn to_route_segments() { + struct DisplaysRoute; + + impl Display for DisplaysRoute { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let segments = vec!["hello", "world"]; + segments.display_route_segments(f) + } + } + + assert_eq!(DisplaysRoute.to_string(), "/hello/world"); +} + +/// Something that can be created from multiple route segments. This must be implemented for any type that is spread into the route segment like `#[route("/:..route_segments")]`. +/// +/// +/// **This trait is automatically implemented for any types that implement `FromIterator`.** +/// +/// ```rust +/// use dioxus::prelude::*; +/// +/// #[derive(Routable, Clone, PartialEq, Debug)] +/// enum Route { +/// // FromRouteSegments must be implemented for any types you use in the route segment +/// // When you spread the route, you can parse multiple values from the route +/// // This url will be in the format `/123/456/789` +/// #[route("/:..numeric_route_segments")] +/// Home { +/// numeric_route_segments: NumericRouteSegments, +/// }, +/// } +/// +/// // We can derive Default for NumericRouteSegments +/// // If the router fails to parse the route segment, it will use the default value instead +/// #[derive(Default, Clone, PartialEq, Debug)] +/// struct NumericRouteSegments { +/// numbers: Vec, +/// } +/// +/// // Implement ToRouteSegments for NumericRouteSegments so that we can display the route segments +/// impl ToRouteSegments for NumericRouteSegments { +/// fn display_route_segments(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +/// for number in &self.numbers { +/// write!(f, "/{}", number)?; +/// } +/// Ok(()) +/// } +/// } +/// +/// // We also need to parse the route segments with `FromRouteSegments` +/// impl FromRouteSegments for NumericRouteSegments { +/// type Err = ::Err; +/// +/// fn from_route_segments(segments: &[&str]) -> Result { +/// let mut numbers = Vec::new(); +/// for segment in segments { +/// numbers.push(segment.parse()?); +/// } +/// Ok(NumericRouteSegments { numbers }) +/// } +/// } +/// +/// # #[component] +/// # fn Home(numeric_route_segments: NumericRouteSegments) -> Element { +/// # unimplemented!() +/// # } +/// ``` +#[rustversion::attr( + since(1.78.0), + diagnostic::on_unimplemented( + message = "`FromRouteSegments` is not implemented for `{Self}`", + label = "spread route segments", + note = "FromRouteSegments is automatically implemented for types that implement `FromIterator` with an `Item` type that implements `Display`. You need to either implement FromIterator or implement FromRouteSegments manually." + ) +)] +pub trait FromRouteSegments: Sized { + /// The error that can occur when parsing route segments. + type Err: std::fmt::Display; + + /// Create an instance of `Self` from route segments. + /// + /// NOTE: This method must parse the output of `ToRouteSegments::display_route_segments` into the type `Self`. + fn from_route_segments(segments: &[&str]) -> Result; +} + +impl> FromRouteSegments for I { + type Err = ::Err; + + fn from_route_segments(segments: &[&str]) -> Result { + segments + .iter() + .map(|s| String::from_route_segment(s)) + .collect() + } +} + +/// A flattened version of [`Routable::SITE_MAP`]. +/// This essentially represents a `Vec>`, which you can collect it into. +type SiteMapFlattened<'a> = FlatMap< + Iter<'a, SiteMapSegment>, + Vec>, + fn(&SiteMapSegment) -> Vec>, +>; /// The Routable trait is implemented for types that can be converted to and from a route and be rendered as a page. /// @@ -41,15 +543,12 @@ pub use sitemap::*; /// // Each enum has an associated url /// #[route("/")] /// Home {}, -/// /// // Routes can include dynamic segments that are parsed from the url /// #[route("/blog/:blog_id")] /// Blog { blog_id: usize }, -/// /// // Or query segments that are parsed from the url /// #[route("/edit?:blog_id")] /// Edit { blog_id: usize }, -/// /// // Or hash segments that are parsed from the url /// #[route("/hashtag/#:hash")] /// Hash { hash: String }, @@ -93,24 +592,14 @@ pub use sitemap::*; note = "Routable should generally be derived using the `#[derive(Routable)]` macro." ) )] -pub trait Routable: Clone + Sized + 'static { +pub trait Routable: FromStr + Display + Clone + 'static { /// The error that can occur when parsing a route. const SITE_MAP: &'static [SiteMapSegment]; /// Render the route at the given level fn render(&self, level: usize) -> Element; - /// Turn this route into a string so we can parse it again later - /// - /// The format here is expected to be a Path like `/about/123` or `https://example.com/about` - fn serialize(&self) -> String; - - /// Turn a string into this route, or return an error if it can't be parsed - /// - /// The format here is expected to be a Path like `/about/123` or `https://example.com/about` - fn deserialize(route: &str) -> Result>; - - /// Get the parent route of this route. + /// Checks if this route is a child of the given route. /// /// # Example /// ```rust @@ -124,33 +613,35 @@ pub trait Routable: Clone + Sized + 'static { /// /// #[derive(Routable, Clone, PartialEq, Debug)] /// enum Route { - /// #[route("/home")] + /// #[route("/")] /// Home {}, - /// #[route("/home/about")] + /// #[route("/about")] /// About {}, /// } /// /// let route = Route::About {}; - /// let parent = route.parent().unwrap(); - /// assert_eq!(parent, Route::Home {}); + /// let parent = Route::Home {}; + /// assert!(route.is_child_of(&parent)); /// ``` - fn parent(&self) -> Option { - let as_str = self.serialize(); - let as_str = as_str.trim_matches('/'); - let segments = as_str.split('/'); - let segment_count = segments.clone().count(); - let new_route = segments - .take(segment_count - 1) - .fold(String::new(), |mut acc, segment| { - acc.push('/'); - acc.push_str(segment); - acc - }); - - Self::deserialize(&new_route).ok() + fn is_child_of(&self, other: &Self) -> bool { + let self_str = self.to_string(); + let self_str = self_str.trim_matches('/'); + let other_str = other.to_string(); + let other_str = other_str.trim_matches('/'); + if other_str.is_empty() { + return true; + } + let self_segments = self_str.split('/'); + let other_segments = other_str.split('/'); + for (self_seg, other_seg) in self_segments.zip(other_segments) { + if self_seg != other_seg { + return false; + } + } + true } - /// Checks if this route is a child of the given route. + /// Get the parent route of this route. /// /// # Example /// ```rust @@ -164,32 +655,30 @@ pub trait Routable: Clone + Sized + 'static { /// /// #[derive(Routable, Clone, PartialEq, Debug)] /// enum Route { - /// #[route("/")] + /// #[route("/home")] /// Home {}, - /// #[route("/about")] + /// #[route("/home/about")] /// About {}, /// } /// /// let route = Route::About {}; - /// let parent = Route::Home {}; - /// assert!(route.is_child_of(&parent)); + /// let parent = route.parent().unwrap(); + /// assert_eq!(parent, Route::Home {}); /// ``` - fn is_child_of(&self, other: &Self) -> bool { - let self_str = self.serialize(); - let self_str = self_str.trim_matches('/'); - let other_str = other.serialize(); - let other_str = other_str.trim_matches('/'); - if other_str.is_empty() { - return true; - } - let self_segments = self_str.split('/'); - let other_segments = other_str.split('/'); - for (self_seg, other_seg) in self_segments.zip(other_segments) { - if self_seg != other_seg { - return false; - } - } - true + fn parent(&self) -> Option { + let as_str = self.to_string(); + let as_str = as_str.trim_matches('/'); + let segments = as_str.split('/'); + let segment_count = segments.clone().count(); + let new_route = segments + .take(segment_count - 1) + .fold(String::new(), |mut acc, segment| { + acc.push('/'); + acc.push_str(segment); + acc + }); + + Self::from_str(&new_route).ok() } /// Returns a flattened version of [`Self::SITE_MAP`]. @@ -214,122 +703,12 @@ pub trait Routable: Clone + Sized + 'static { } } - Self::deserialize(&route).ok() + route.parse().ok() }) .collect() } } -/// An error that occurs when parsing a route. -#[derive(Debug, PartialEq)] -pub struct RouteParseError { - /// The attempted routes that failed to match. - pub attempted_routes: Vec, -} - -impl std::error::Error for RouteParseError {} - -impl Display for RouteParseError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Route did not match:\nAttempted Matches:\n")?; - for (i, route) in self.attempted_routes.iter().enumerate() { - writeln!(f, "{}) {route}", i + 1)?; - } - Ok(()) - } -} - -/// Something that can be created from multiple route segments. This must be implemented for any type that is spread into the route segment like `#[route("/:..route_segments")]`. -/// -/// -/// **This trait is automatically implemented for any types that implement `FromIterator`.** -/// -/// ```rust -/// use dioxus::prelude::*; -/// -/// #[derive(Routable, Clone, PartialEq, Debug)] -/// enum Route { -/// // FromRouteSegments must be implemented for any types you use in the route segment -/// // When you spread the route, you can parse multiple values from the route -/// // This url will be in the format `/123/456/789` -/// #[route("/:..numeric_route_segments")] -/// Home { -/// numeric_route_segments: NumericRouteSegments, -/// }, -/// } -/// -/// // We can derive Default for NumericRouteSegments -/// // If the router fails to parse the route segment, it will use the default value instead -/// #[derive(Default, Clone, PartialEq, Debug)] -/// struct NumericRouteSegments { -/// numbers: Vec, -/// } -/// -/// // Implement ToRouteSegments for NumericRouteSegments so that we can display the route segments -/// impl ToRouteSegments for NumericRouteSegments { -/// fn display_route_segments(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { -/// for number in &self.numbers { -/// write!(f, "/{}", number)?; -/// } -/// Ok(()) -/// } -/// } -/// -/// // We also need to parse the route segments with `FromRouteSegments` -/// impl FromRouteSegments for NumericRouteSegments { -/// type Err = ::Err; -/// -/// fn from_route_segments(segments: &[&str]) -> Result { -/// let mut numbers = Vec::new(); -/// for segment in segments { -/// numbers.push(segment.parse()?); -/// } -/// Ok(NumericRouteSegments { numbers }) -/// } -/// } -/// -/// # #[component] -/// # fn Home(numeric_route_segments: NumericRouteSegments) -> Element { -/// # unimplemented!() -/// # } -/// ``` -#[rustversion::attr( - since(1.78.0), - diagnostic::on_unimplemented( - message = "`FromRouteSegments` is not implemented for `{Self}`", - label = "spread route segments", - note = "FromRouteSegments is automatically implemented for types that implement `FromIterator` with an `Item` type that implements `Display`. You need to either implement FromIterator or implement FromRouteSegments manually." - ) -)] -pub trait FromRouteSegments: Sized { - /// The error that can occur when parsing route segments. - type Err: std::fmt::Display; - - /// Create an instance of `Self` from route segments. - /// - /// NOTE: This method must parse the output of `ToRouteSegments::display_route_segments` into the type `Self`. - fn from_route_segments(segments: &[&str]) -> Result; -} - -impl> FromRouteSegments for I { - type Err = ::Err; - - fn from_route_segments(segments: &[&str]) -> Result { - segments - .iter() - .map(|s| String::from_route_segment(s)) - .collect() - } -} - -/// A flattened version of [`Routable::SITE_MAP`]. -/// This essentially represents a `Vec>`, which you can collect it into. -type SiteMapFlattened<'a> = FlatMap< - Iter<'a, SiteMapSegment>, - Vec>, - fn(&SiteMapSegment) -> Vec>, ->; - /// A type erased map of the site structure. #[derive(Debug, Clone, PartialEq)] pub struct SiteMapSegment { diff --git a/packages/router/src/routable/hash.rs b/packages/router/src/routable/hash.rs deleted file mode 100644 index b4079fa194..0000000000 --- a/packages/router/src/routable/hash.rs +++ /dev/null @@ -1,89 +0,0 @@ -use std::iter::FlatMap; -use std::slice::Iter; -use std::{fmt::Display, str::FromStr}; - -/// Something that can be created from an entire hash fragment. This must be implemented for any type that is used as a hash fragment like `#[route("/#:hash_fragment")]`. -/// -/// -/// **This trait is automatically implemented for any types that implement `FromStr` and `Default`.** -/// -/// # Example -/// -/// ```rust -/// use dioxus::prelude::*; -/// -/// #[derive(Routable, Clone)] -/// #[rustfmt::skip] -/// enum Route { -/// // State is stored in the url hash -/// #[route("/#:url_hash")] -/// Home { -/// url_hash: State, -/// }, -/// } -/// -/// #[component] -/// fn Home(url_hash: State) -> Element { -/// unimplemented!() -/// } -/// -/// -/// #[derive(Clone, PartialEq, Default)] -/// struct State { -/// count: usize, -/// other_count: usize -/// } -/// -/// // The hash segment will be displayed as a string (this will be url encoded automatically) -/// impl std::fmt::Display for State { -/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { -/// write!(f, "{}-{}", self.count, self.other_count) -/// } -/// } -/// -/// // We need to parse the hash fragment into a struct from the string (this will be url decoded automatically) -/// impl FromHashFragment for State { -/// fn from_hash_fragment(hash: &str) -> Self { -/// let Some((first, second)) = hash.split_once('-') else { -/// // URL fragment parsing shouldn't fail. You can return a default value if you want -/// return Default::default(); -/// }; -/// -/// let first = first.parse().unwrap(); -/// let second = second.parse().unwrap(); -/// -/// State { -/// count: first, -/// other_count: second, -/// } -/// } -/// } -/// ``` -#[rustversion::attr( - since(1.78.0), - diagnostic::on_unimplemented( - message = "`FromHashFragment` is not implemented for `{Self}`", - label = "hash fragment", - note = "FromHashFragment is automatically implemented for types that implement `FromStr` and `Default`. You need to either implement FromStr and Default or implement FromHashFragment manually." - ) -)] -pub trait FromHashFragment { - /// Create an instance of `Self` from a hash fragment. - fn from_hash_fragment(hash: &str) -> Self; -} - -impl FromHashFragment for T -where - T: FromStr + Default, - T::Err: std::fmt::Display, -{ - fn from_hash_fragment(hash: &str) -> Self { - match T::from_str(hash) { - Ok(value) => value, - Err(err) => { - tracing::error!("Failed to parse hash fragment: {}", err); - Default::default() - } - } - } -} diff --git a/packages/router/src/routable/query.rs b/packages/router/src/routable/query.rs deleted file mode 100644 index 44f874667d..0000000000 --- a/packages/router/src/routable/query.rs +++ /dev/null @@ -1,147 +0,0 @@ -use std::iter::FlatMap; -use std::slice::Iter; -use std::{fmt::Display, str::FromStr}; - -/// Something that can be created from an entire query string. This trait must be implemented for any type that is spread into the query segment like `#[route("/?:..query")]`. -/// -/// -/// **This trait is automatically implemented for any types that implement `From<&str>`.** -/// -/// ```rust -/// use dioxus::prelude::*; -/// -/// #[derive(Routable, Clone, PartialEq, Debug)] -/// enum Route { -/// // FromQuery must be implemented for any types you spread into the query segment -/// #[route("/?:..query")] -/// Home { -/// query: CustomQuery -/// }, -/// } -/// -/// #[derive(Default, Clone, PartialEq, Debug)] -/// struct CustomQuery { -/// count: i32, -/// } -/// -/// // We implement From<&str> for CustomQuery so that FromQuery is implemented automatically -/// impl From<&str> for CustomQuery { -/// fn from(query: &str) -> Self { -/// CustomQuery { -/// count: query.parse().unwrap_or(0), -/// } -/// } -/// } -/// -/// // We also need to implement Display for CustomQuery which will be used to format the query string into the URL -/// impl std::fmt::Display for CustomQuery { -/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { -/// write!(f, "{}", self.count) -/// } -/// } -/// -/// # #[component] -/// # fn Home(query: CustomQuery) -> Element { -/// # unimplemented!() -/// # } -/// ``` -#[rustversion::attr( - since(1.78.0), - diagnostic::on_unimplemented( - message = "`FromQuery` is not implemented for `{Self}`", - label = "spread query", - note = "FromQuery is automatically implemented for types that implement `From<&str>`. You need to either implement From<&str> or implement FromQuery manually." - ) -)] -pub trait FromQuery { - /// Create an instance of `Self` from a query string. - fn from_query(query: &str) -> Self; -} - -impl From<&'a str>> FromQuery for T { - fn from_query(query: &str) -> Self { - T::from(query) - } -} - -/// Something that can be created from a query argument. This trait must be implemented for any type that is used as a query argument like `#[route("/?:query")]`. -/// -/// **This trait is automatically implemented for any types that implement `FromStr` and `Default`.** -/// -/// ```rust -/// use dioxus::prelude::*; -/// -/// #[derive(Routable, Clone, PartialEq, Debug)] -/// enum Route { -/// // FromQuerySegment must be implemented for any types you use in the query segment -/// // When you don't spread the query, you can parse multiple values form the query -/// // This url will be in the format `/?query=123&other=456` -/// #[route("/?:query&:other")] -/// Home { -/// query: CustomQuery, -/// other: i32, -/// }, -/// } -/// -/// // We can derive Default for CustomQuery -/// // If the router fails to parse the query value, it will use the default value instead -/// #[derive(Default, Clone, PartialEq, Debug)] -/// struct CustomQuery { -/// count: i32, -/// } -/// -/// // We implement FromStr for CustomQuery so that FromQuerySegment is implemented automatically -/// impl std::str::FromStr for CustomQuery { -/// type Err = ::Err; -/// -/// fn from_str(query: &str) -> Result { -/// Ok(CustomQuery { -/// count: query.parse()?, -/// }) -/// } -/// } -/// -/// // We also need to implement Display for CustomQuery which will be used to format the query string into the URL -/// impl std::fmt::Display for CustomQuery { -/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { -/// write!(f, "{}", self.count) -/// } -/// } -/// -/// # #[component] -/// # fn Home(query: CustomQuery, other: i32) -> Element { -/// # unimplemented!() -/// # } -/// ``` -#[rustversion::attr( - since(1.78.0), - diagnostic::on_unimplemented( - message = "`FromQueryArgument` is not implemented for `{Self}`", - label = "query argument", - note = "FromQueryArgument is automatically implemented for types that implement `FromStr` and `Default`. You need to either implement FromStr and Default or implement FromQueryArgument manually." - ) -)] -pub trait FromQueryArgument: Default { - /// The error that can occur when parsing a query argument. - type Err; - - /// Create an instance of `Self` from a query string. - fn from_query_argument(argument: &str) -> Result; -} - -impl FromQueryArgument for T -where - ::Err: Display, -{ - type Err = ::Err; - - fn from_query_argument(argument: &str) -> Result { - match T::from_str(argument) { - Ok(result) => Ok(result), - Err(err) => { - tracing::error!("Failed to parse query argument: {}", err); - Err(err) - } - } - } -} diff --git a/packages/router/src/routable/segments.rs b/packages/router/src/routable/segments.rs deleted file mode 100644 index 1e372443f0..0000000000 --- a/packages/router/src/routable/segments.rs +++ /dev/null @@ -1,178 +0,0 @@ -use dioxus_lib::prelude::*; - -use std::iter::FlatMap; -use std::slice::Iter; -use std::{fmt::Display, str::FromStr}; - -/// Something that can be created from a single route segment. This must be implemented for any type that is used as a route segment like `#[route("/:route_segment")]`. -/// -/// -/// **This trait is automatically implemented for any types that implement `FromStr` and `Default`.** -/// -/// ```rust -/// use dioxus::prelude::*; -/// -/// #[derive(Routable, Clone, PartialEq, Debug)] -/// enum Route { -/// // FromRouteSegment must be implemented for any types you use in the route segment -/// // When you don't spread the route, you can parse multiple values from the route -/// // This url will be in the format `/123/456` -/// #[route("/:route_segment_one/:route_segment_two")] -/// Home { -/// route_segment_one: CustomRouteSegment, -/// route_segment_two: i32, -/// }, -/// } -/// -/// // We can derive Default for CustomRouteSegment -/// // If the router fails to parse the route segment, it will use the default value instead -/// #[derive(Default, PartialEq, Clone, Debug)] -/// struct CustomRouteSegment { -/// count: i32, -/// } -/// -/// // We implement FromStr for CustomRouteSegment so that FromRouteSegment is implemented automatically -/// impl std::str::FromStr for CustomRouteSegment { -/// type Err = ::Err; -/// -/// fn from_str(route_segment: &str) -> Result { -/// Ok(CustomRouteSegment { -/// count: route_segment.parse()?, -/// }) -/// } -/// } -/// -/// // We also need to implement Display for CustomRouteSegment which will be used to format the route segment into the URL -/// impl std::fmt::Display for CustomRouteSegment { -/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { -/// write!(f, "{}", self.count) -/// } -/// } -/// -/// # #[component] -/// # fn Home(route_segment_one: CustomRouteSegment, route_segment_two: i32) -> Element { -/// # unimplemented!() -/// # } -/// ``` -#[rustversion::attr( - since(1.78.0), - diagnostic::on_unimplemented( - message = "`FromRouteSegment` is not implemented for `{Self}`", - label = "route segment", - note = "FromRouteSegment is automatically implemented for types that implement `FromStr` and `Default`. You need to either implement FromStr and Default or implement FromRouteSegment manually." - ) -)] -pub trait FromRouteSegment: Sized { - /// The error that can occur when parsing a route segment. - type Err; - - /// Create an instance of `Self` from a route segment. - fn from_route_segment(route: &str) -> Result; -} - -impl FromRouteSegment for T -where - ::Err: Display, -{ - type Err = ::Err; - - fn from_route_segment(route: &str) -> Result { - T::from_str(route) - } -} - -#[test] -fn full_circle() { - let route = "testing 1234 hello world"; - assert_eq!(String::from_route_segment(route).unwrap(), route); -} - -/// Something that can be converted into multiple route segments. This must be implemented for any type that is spread into the route segment like `#[route("/:..route_segments")]`. -/// -/// -/// **This trait is automatically implemented for any types that implement `IntoIterator`.** -/// -/// ```rust -/// use dioxus::prelude::*; -/// -/// #[derive(Routable, Clone, PartialEq, Debug)] -/// enum Route { -/// // FromRouteSegments must be implemented for any types you use in the route segment -/// // When you spread the route, you can parse multiple values from the route -/// // This url will be in the format `/123/456/789` -/// #[route("/:..numeric_route_segments")] -/// Home { -/// numeric_route_segments: NumericRouteSegments, -/// }, -/// } -/// -/// // We can derive Default for NumericRouteSegments -/// // If the router fails to parse the route segment, it will use the default value instead -/// #[derive(Default, PartialEq, Clone, Debug)] -/// struct NumericRouteSegments { -/// numbers: Vec, -/// } -/// -/// // Implement ToRouteSegments for NumericRouteSegments so that we can display the route segments -/// impl ToRouteSegments for NumericRouteSegments { -/// fn display_route_segments(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { -/// for number in &self.numbers { -/// write!(f, "/{}", number)?; -/// } -/// Ok(()) -/// } -/// } -/// -/// // We also need to parse the route segments with `FromRouteSegments` -/// impl FromRouteSegments for NumericRouteSegments { -/// type Err = ::Err; -/// -/// fn from_route_segments(segments: &[&str]) -> Result { -/// let mut numbers = Vec::new(); -/// for segment in segments { -/// numbers.push(segment.parse()?); -/// } -/// Ok(NumericRouteSegments { numbers }) -/// } -/// } -/// -/// # #[component] -/// # fn Home(numeric_route_segments: NumericRouteSegments) -> Element { -/// # unimplemented!() -/// # } -/// ``` -pub trait ToRouteSegments { - /// Display the route segments with each route segment separated by a `/`. This should not start with a `/`. - /// - fn display_route_segments(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result; -} - -// Implement ToRouteSegments for any type that can turn &self into an iterator of &T where T: Display -impl ToRouteSegments for I -where - for<'a> &'a I: IntoIterator, -{ - fn display_route_segments(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - for segment in self { - write!(f, "/")?; - let segment = segment.to_string(); - let encoded = urlencoding::encode(&segment); - write!(f, "{}", encoded)?; - } - Ok(()) - } -} - -#[test] -fn to_route_segments() { - struct DisplaysRoute; - - impl Display for DisplaysRoute { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let segments = vec!["hello", "world"]; - segments.display_route_segments(f) - } - } - - assert_eq!(DisplaysRoute.to_string(), "/hello/world"); -} diff --git a/packages/router/src/routable/sitemap.rs b/packages/router/src/routable/sitemap.rs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/packages/router/src/router_cfg.rs b/packages/router/src/router_cfg.rs index b8bc3e1dc6..9a1851539f 100644 --- a/packages/router/src/router_cfg.rs +++ b/packages/router/src/router_cfg.rs @@ -1,7 +1,5 @@ use crate::prelude::*; -use dioxus_document::DocumentContext; use dioxus_lib::prelude::*; -use generic_router::GenericRouterContext; use std::sync::Arc; /// Global configuration options for the router. @@ -21,14 +19,14 @@ use std::sync::Arc; /// } /// let cfg = RouterConfig::default().history(MemoryHistory::::default()); /// ``` -pub struct RouterConfig { +pub struct RouterConfig { pub(crate) failure_external_navigation: fn() -> Element, - pub(crate) history: Option, + pub(crate) history: Option>, pub(crate) on_update: Option>, pub(crate) initial_route: Option, } -impl Default for RouterConfig { +impl Default for RouterConfig { fn default() -> Self { Self { failure_external_navigation: FailureExternalNavigation, @@ -39,26 +37,26 @@ impl Default for RouterConfig { } } -// impl RouterConfig -// where -// ::Err: std::fmt::Display, -// { -// pub(crate) fn take_history(&mut self) -> Box { -// self.history -// .take() -// .unwrap_or_else(|| { -// let initial_route = self.initial_route.clone().unwrap_or_else(|| "/".parse().unwrap_or_else(|err| -// panic!("index route does not exist:\n{}\n use MemoryHistory::with_initial_path or RouterConfig::initial_route to set a custom path", err) -// )); -// default_history(initial_route) -// }) -// } -// } +impl RouterConfig +where + ::Err: std::fmt::Display, +{ + pub(crate) fn take_history(&mut self) -> Box { + self.history + .take() + .unwrap_or_else(|| { + let initial_route = self.initial_route.clone().unwrap_or_else(|| "/".parse().unwrap_or_else(|err| + panic!("index route does not exist:\n{}\n use MemoryHistory::with_initial_path or RouterConfig::initial_route to set a custom path", err) + )); + default_history(initial_route) + }) + } +} impl RouterConfig where R: Routable, - // ::Err: std::fmt::Display, + ::Err: std::fmt::Display, { /// A function to be called whenever the routing is updated. /// @@ -83,23 +81,16 @@ where } } - pub fn with_initial_path(self, initial_path: R) -> Self { + /// The [`HistoryProvider`] the router should use. + /// + /// Defaults to a different history provider depending on the target platform. + pub fn history(self, history: impl HistoryProvider + 'static) -> Self { Self { - initial_route: Some(initial_path), + history: Some(Box::new(AnyHistoryProviderImplWrapper::new(history))), ..self } } - // /// The [`HistoryProvider`] the router should use. - // /// - // /// Defaults to a different history provider depending on the target platform. - // pub fn history(self, history: impl HistoryProvider + 'static) -> Self { - // Self { - // history: Some(Box::new(AnyHistoryProviderImplWrapper::new(history))), - // ..self - // } - // } - /// The initial route the router should use if no history provider is set. pub fn initial_route(self, route: R) -> Self { Self { @@ -118,3 +109,47 @@ where } } } + +/// Get the default history provider for the current platform. +#[allow(unreachable_code, unused)] +fn default_history(initial_route: R) -> Box +where + ::Err: std::fmt::Display, +{ + // If we're on the web and have wasm, use the web history provider + + #[cfg(all(target_arch = "wasm32", feature = "web"))] + return Box::new(AnyHistoryProviderImplWrapper::new( + WebHistory::::default(), + )); + + // If we're using fullstack and server side rendering, use the memory history provider + #[cfg(all(feature = "fullstack", feature = "ssr"))] + return Box::new(AnyHistoryProviderImplWrapper::new( + MemoryHistory::::with_initial_path( + dioxus_fullstack::prelude::server_context() + .request_parts() + .uri + .to_string() + .parse() + .unwrap_or_else(|err| { + tracing::error!("Failed to parse uri: {}", err); + "/".parse().unwrap_or_else(|err| { + panic!("Failed to parse uri: {}", err); + }) + }), + ), + )); + + // If liveview is enabled, use the liveview history provider + #[cfg(feature = "liveview")] + return Box::new(AnyHistoryProviderImplWrapper::new( + LiveviewHistory::new_with_initial_path(initial_route), + )); + + // If none of the above, use the memory history provider, which is a decent enough fallback + // Eventually we want to integrate with the mobile history provider, and other platform providers + Box::new(AnyHistoryProviderImplWrapper::new( + MemoryHistory::with_initial_path(initial_route), + )) +} diff --git a/packages/router/tests/site_map.rs b/packages/router/tests/site_map.rs index 37a318e77b..3252567890 100644 --- a/packages/router/tests/site_map.rs +++ b/packages/router/tests/site_map.rs @@ -6,7 +6,6 @@ fn with_class() { enum ChildRoute { #[route("/")] ChildRoot {}, - #[route("/:not_static")] NotStatic { not_static: String }, } @@ -15,10 +14,8 @@ fn with_class() { enum Route { #[route("/")] Root {}, - #[route("/test")] Test {}, - #[child("/child")] Nested { child: ChildRoute }, } diff --git a/packages/router/tests/via_ssr/link.rs b/packages/router/tests/via_ssr/link.rs index b6fe9f53d2..0cdfb92eb9 100644 --- a/packages/router/tests/via_ssr/link.rs +++ b/packages/router/tests/via_ssr/link.rs @@ -1,6 +1,10 @@ use dioxus::prelude::*; +use std::str::FromStr; -fn prepare() -> String { +fn prepare() -> String +where + ::Err: std::fmt::Display, +{ let mut vdom = VirtualDom::new_with_props( App, AppProps:: { @@ -30,10 +34,15 @@ fn prepare() -> String { } #[allow(non_snake_case)] - fn App(_props: AppProps) -> Element { + fn App(_props: AppProps) -> Element + where + ::Err: std::fmt::Display, + { rsx! { h1 { "App" } - Router:: { config: |_| RouterConfig::default() } + Router:: { + config: |_| RouterConfig::default().history(MemoryHistory::default()) + } } } } @@ -56,7 +65,10 @@ fn href_internal() { #[component] fn Root() -> Element { rsx! { - Link { to: Route::Test {}, "Link" } + Link { + to: Route::Test {}, + "Link" + } } } @@ -83,7 +95,10 @@ fn href_external() { #[component] fn Root() -> Element { rsx! { - Link { to: "https://dioxuslabs.com/", "Link" } + Link { + to: "https://dioxuslabs.com/", + "Link" + } } } @@ -114,7 +129,11 @@ fn with_class() { #[component] fn Root() -> Element { rsx! { - Link { to: Route::Test {}, class: "test_class", "Link" } + Link { + to: Route::Test {}, + class: "test_class", + "Link" + } } } @@ -211,7 +230,11 @@ fn with_id() { #[component] fn Root() -> Element { rsx! { - Link { to: Route::Test {}, id: "test_id", "Link" } + Link { + to: Route::Test {}, + id: "test_id", + "Link" + } } } @@ -242,7 +265,11 @@ fn with_new_tab() { #[component] fn Root() -> Element { rsx! { - Link { to: Route::Test {}, new_tab: true, "Link" } + Link { + to: Route::Test {}, + new_tab: true, + "Link" + } } } @@ -266,7 +293,11 @@ fn with_new_tab_external() { #[component] fn Root() -> Element { rsx! { - Link { to: "https://dioxuslabs.com/", new_tab: true, "Link" } + Link { + to: "https://dioxuslabs.com/", + new_tab: true, + "Link" + } } } @@ -298,7 +329,11 @@ fn with_rel() { #[component] fn Root() -> Element { rsx! { - Link { to: Route::Test {}, rel: "test_rel".to_string(), "Link" } + Link { + to: Route::Test {}, + rel: "test_rel".to_string(), + "Link" + } } } diff --git a/packages/router/tests/via_ssr/outlet.rs b/packages/router/tests/via_ssr/outlet.rs index 8e151e2dc9..f3ab2f658f 100644 --- a/packages/router/tests/via_ssr/outlet.rs +++ b/packages/router/tests/via_ssr/outlet.rs @@ -38,59 +38,53 @@ fn prepare(path: impl Into) -> VirtualDom { fn App(path: Route) -> Element { rsx! { h1 { "App" } - Router:: { config: move |_| { RouterConfig::default().with_initial_path(path.clone()) } } + Router:: { + config: move |_| { + RouterConfig::default().history(MemoryHistory::with_initial_path(path.clone())) + } + } } } #[component] fn RootIndex() -> Element { - rsx! { - h2 { "Root Index" } - } + rsx! { h2 { "Root Index" } } } #[component] fn Fixed() -> Element { rsx! { h2 { "Fixed" } - Outlet:: {} + Outlet:: { } } } #[component] fn FixedIndex() -> Element { - rsx! { - h3 { "Fixed - Index" } - } + rsx! { h3 { "Fixed - Index" } } } #[component] fn FixedFixed() -> Element { - rsx! { - h3 { "Fixed - Fixed" } - } + rsx! { h3 { "Fixed - Fixed"} } } #[component] fn Parameter(id: u8) -> Element { rsx! { h2 { "Parameter {id}" } - Outlet:: {} + Outlet:: { } } } #[component] fn ParameterIndex(id: u8) -> Element { - rsx! { - h3 { "Parameter - Index" } - } + rsx! { h3 { "Parameter - Index" } } } #[component] fn ParameterFixed(id: u8) -> Element { - rsx! { - h3 { "Parameter - Fixed" } - } + rsx! { h3 { "Parameter - Fixed" } } } } diff --git a/packages/router/tests/via_ssr/redirect.rs b/packages/router/tests/via_ssr/redirect.rs index 4af82e0b1b..bd9c919ca4 100644 --- a/packages/router/tests/via_ssr/redirect.rs +++ b/packages/router/tests/via_ssr/redirect.rs @@ -33,6 +33,10 @@ fn Home(lang: String) -> Element { #[component] fn App(path: Route) -> Element { rsx! { - Router:: { config: move |_| RouterConfig::default().with_initial_path(path.clone()) } + Router:: { + config: { + move |_| RouterConfig::default().history(MemoryHistory::with_initial_path(path.clone())) + } + } } } diff --git a/packages/router/tests/via_ssr/without_index.rs b/packages/router/tests/via_ssr/without_index.rs index 4b5be6a79c..9ee85706b4 100644 --- a/packages/router/tests/via_ssr/without_index.rs +++ b/packages/router/tests/via_ssr/without_index.rs @@ -34,7 +34,7 @@ fn App(path: Route) -> Element { rsx! { Router:: { config: { - move || RouterConfig::default().with_initial_path(path) + move || RouterConfig::default().history(MemoryHistory::with_initial_path(path)) } } } diff --git a/packages/rsx-hotreload/src/context.rs b/packages/rsx-hotreload/src/context.rs deleted file mode 100644 index 8b13789179..0000000000 --- a/packages/rsx-hotreload/src/context.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/packages/rsx/src/component.rs b/packages/rsx/src/component.rs index af5a33baff..f3ddac785d 100644 --- a/packages/rsx/src/component.rs +++ b/packages/rsx/src/component.rs @@ -209,7 +209,7 @@ impl Component { /// Create the tokens we'll use for the props of the component /// - /// todo: don't create the tokenstream from scratch and instead dump it into the existing stream + /// todo: don't create the tokenstream from scratch and instead dump it into the existing streama fn create_props(&self) -> TokenStream2 { let manual_props = self.manual_props(); diff --git a/packages/runtime-config/Cargo.toml b/packages/runtime-config/Cargo.toml deleted file mode 100644 index f899820e81..0000000000 --- a/packages/runtime-config/Cargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "dioxus-runtime-config" -edition = "2021" -version.workspace = true - -[dependencies] \ No newline at end of file diff --git a/packages/runtime-config/README.md b/packages/runtime-config/README.md deleted file mode 100644 index dfa2f77135..0000000000 --- a/packages/runtime-config/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# dioxus-runtime-config - -A crate that provides key/value names and types for configuring Dioxus applications at runtime. - -This crate exists for us to very cleanly define the exact fields we want to pass down to Dioxus applications at runtime but without exposing the entire config object. - -This leads to faster compile times, smaller binaries, and a clearer distinction between the config and the application. diff --git a/packages/runtime-config/src/lib.rs b/packages/runtime-config/src/lib.rs deleted file mode 100644 index d948bd4099..0000000000 --- a/packages/runtime-config/src/lib.rs +++ /dev/null @@ -1,24 +0,0 @@ -use std::net::SocketAddr; - -pub const DEVSERVER_RAW_ADDR_ENV: &str = "DIOXUS_DEVSERVER_ADDR"; -pub const FULLSTACK_ADDRESS_ENV: &str = "DIOXUS_FULLSTACK_ADDRESS"; - -/// when targetting ios, we need to set a prefix to the argument such that it gets picked up by simctl -pub const IOS_DEVSERVER_ADDR_ENV: &str = "SIMCTL_CHILD_DIOXUS_DEVSERVER_ADDR"; - -/// Get the address of the devserver for use over a raw socket -/// -/// This is not a websocket! There's no protocol! -pub fn devserver_raw_addr() -> Option { - // #[cfg(target_os = "ios")] - // return std::env::var(IOS_DEVSERVER_ADDR_ENV).ok(); - - // #[cfg(not(target_os = "ios"))] - return std::env::var(DEVSERVER_RAW_ADDR_ENV).ok(); -} - -pub fn fullstack_address() -> Option { - std::env::var(FULLSTACK_ADDRESS_ENV) - .ok() - .and_then(|s| s.parse().ok()) -} diff --git a/packages/server/Cargo.toml b/packages/server/Cargo.toml deleted file mode 100644 index 16b7c0fba2..0000000000 --- a/packages/server/Cargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "dioxus-server" -edition = "2021" -version.workspace = true - -[dependencies] \ No newline at end of file diff --git a/packages/server/src/document.rs b/packages/server/src/document.rs deleted file mode 100644 index 8b13789179..0000000000 --- a/packages/server/src/document.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/packages/server/src/lib.rs b/packages/server/src/lib.rs deleted file mode 100644 index 103318948c..0000000000 --- a/packages/server/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ -mod document; diff --git a/packages/ssr/src/renderer.rs b/packages/ssr/src/renderer.rs index 85d41003ae..d9e178f32e 100644 --- a/packages/ssr/src/renderer.rs +++ b/packages/ssr/src/renderer.rs @@ -1,5 +1,6 @@ use super::cache::Segment; use crate::cache::StringCache; + use dioxus_core::{prelude::*, AttributeValue, DynamicNode}; use rustc_hash::FxHashMap; use std::fmt::Write; diff --git a/packages/static-generation/Cargo.toml b/packages/static-generation/Cargo.toml index ddb5546956..3e5ac6e32f 100644 --- a/packages/static-generation/Cargo.toml +++ b/packages/static-generation/Cargo.toml @@ -13,7 +13,7 @@ resolver = "2" [dependencies] dioxus-fullstack = { workspace = true } dioxus-lib.workspace = true -dioxus-router = { workspace = true } +dioxus-router = { workspace = true, features = ["fullstack"]} dioxus-ssr = { workspace = true, optional = true } dioxus-isrg = { workspace = true, optional = true } axum = { workspace = true, features = ["ws", "macros"], optional = true } @@ -23,7 +23,8 @@ dioxus-cli-config = { workspace = true, optional = true } dioxus-web = { workspace = true, features = ["hydrate"], optional = true } tokio = { workspace = true, optional = true } http = { workspace = true, optional = true } -tracing = { workspace = true } +tower = { workspace = true, features = ["util"], optional = true } +tracing.workspace = true [dev-dependencies] dioxus = { workspace = true } diff --git a/packages/web/Cargo.toml b/packages/web/Cargo.toml index 1d3df10e4c..f79b2692a8 100644 --- a/packages/web/Cargo.toml +++ b/packages/web/Cargo.toml @@ -10,14 +10,33 @@ homepage = "https://dioxuslabs.com/learn/0.5/getting_started" keywords = ["dom", "ui", "gui", "react", "wasm"] [dependencies] - +dioxus-core = { workspace = true } dioxus-core-types = { workspace = true } dioxus-html = { workspace = true } dioxus-devtools = { workspace = true } dioxus-signals = { workspace = true } -dioxus-interpreter-js = { workspace = true, features = ["minimal_bindings", "webonly"] } +dioxus-interpreter-js = { workspace = true, features = [ + "minimal_bindings", + "webonly", +] } generational-box = { workspace = true } -async-trait = { workspace = true } + +js-sys = "0.3.56" +wasm-bindgen = { workspace = true } +wasm-bindgen-futures = "0.4.29" +tracing = { workspace = true } +rustc-hash = { workspace = true } +console_error_panic_hook = { version = "0.1.7", optional = true } +futures-util = { workspace = true, features = [ + "std", + "async-await", + "async-await-macro", +] } +futures-channel = { workspace = true } +serde_json = { version = "1.0", optional = true } +serde = { version = "1.0", optional = true } +serde-wasm-bindgen = { version = "0.5.0", optional = true } + ciborium = { workspace = true, optional = true } async-trait = { version = "0.1.58", optional = true } @@ -62,7 +81,6 @@ lazy-js-bundle = { workspace = true } [features] default = ["panic_hook", "mounted", "file_engine", "devtools", "document"] panic_hook = ["dep:console_error_panic_hook"] - hydrate = ["web-sys/Comment", "ciborium", "dep:serde"] mounted = [ "web-sys/Element", @@ -88,7 +106,8 @@ document = ["dioxus-html/document", "dep:serde-wasm-bindgen", "dep:serde_json", dioxus = { workspace = true, default-features = true } wasm-bindgen-test = "0.3.29" dioxus-ssr = { workspace = true, default-features = false } -gloo-timers = { workspace = true } +gloo-timers = "0.2.3" +gloo-dialogs = "0.1.1" dioxus-web = { path = ".", features = ["hydrate"] } tracing-wasm = "0.2.1" diff --git a/packages/web/src/devtools.rs b/packages/web/src/devtools.rs index 411de3823f..48a6f8ea0c 100644 --- a/packages/web/src/devtools.rs +++ b/packages/web/src/devtools.rs @@ -13,7 +13,7 @@ use futures_channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender}; use js_sys::JsString; use wasm_bindgen::JsCast; use wasm_bindgen::{closure::Closure, JsValue}; -use web_sys::{window, Event, MessageEvent, WebSocket}; +use web_sys::{window, CloseEvent, MessageEvent, WebSocket}; const POLL_INTERVAL_MIN: i32 = 250; const POLL_INTERVAL_MAX: i32 = 4000; @@ -75,7 +75,7 @@ fn make_ws(tx: UnboundedSender, poll_interval: i32, reload: bool) "Your app is being rebuilt.", "A non-hot-reloadable change occurred and we must rebuild.", ToastLevel::Info, - Duration::from_secs(600), + TOAST_TIMEOUT, false, ), // The devserver is telling us that the full rebuild failed. @@ -111,13 +111,11 @@ fn make_ws(tx: UnboundedSender, poll_interval: i32, reload: bool) // Set the onclose handler to reload the page if the connection is closed ws.set_onclose(Some( - Closure::::new(move |e: Event| { + Closure::::new(move |e: CloseEvent| { // Firefox will send a 1001 code when the connection is closed because the page is reloaded // Only firefox will trigger the onclose event when the page is reloaded manually: https://stackoverflow.com/questions/10965720/should-websocket-onclose-be-triggered-by-user-navigation-or-refresh // We should not reload the page in this case - if js_sys::Reflect::get(&e, &"code".into()).map(|f| f.as_f64().unwrap_or(0.0)) - == Ok(1001.0) - { + if e.code() == 1001 { return; } @@ -233,11 +231,9 @@ pub(crate) fn invalidate_browser_asset_cache() { for x in 0..links.length() { use wasm_bindgen::JsCast; - if let Some(link) = links.get(x) { - let link: web_sys::Element = link.unchecked_into(); - if let Some(href) = link.get_attribute("href") { - _ = link.set_attribute("href", &format!("{}?{}", href, noise)); - } + let link: web_sys::Element = links.get(x).unwrap().unchecked_into(); + if let Some(href) = link.get_attribute("href") { + _ = link.set_attribute("href", &format!("{}?{}", href, noise)); } } } diff --git a/packages/web/src/document.rs b/packages/web/src/document.rs index 1f5e2b9e7a..62127c7c7a 100644 --- a/packages/web/src/document.rs +++ b/packages/web/src/document.rs @@ -1,5 +1,3 @@ -use std::rc::Rc; - use dioxus_core::ScopeId; use dioxus_html::document::{Document, EvalError, Evaluator}; use generational_box::{AnyStorage, GenerationalBox, UnsyncStorage}; @@ -57,86 +55,139 @@ extern "C" { /// Provides the WebEvalProvider through [`ScopeId::provide_context`]. pub fn init_document() { - let provider = WebDocument::get(); + let provider: Rc = Rc::new(WebDocument); if ScopeId::ROOT.has_context::>().is_none() { ScopeId::ROOT.provide_context(provider); } } /// The web-target's document provider. -pub struct WebDocument { - document: web_sys::Document, -} +pub struct WebDocument; +impl Document for WebDocument { + fn new_evaluator(&self, js: String) -> GenerationalBox> { + WebEvaluator::create(js) + } -impl WebDocument { - /// Get the web document provider - pub fn get() -> Rc { - let window = web_sys::window().unwrap(); - let document = window.document().unwrap(); - let provider: Rc = Rc::new(WebDocument { document }); - provider + fn as_any(&self) -> &dyn std::any::Any { + self } } -impl Document for WebDocument { - fn eval(&self, js: String) -> Eval { - let (tx, eval) = Eval::from_parts(); - - // todo: this deserialize is probably wrong. - _ = match js_sys::eval(&js) { - Ok(ok) => { - tracing::trace!("eval result: {ok:#?}"); - let msg = serde_wasm_bindgen::from_value(ok).unwrap_or_default(); +/// Required to avoid blocking the Rust WASM thread. +const PROMISE_WRAPPER: &str = r#" + return new Promise(async (resolve, _reject) => { + {JS_CODE} + resolve(null); + }); +"#; + +type NextPoll = Pin>>>; + +/// Represents a web-target's JavaScript evaluator. +struct WebEvaluator { + channels: WeakDioxusChannel, + next_future: Option, + result: Option>, +} - tx.send(Ok(msg)) +impl WebEvaluator { + /// Creates a new evaluator for web-based targets. + fn create(js: String) -> GenerationalBox> { + let owner = UnsyncStorage::owner(); + + let generational_box = owner.invalid(); + + // add the drop handler to DioxusChannel so that it gets dropped when the channel is dropped in js + let channels = WebDioxusChannel::new(JSOwner::new(owner)); + + // The Rust side of the channel is a weak reference to the DioxusChannel + let weak_channels = channels.weak(); + + // Wrap the evaluated JS in a promise so that wasm can continue running (send/receive data from js) + let code = PROMISE_WRAPPER.replace("{JS_CODE}", &js); + + let result = match Function::new_with_args("dioxus", &code).call1(&JsValue::NULL, &channels) + { + Ok(result) => { + if let Ok(stringified) = js_sys::JSON::stringify(&result) { + if !stringified.is_undefined() && stringified.is_valid_utf16() { + let string: String = stringified.into(); + Value::from_str(&string).map_err(|e| { + EvalError::Communication(format!("Failed to parse result - {}", e)) + }) + } else { + Err(EvalError::Communication( + "Failed to stringify result".into(), + )) + } + } else { + Err(EvalError::Communication( + "Failed to stringify result".into(), + )) + } } - Err(_err) => tx.send(Err(dioxus_document::EvalError::Communication( - "eval failed".to_string(), - ))), + Err(err) => Err(EvalError::InvalidJs( + err.as_string().unwrap_or("unknown".to_string()), + )), }; - eval - } + generational_box.set(Box::new(Self { + channels: weak_channels, + result: Some(result), + next_future: None, + }) as Box); - fn set_title(&self, title: String) { - self.document.set_title(&title); + generational_box } +} - fn create_head_element( - &self, - name: &str, - attributes: Vec<(&str, String)>, - contents: Option, - ) { - if let Some(head) = self.document.head() { - let element = self.document.create_element(name).unwrap(); - for (name, value) in attributes { - element.set_attribute(name, &value).unwrap(); - } - if let Some(contents) = contents { - element.set_inner_html(&contents); - } - head.append_child(&element.into()).unwrap(); +impl Evaluator for WebEvaluator { + /// Runs the evaluated JavaScript. + fn poll_join( + &mut self, + _cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + if let Some(result) = self.result.take() { + std::task::Poll::Ready(result) + } else { + std::task::Poll::Ready(Err(EvalError::Finished)) } } - fn current_route(&self) -> String { - todo!() - } - - fn go_back(&self) { - todo!() - } + /// Sends a message to the evaluated JavaScript. + fn send(&self, data: serde_json::Value) -> Result<(), EvalError> { + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - fn go_forward(&self) { - todo!() - } + let data = match data.serialize(&serializer) { + Ok(d) => d, + Err(e) => return Err(EvalError::Communication(e.to_string())), + }; - fn push_route(&self, route: String) { - todo!() + self.channels.rust_send(data); + Ok(()) } - fn replace_route(&self, path: String) { - todo!() + /// Gets an UnboundedReceiver to receive messages from the evaluated JavaScript. + fn poll_recv( + &mut self, + context: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + if self.next_future.is_none() { + let channels: WebDioxusChannel = self.channels.clone().into(); + let pinned = Box::pin(async move { + let fut = channels.rust_recv(); + let data = fut.await; + serde_wasm_bindgen::from_value::(data) + .map_err(|err| EvalError::Communication(err.to_string())) + }); + self.next_future = Some(pinned); + } + let fut = self.next_future.as_mut().unwrap(); + let mut pinned = std::pin::pin!(fut); + let result = pinned.as_mut().poll(context); + if result.is_ready() { + self.next_future = None; + } + result } } diff --git a/packages/web/src/event/drag.rs b/packages/web/src/event/drag.rs deleted file mode 100644 index 03f846bba6..0000000000 --- a/packages/web/src/event/drag.rs +++ /dev/null @@ -1,95 +0,0 @@ -use dioxus_html::{ - point_interaction::{ - InteractionElementOffset, InteractionLocation, ModifiersInteraction, PointerInteraction, - }, - HasDragData, HasFileData, HasMouseData, -}; -use wasm_bindgen::JsCast; -use web_sys::MouseEvent; - -use super::synthetic::Synthetic; - -pub struct WebDragData { - raw: Synthetic, -} - -impl WebDragData { - pub fn new(raw: MouseEvent) -> Self { - Self { - raw: Synthetic::new(raw), - } - } -} - -impl HasDragData for WebDragData { - fn as_any(&self) -> &dyn std::any::Any { - &self.raw as &dyn std::any::Any - } -} - -impl HasMouseData for WebDragData { - fn as_any(&self) -> &dyn std::any::Any { - &self.raw as &dyn std::any::Any - } -} - -impl PointerInteraction for WebDragData { - fn trigger_button(&self) -> Option { - self.raw.trigger_button() - } - - fn held_buttons(&self) -> dioxus_html::input_data::MouseButtonSet { - self.raw.held_buttons() - } -} - -impl ModifiersInteraction for WebDragData { - fn modifiers(&self) -> dioxus_html::prelude::Modifiers { - self.raw.modifiers() - } -} - -impl InteractionElementOffset for WebDragData { - fn coordinates(&self) -> dioxus_html::geometry::Coordinates { - self.raw.coordinates() - } - - fn element_coordinates(&self) -> dioxus_html::geometry::ElementPoint { - self.raw.element_coordinates() - } -} - -impl InteractionLocation for WebDragData { - fn client_coordinates(&self) -> dioxus_html::geometry::ClientPoint { - self.raw.client_coordinates() - } - - fn screen_coordinates(&self) -> dioxus_html::geometry::ScreenPoint { - self.raw.screen_coordinates() - } - - fn page_coordinates(&self) -> dioxus_html::geometry::PagePoint { - self.raw.page_coordinates() - } -} - -impl HasFileData for WebDragData { - #[cfg(feature = "file-engine")] - fn files(&self) -> Option> { - use super::file::WebFileEngine; - - self.raw - .event - .dyn_ref::() - .and_then(|drag_event| { - drag_event.data_transfer().and_then(|dt| { - dt.files().and_then(|files| { - #[allow(clippy::arc_with_non_send_sync)] - WebFileEngine::new(files).map(|f| { - std::sync::Arc::new(f) as std::sync::Arc - }) - }) - }) - }) - } -} diff --git a/packages/web/src/event/ext.rs b/packages/web/src/event/ext.rs deleted file mode 100644 index d6bce1134b..0000000000 --- a/packages/web/src/event/ext.rs +++ /dev/null @@ -1,155 +0,0 @@ -/// A extension trait for web-sys events that provides a way to get the event as a web-sys event. -pub trait WebEventExt { - /// Try to downcast this event as a `web-sys` event. - fn try_as_web_event(&self) -> Option; - - /// Downcast this event as a `web-sys` event. - #[inline(always)] - fn as_web_event(&self) -> E - where - E: 'static, - { - self.try_as_web_event().unwrap_or_else(|| { - panic!( - "Error downcasting to `web-sys`, event should be a {}.", - std::any::type_name::() - ) - }) - } -} - -// impl WebEventExt for dioxus_html::AnimationData { -// #[inline(always)] -// fn try_as_web_event(&self) -> Option { -// self.downcast::().cloned() -// } -// } - -// impl WebEventExt for dioxus_html::ClipboardData { -// #[inline(always)] -// fn try_as_web_event(&self) -> Option { -// self.downcast::().cloned() -// } -// } - -// impl WebEventExt for dioxus_html::CompositionData { -// #[inline(always)] -// fn try_as_web_event(&self) -> Option { -// self.downcast::().cloned() -// } -// } - -// impl WebEventExt for dioxus_html::DragData { -// #[inline(always)] -// fn try_as_web_event(&self) -> Option { -// self.downcast::() -// .map(|data| &data.raw) -// .cloned() -// } -// } - -// impl WebEventExt for dioxus_html::FocusData { -// #[inline(always)] -// fn try_as_web_event(&self) -> Option { -// self.downcast::().cloned() -// } -// } - -// impl WebEventExt for dioxus_html::FormData { -// #[inline(always)] -// fn try_as_web_event(&self) -> Option { -// self.downcast::().cloned() -// } -// } - -// impl WebEventExt for dioxus_html::ImageData { -// #[inline(always)] -// fn try_as_web_event(&self) -> Option { -// self.downcast::().cloned() -// } -// } - -// impl WebEventExt for dioxus_html::KeyboardData { -// #[inline(always)] -// fn try_as_web_event(&self) -> Option { -// self.downcast::().cloned() -// } -// } - -// impl WebEventExt for dioxus_html::MediaData { -// #[inline(always)] -// fn try_as_web_event(&self) -> Option { -// self.downcast::().cloned() -// } -// } - -// impl WebEventExt for MountedData { -// #[inline(always)] -// fn try_as_web_event(&self) -> Option { -// self.downcast::().cloned() -// } -// } - -// impl WebEventExt for dioxus_html::MouseData { -// #[inline(always)] -// fn try_as_web_event(&self) -> Option { -// self.downcast::().cloned() -// } -// } - -// impl WebEventExt for dioxus_html::PointerData { -// #[inline(always)] -// fn try_as_web_event(&self) -> Option { -// self.downcast::().cloned() -// } -// } - -// impl WebEventExt for ScrollData { -// #[inline(always)] -// fn try_as_web_event(&self) -> Option { -// self.downcast::().cloned() -// } -// } - -// impl WebEventExt for dioxus_html::SelectionData { -// #[inline(always)] -// fn try_as_web_event(&self) -> Option { -// self.downcast::().cloned() -// } -// } - -// impl WebEventExt for dioxus_html::ToggleData { -// #[inline(always)] -// fn try_as_web_event(&self) -> Option { -// self.downcast::().cloned() -// } -// } - -// impl WebEventExt for dioxus_html::TouchData { -// #[inline(always)] -// fn try_as_web_event(&self) -> Option { -// self.downcast::().cloned() -// } -// } - -// impl WebEventExt for dioxus_html::TransitionData { -// #[inline(always)] -// fn try_as_web_event(&self) -> Option { -// self.downcast::().cloned() -// } -// } - -// impl WebEventExt for dioxus_html::WheelData { -// #[inline(always)] -// fn try_as_web_event(&self) -> Option { -// self.downcast::().cloned() -// } -// } - -// impl WebEventExt for dioxus_html::ResizeData { -// #[inline(always)] -// fn try_as_web_event(&self) -> Option { -// self.downcast::() -// .and_then(|e| e.detail().dyn_into::().ok()) -// } -// } diff --git a/packages/web/src/event/file.rs b/packages/web/src/event/file.rs deleted file mode 100644 index 79696ea1ed..0000000000 --- a/packages/web/src/event/file.rs +++ /dev/null @@ -1,144 +0,0 @@ -use std::any::Any; - -#[cfg(feature = "file-engine")] -use dioxus_html::FileEngine; -// use dioxus_html::FileEngine; -use futures_channel::oneshot; -use js_sys::Uint8Array; -use wasm_bindgen::{prelude::Closure, JsCast}; - -#[cfg(feature = "file-engine")] -use web_sys::{File, FileList, FileReader}; - -/// A file engine for the web platform -#[cfg(feature = "file-engine")] -pub struct WebFileEngine { - file_reader: FileReader, - file_list: FileList, -} - -#[cfg(feature = "file-engine")] -impl WebFileEngine { - /// Create a new file engine from a file list - pub fn new(file_list: FileList) -> Option { - Some(Self { - file_list, - file_reader: FileReader::new().ok()?, - }) - } - - fn len(&self) -> usize { - self.file_list.length() as usize - } - - fn get(&self, index: usize) -> Option { - self.file_list.item(index as u32) - } - - fn find(&self, name: &str) -> Option { - (0..self.len()) - .filter_map(|i| self.get(i)) - .find(|f| f.name() == name) - } -} - -#[cfg(feature = "file-engine")] -#[async_trait::async_trait(?Send)] -impl FileEngine for WebFileEngine { - fn files(&self) -> Vec { - (0..self.len()) - .filter_map(|i| self.get(i).map(|f| f.name())) - .collect() - } - - async fn file_size(&self, file: &str) -> Option { - let file = self.find(file)?; - Some(file.size() as u64) - } - - // read a file to bytes - async fn read_file(&self, file: &str) -> Option> { - let file = self.find(file)?; - - let file_reader = self.file_reader.clone(); - let (rx, tx) = oneshot::channel(); - let on_load: Closure = Closure::new({ - let mut rx = Some(rx); - move || { - let result = file_reader.result(); - let _ = rx - .take() - .expect("multiple files read without refreshing the channel") - .send(result); - } - }); - - self.file_reader - .set_onload(Some(on_load.as_ref().unchecked_ref())); - on_load.forget(); - self.file_reader.read_as_array_buffer(&file).ok()?; - - if let Ok(Ok(js_val)) = tx.await { - let as_u8_arr = Uint8Array::new(&js_val); - let as_u8_vec = as_u8_arr.to_vec(); - - Some(as_u8_vec) - } else { - None - } - } - - // read a file to string - async fn read_file_to_string(&self, file: &str) -> Option { - let file = self.find(file)?; - - let file_reader = self.file_reader.clone(); - let (rx, tx) = oneshot::channel(); - let on_load: Closure = Closure::new({ - let mut rx = Some(rx); - move || { - let result = file_reader.result(); - let _ = rx - .take() - .expect("multiple files read without refreshing the channel") - .send(result); - } - }); - - self.file_reader - .set_onload(Some(on_load.as_ref().unchecked_ref())); - on_load.forget(); - self.file_reader.read_as_text(&file).ok()?; - - if let Ok(Ok(js_val)) = tx.await { - js_val.as_string() - } else { - None - } - } - - async fn get_native_file(&self, file: &str) -> Option> { - let file = self.find(file)?; - Some(Box::new(file)) - } -} - -#[cfg(feature = "file-engine")] -/// Helper trait for WebFileEngine -#[async_trait::async_trait(?Send)] -pub trait WebFileEngineExt { - /// returns web_sys::File - #[cfg(feature = "file-engine")] - async fn get_web_file(&self, file: &str) -> Option; -} - -#[cfg(feature = "file-engine")] -#[async_trait::async_trait(?Send)] -impl WebFileEngineExt for std::sync::Arc { - #[cfg(feature = "file-engine")] - async fn get_web_file(&self, file: &str) -> Option { - let native_file = self.get_native_file(file).await?; - let ret = native_file.downcast::().ok()?; - Some(*ret) - } -} diff --git a/packages/web/src/event/form.rs b/packages/web/src/event/form.rs deleted file mode 100644 index c909e4cd43..0000000000 --- a/packages/web/src/event/form.rs +++ /dev/null @@ -1,150 +0,0 @@ -use std::{any::Any, collections::HashMap}; - -use dioxus_html::{FormValue, HasFileData, HasFormData}; -use js_sys::Array; -use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue}; -use web_sys::{Element, Event}; - -pub struct WebFormData { - pub element: Element, - pub raw: Event, -} - -impl WebFormData { - pub fn new(element: Element, raw: Event) -> Self { - Self { element, raw } - } -} - -impl HasFormData for WebFormData { - fn value(&self) -> String { - let target = &self.element; - target - .dyn_ref() - .map(|input: &web_sys::HtmlInputElement| { - // todo: special case more input types - match input.type_().as_str() { - "checkbox" => { - match input.checked() { - true => "true".to_string(), - false => "false".to_string(), - } - }, - _ => { - input.value() - } - } - }) - .or_else(|| { - target - .dyn_ref() - .map(|input: &web_sys::HtmlTextAreaElement| input.value()) - }) - // select elements are NOT input events - because - why woudn't they be?? - .or_else(|| { - target - .dyn_ref() - .map(|input: &web_sys::HtmlSelectElement| input.value()) - }) - .or_else(|| { - target - .dyn_ref::() - .unwrap() - .text_content() - }) - .expect("only an InputElement or TextAreaElement or an element with contenteditable=true can have an oninput event listener") - } - - fn values(&self) -> HashMap { - let mut values = HashMap::new(); - - fn insert_value(map: &mut HashMap, key: String, new_value: String) { - map.entry(key.clone()).or_default().0.push(new_value); - } - - // try to fill in form values - if let Some(form) = self.element.dyn_ref::() { - let form_data = get_form_data(form); - for value in form_data.entries().into_iter().flatten() { - if let Ok(array) = value.dyn_into::() { - if let Some(name) = array.get(0).as_string() { - if let Ok(item_values) = array.get(1).dyn_into::() { - item_values - .iter() - .filter_map(|v| v.as_string()) - .for_each(|v| insert_value(&mut values, name.clone(), v)); - } else if let Ok(item_value) = array.get(1).dyn_into::() { - insert_value(&mut values, name, item_value.as_string().unwrap()); - } - } - } - } - } else if let Some(select) = self.element.dyn_ref::() { - // try to fill in select element values - let options = get_select_data(select); - values.insert("options".to_string(), FormValue(options)); - } - - values - } - - fn as_any(&self) -> &dyn Any { - &self.raw as &dyn Any - } -} - -impl HasFileData for WebFormData { - #[cfg(feature = "file-engine")] - fn files(&self) -> Option> { - use super::file::WebFileEngine; - - let files = self - .element - .dyn_ref() - .and_then(|input: &web_sys::HtmlInputElement| { - input.files().and_then(|files| { - #[allow(clippy::arc_with_non_send_sync)] - WebFileEngine::new(files).map(|f| { - std::sync::Arc::new(f) as std::sync::Arc - }) - }) - }); - - files - } -} - -// web-sys does not expose the keys api for form data, so we need to manually bind to it -#[wasm_bindgen(inline_js = r#" -export function get_form_data(form) { - let values = new Map(); - const formData = new FormData(form); - - for (let name of formData.keys()) { - values.set(name, formData.getAll(name)); - } - - return values; -} -"#)] -extern "C" { - fn get_form_data(form: &web_sys::HtmlFormElement) -> js_sys::Map; -} - -// web-sys does not expose the keys api for select data, so we need to manually bind to it -#[wasm_bindgen(inline_js = r#" -export function get_select_data(select) { - let values = []; - for (let i = 0; i < select.options.length; i++) { - let option = select.options[i]; - if (option.selected) { - values.push(option.value.toString()); - } - } - - return values; -} -"#)] -extern "C" { - fn get_select_data(select: &web_sys::HtmlSelectElement) -> Vec; -} diff --git a/packages/web/src/event/image.rs b/packages/web/src/event/image.rs deleted file mode 100644 index 11c9d44787..0000000000 --- a/packages/web/src/event/image.rs +++ /dev/null @@ -1,26 +0,0 @@ -use std::any::Any; - -use dioxus_html::HasImageData; -use web_sys::Event; - -#[derive(Clone)] -pub struct WebImageEvent { - raw: Event, - error: bool, -} - -impl WebImageEvent { - pub fn new(raw: Event, error: bool) -> Self { - Self { raw, error } - } -} - -impl HasImageData for WebImageEvent { - fn load_error(&self) -> bool { - self.error - } - - fn as_any(&self) -> &dyn Any { - &self.raw as &dyn Any - } -} diff --git a/packages/web/src/event/resize.rs b/packages/web/src/event/resize.rs deleted file mode 100644 index 8b13789179..0000000000 --- a/packages/web/src/event/resize.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/packages/web/src/event/synthetic.rs b/packages/web/src/event/synthetic.rs deleted file mode 100644 index a3d7924f03..0000000000 --- a/packages/web/src/event/synthetic.rs +++ /dev/null @@ -1,622 +0,0 @@ -use dioxus_html::geometry::PixelsSize; -use dioxus_html::geometry::WheelDelta; -use dioxus_html::geometry::{ClientPoint, ElementPoint, PagePoint, ScreenPoint}; -use dioxus_html::input_data::{decode_key_location, decode_mouse_button_set, MouseButton}; -use dioxus_html::prelude::*; -use dioxus_html::HasFileData; -use dioxus_html::{events::HasKeyboardData, input_data::MouseButtonSet}; -use keyboard_types::{Code, Key, Modifiers}; -use std::str::FromStr; -use wasm_bindgen::JsCast; -use web_sys::{js_sys, ResizeObserverEntry}; -use web_sys::{ - AnimationEvent, CompositionEvent, Event, KeyboardEvent, MouseEvent, PointerEvent, Touch, - TouchEvent, TransitionEvent, WheelEvent, -}; - -/// A wrapper for the websys event that allows us to give it the impls from dioxus-html -pub struct Synthetic { - pub event: T, -} - -impl Synthetic { - pub fn new(event: T) -> Self { - Self { event } - } -} - -impl HasCompositionData for Synthetic { - fn data(&self) -> std::string::String { - self.event.data().unwrap_or_default() - } - - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl HasKeyboardData for Synthetic { - fn key(&self) -> Key { - Key::from_str(self.event.key().as_str()).unwrap_or(Key::Unidentified) - } - - fn code(&self) -> Code { - Code::from_str(self.event.code().as_str()).unwrap_or(Code::Unidentified) - } - - fn location(&self) -> keyboard_types::Location { - decode_key_location(self.event.location() as usize) - } - - fn is_auto_repeating(&self) -> bool { - self.event.repeat() - } - - fn is_composing(&self) -> bool { - self.event.is_composing() - } - - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl ModifiersInteraction for Synthetic { - fn modifiers(&self) -> Modifiers { - let mut modifiers = Modifiers::empty(); - - if self.event.alt_key() { - modifiers.insert(Modifiers::ALT); - } - if self.event.ctrl_key() { - modifiers.insert(Modifiers::CONTROL); - } - if self.event.meta_key() { - modifiers.insert(Modifiers::META); - } - if self.event.shift_key() { - modifiers.insert(Modifiers::SHIFT); - } - - modifiers - } -} - -impl InteractionLocation for Synthetic { - fn client_coordinates(&self) -> ClientPoint { - ClientPoint::new(self.event.client_x().into(), self.event.client_y().into()) - } - - fn page_coordinates(&self) -> PagePoint { - PagePoint::new(self.event.page_x().into(), self.event.page_y().into()) - } - - fn screen_coordinates(&self) -> ScreenPoint { - ScreenPoint::new(self.event.screen_x().into(), self.event.screen_y().into()) - } -} - -impl InteractionElementOffset for Synthetic { - fn element_coordinates(&self) -> ElementPoint { - ElementPoint::new(self.event.offset_x().into(), self.event.offset_y().into()) - } -} - -impl ModifiersInteraction for Synthetic { - fn modifiers(&self) -> Modifiers { - let mut modifiers = Modifiers::empty(); - - if self.event.alt_key() { - modifiers.insert(Modifiers::ALT); - } - if self.event.ctrl_key() { - modifiers.insert(Modifiers::CONTROL); - } - if self.event.meta_key() { - modifiers.insert(Modifiers::META); - } - if self.event.shift_key() { - modifiers.insert(Modifiers::SHIFT); - } - - modifiers - } -} - -impl PointerInteraction for Synthetic { - fn held_buttons(&self) -> MouseButtonSet { - decode_mouse_button_set(self.event.buttons()) - } - - fn trigger_button(&self) -> Option { - Some(MouseButton::from_web_code(self.event.button())) - } -} - -impl HasMouseData for Synthetic { - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl HasFileData for Synthetic {} - -impl HasDragData for Synthetic { - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl ModifiersInteraction for Synthetic { - fn modifiers(&self) -> Modifiers { - let mut modifiers = Modifiers::empty(); - - if self.event.alt_key() { - modifiers.insert(Modifiers::ALT); - } - if self.event.ctrl_key() { - modifiers.insert(Modifiers::CONTROL); - } - if self.event.meta_key() { - modifiers.insert(Modifiers::META); - } - if self.event.shift_key() { - modifiers.insert(Modifiers::SHIFT); - } - - modifiers - } -} - -impl HasTouchData for Synthetic { - fn touches(&self) -> Vec { - let touches = TouchEvent::touches(&self.event); - let mut result = Vec::with_capacity(touches.length() as usize); - for i in 0..touches.length() { - let touch = touches.get(i).unwrap(); - result.push(TouchPoint::new(Synthetic::new(touch))); - } - result - } - - fn touches_changed(&self) -> Vec { - let touches = self.event.changed_touches(); - let mut result = Vec::with_capacity(touches.length() as usize); - for i in 0..touches.length() { - let touch = touches.get(i).unwrap(); - result.push(TouchPoint::new(Synthetic::new(touch))); - } - result - } - - fn target_touches(&self) -> Vec { - let touches = self.event.target_touches(); - let mut result = Vec::with_capacity(touches.length() as usize); - for i in 0..touches.length() { - let touch = touches.get(i).unwrap(); - result.push(TouchPoint::new(Synthetic::new(touch))); - } - result - } - - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl HasTouchPointData for Synthetic { - fn identifier(&self) -> i32 { - self.event.identifier() - } - - fn radius(&self) -> ScreenPoint { - ScreenPoint::new(self.event.radius_x().into(), self.event.radius_y().into()) - } - - fn rotation(&self) -> f64 { - self.event.rotation_angle() as f64 - } - - fn force(&self) -> f64 { - self.event.force() as f64 - } - - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl InteractionLocation for Synthetic { - fn client_coordinates(&self) -> ClientPoint { - ClientPoint::new(self.event.client_x().into(), self.event.client_y().into()) - } - - fn screen_coordinates(&self) -> ScreenPoint { - ScreenPoint::new(self.event.screen_x().into(), self.event.screen_y().into()) - } - - fn page_coordinates(&self) -> PagePoint { - PagePoint::new(self.event.page_x().into(), self.event.page_y().into()) - } -} - -impl HasPointerData for Synthetic { - fn pointer_id(&self) -> i32 { - self.event.pointer_id() - } - - fn width(&self) -> i32 { - self.event.width() - } - - fn height(&self) -> i32 { - self.event.height() - } - - fn pressure(&self) -> f32 { - self.event.pressure() - } - - fn tangential_pressure(&self) -> f32 { - self.event.tangential_pressure() - } - - fn tilt_x(&self) -> i32 { - self.event.tilt_x() - } - - fn tilt_y(&self) -> i32 { - self.event.tilt_y() - } - - fn twist(&self) -> i32 { - self.event.twist() - } - - fn pointer_type(&self) -> String { - self.event.pointer_type() - } - - fn is_primary(&self) -> bool { - self.event.is_primary() - } - - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl InteractionLocation for Synthetic { - fn client_coordinates(&self) -> ClientPoint { - ClientPoint::new(self.event.client_x().into(), self.event.client_y().into()) - } - - fn screen_coordinates(&self) -> ScreenPoint { - ScreenPoint::new(self.event.screen_x().into(), self.event.screen_y().into()) - } - - fn page_coordinates(&self) -> PagePoint { - PagePoint::new(self.event.page_x().into(), self.event.page_y().into()) - } -} - -impl InteractionElementOffset for Synthetic { - fn element_coordinates(&self) -> ElementPoint { - ElementPoint::new(self.event.offset_x().into(), self.event.offset_y().into()) - } -} - -impl ModifiersInteraction for Synthetic { - fn modifiers(&self) -> Modifiers { - let mut modifiers = Modifiers::empty(); - - if self.event.alt_key() { - modifiers.insert(Modifiers::ALT); - } - if self.event.ctrl_key() { - modifiers.insert(Modifiers::CONTROL); - } - if self.event.meta_key() { - modifiers.insert(Modifiers::META); - } - if self.event.shift_key() { - modifiers.insert(Modifiers::SHIFT); - } - - modifiers - } -} - -impl PointerInteraction for Synthetic { - fn held_buttons(&self) -> MouseButtonSet { - decode_mouse_button_set(self.event.buttons()) - } - - fn trigger_button(&self) -> Option { - Some(MouseButton::from_web_code(self.event.button())) - } -} - -impl HasWheelData for Synthetic { - fn delta(&self) -> WheelDelta { - WheelDelta::from_web_attributes( - self.event.delta_mode(), - self.event.delta_x(), - self.event.delta_y(), - self.event.delta_z(), - ) - } - - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl HasMouseData for Synthetic { - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl InteractionLocation for Synthetic { - fn client_coordinates(&self) -> ClientPoint { - ClientPoint::new(self.event.client_x().into(), self.event.client_y().into()) - } - - fn screen_coordinates(&self) -> ScreenPoint { - ScreenPoint::new(self.event.screen_x().into(), self.event.screen_y().into()) - } - - fn page_coordinates(&self) -> PagePoint { - PagePoint::new(self.event.page_x().into(), self.event.page_y().into()) - } -} - -impl InteractionElementOffset for Synthetic { - fn element_coordinates(&self) -> ElementPoint { - ElementPoint::new(self.event.offset_x().into(), self.event.offset_y().into()) - } -} - -impl ModifiersInteraction for Synthetic { - fn modifiers(&self) -> Modifiers { - let mut modifiers = Modifiers::empty(); - - if self.event.alt_key() { - modifiers.insert(Modifiers::ALT); - } - if self.event.ctrl_key() { - modifiers.insert(Modifiers::CONTROL); - } - if self.event.meta_key() { - modifiers.insert(Modifiers::META); - } - if self.event.shift_key() { - modifiers.insert(Modifiers::SHIFT); - } - - modifiers - } -} - -impl PointerInteraction for Synthetic { - fn held_buttons(&self) -> MouseButtonSet { - decode_mouse_button_set(self.event.buttons()) - } - - fn trigger_button(&self) -> Option { - Some(MouseButton::from_web_code(self.event.button())) - } -} - -impl HasAnimationData for Synthetic { - fn animation_name(&self) -> String { - self.event.animation_name() - } - - fn pseudo_element(&self) -> String { - self.event.pseudo_element() - } - - fn elapsed_time(&self) -> f32 { - self.event.elapsed_time() - } - - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl HasTransitionData for Synthetic { - fn elapsed_time(&self) -> f32 { - self.event.elapsed_time() - } - - fn property_name(&self) -> String { - self.event.property_name() - } - - fn pseudo_element(&self) -> String { - self.event.pseudo_element() - } - - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -#[cfg(feature = "mounted")] -impl MountedElement for Synthetic { - fn as_any(&self) -> &dyn std::any::Any { - self - } - // fn get_scroll_offset( - // &self, - // ) -> std::pin::Pin>>> - // { - // let left = self.event.scroll_left(); - // let top = self.event.scroll_top(); - // let result = Ok(geometry::PixelsVector2D::new(left as f64, top as f64)); - // Box::pin(async { result }) - // } - - // fn get_scroll_size( - // &self, - // ) -> std::pin::Pin>>> - // { - // let width = self.event.scroll_width(); - // let height = self.event.scroll_height(); - // let result = Ok(geometry::PixelsSize::new(width as f64, height as f64)); - // Box::pin(async { result }) - // } - - // fn get_client_rect( - // &self, - // ) -> std::pin::Pin>>> - // { - // let rect = self.event.get_bounding_client_rect(); - // let result = Ok(geometry::PixelsRect::new( - // euclid::Point2D::new(rect.left(), rect.top()), - // euclid::Size2D::new(rect.width(), rect.height()), - // )); - // Box::pin(async { result }) - // } - - // fn as_any(&self) -> &dyn std::any::Any { - // self - // } - - // fn scroll_to( - // &self, - // behavior: ScrollBehavior, - // ) -> std::pin::Pin>>> { - // let options = web_sys::ScrollIntoViewOptions::new(); - // match behavior { - // ScrollBehavior::Instant => { - // options.set_behavior(web_sys::ScrollBehavior::Instant); - // } - // ScrollBehavior::Smooth => { - // options.set_behavior(web_sys::ScrollBehavior::Smooth); - // } - // } - // self.event - // .scroll_into_view_with_scroll_into_view_options(&options); - - // Box::pin(async { Ok(()) }) - // } - - // fn set_focus( - // &self, - // focus: bool, - // ) -> std::pin::Pin>>> { - // #[derive(Debug)] - // struct FocusError(wasm_bindgen::JsValue); - - // impl std::fmt::Display for FocusError { - // fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - // write!(f, "failed to focus element {:?}", self.event.0) - // } - // } - - // impl std::error::Error for FocusError {} - - // let result = self - // .dyn_ref::() - // .ok_or_else(|| MountedError::OperationFailed(Box::new(FocusError(self.event.into())))) - // .and_then(|e| { - // (if focus { e.focus() } else { e.blur() }) - // .map_err(|err| MountedError::OperationFailed(Box::new(FocusError(err)))) - // }); - // Box::pin(async { result }) - // } -} - -fn extract_first_size(resize_observer_output: js_sys::Array) -> ResizeResult { - let first = resize_observer_output.get(0); - let size = first.unchecked_into::(); - - // inline_size matches the width of the element if its writing-mode is horizontal, the height otherwise - let inline_size = size.inline_size(); - // block_size matches the height of the element if its writing-mode is horizontal, the width otherwise - let block_size = size.block_size(); - - Ok(PixelsSize::new(inline_size, block_size)) -} - -// impl From<&Event> for ResizeData { -// #[inline] -// fn from(e: &Event) -> Self { -// let e: &CustomEvent = e.unchecked_ref(); -// let value = e.detail(); -// Self::from(value.unchecked_into::()) -// } -// } - -impl HasResizeData for Synthetic { - fn as_any(&self) -> &dyn std::any::Any { - self - } - - fn get_border_box_size(&self) -> ResizeResult { - extract_first_size(self.event.border_box_size()) - } - - fn get_content_box_size(&self) -> ResizeResult { - extract_first_size(self.event.content_box_size()) - } -} - -impl HasScrollData for Synthetic { - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl HasClipboardData for Synthetic { - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl HasFocusData for Synthetic { - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl HasToggleData for Synthetic { - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl HasSelectionData for Synthetic { - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl HasMediaData for Synthetic { - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -impl HasFileData for Synthetic { - #[cfg(feature = "file-engine")] - fn files(&self) -> Option> { - let files = self - .event - .dyn_ref() - .and_then(|input: &web_sys::HtmlInputElement| { - input.files().and_then(|files| { - #[allow(clippy::arc_with_non_send_sync)] - super::file::WebFileEngine::new(files).map(|f| { - std::sync::Arc::new(f) as std::sync::Arc - }) - }) - }); - - files - } -} diff --git a/packages/web/src/hydration/deserialize.rs b/packages/web/src/hydration/deserialize.rs index 00fadb0a8e..f97677e51b 100644 --- a/packages/web/src/hydration/deserialize.rs +++ b/packages/web/src/hydration/deserialize.rs @@ -44,9 +44,9 @@ pub(crate) struct HTMLDataCursor { } impl HTMLDataCursor { - pub(crate) fn from_serialized(data: &[u8]) -> Option { - let deserialized: Vec>> = ciborium::from_reader(Cursor::new(data)).ok()?; - Some(Self::new(deserialized)) + pub(crate) fn from_serialized(data: &[u8]) -> Self { + let deserialized = ciborium::from_reader(Cursor::new(data)).unwrap(); + Self::new(deserialized) } /// Get the error if there is one diff --git a/packages/web/src/hydration/hydrate.rs b/packages/web/src/hydration/hydrate.rs index a10f97c384..a362eddef5 100644 --- a/packages/web/src/hydration/hydrate.rs +++ b/packages/web/src/hydration/hydrate.rs @@ -25,8 +25,6 @@ pub(crate) enum RehydrationError { SuspenseHydrationIdNotFound, /// The client tried to rehydrate a dom id that was not found on the server ElementNotFound, - /// Could not deserialize the data, might not be an issue? - MissingData, } #[derive(Debug)] @@ -140,8 +138,7 @@ impl WebsysDom { self.interpreter.base().push_root(node); } - let server_data = - HTMLDataCursor::from_serialized(&data).ok_or_else(|| RehydrationError::MissingData)?; + let server_data = HTMLDataCursor::from_serialized(&data); // If the server serialized an error into the suspense boundary, throw it on the client so that it bubbles up to the nearest error boundary if let Some(error) = server_data.error() { dom.in_runtime(|| id.throw_error(error)); diff --git a/packages/web/src/hydration/mod.rs b/packages/web/src/hydration/mod.rs index f7bc98dec4..a0e56e6943 100644 --- a/packages/web/src/hydration/mod.rs +++ b/packages/web/src/hydration/mod.rs @@ -5,7 +5,6 @@ mod hydrate; #[cfg(feature = "hydrate")] pub use deserialize::*; - #[cfg(feature = "hydrate")] #[allow(unused)] pub use hydrate::*; @@ -20,20 +19,3 @@ pub(crate) struct SuspenseMessage { /// The data to hydrate the suspense boundary with data: Vec, } - -#[cfg(feature = "hydrate")] -// Get the initial hydration data from the client -#[wasm_bindgen::prelude::wasm_bindgen(inline_js = r#" - export function get_initial_hydration_data() { - if (window.initial_dioxus_hydration_data === undefined) { - return new Uint8Array(); - } - - const decoded = atob(window.initial_dioxus_hydration_data); - return Uint8Array.from(decoded, (c) => c.charCodeAt(0)) - } -"#)] -extern "C" { - /// Get the initial hydration data from the client - pub fn get_initial_hydration_data() -> js_sys::Uint8Array; -} diff --git a/packages/web/src/launch.rs b/packages/web/src/launch.rs index 0c5059a92a..4cc421e9d8 100644 --- a/packages/web/src/launch.rs +++ b/packages/web/src/launch.rs @@ -19,11 +19,6 @@ pub fn launch( launch_virtual_dom(vdom, platform_config) } -/// Launch the web application with the given root component and config -pub fn launch_cfg(root: fn() -> Element, platform_config: Config) { - launch(root, Vec::new(), platform_config); -} - /// Launch the web application with a prebuild virtual dom /// /// For a builder API, see `LaunchBuilder` defined in the `dioxus` crate. @@ -32,3 +27,8 @@ pub fn launch_virtual_dom(vdom: VirtualDom, platform_config: Config) { crate::run(vdom, platform_config).await; }); } + +/// Launch the web application with the given root component and config +pub fn launch_cfg(root: fn() -> Element, platform_config: Config) { + launch(root, Vec::new(), platform_config); +} diff --git a/packages/web/src/lib.rs b/packages/web/src/lib.rs index 7018f92c2d..8e772cef70 100644 --- a/packages/web/src/lib.rs +++ b/packages/web/src/lib.rs @@ -47,7 +47,6 @@ pub use file_engine::*; mod devtools; mod hydration; - #[allow(unused)] pub use hydration::*; @@ -87,30 +86,30 @@ pub async fn run(mut virtual_dom: VirtualDom, web_config: Config) -> ! { if should_hydrate { #[cfg(feature = "hydrate")] { - let hydration_data = get_initial_hydration_data().to_vec(); - - if let Some(server_data) = HTMLDataCursor::from_serialized(&hydration_data) { - // If the server serialized an error into the root suspense boundary, throw it into the root scope - if let Some(error) = server_data.error() { - virtual_dom.in_runtime(|| dioxus_core::ScopeId::APP.throw_error(error)); + websys_dom.skip_mutations = true; + // Get the initial hydration data from the client + #[wasm_bindgen::prelude::wasm_bindgen(inline_js = r#" + export function get_initial_hydration_data() { + const decoded = atob(window.initial_dioxus_hydration_data); + return Uint8Array.from(decoded, (c) => c.charCodeAt(0)) } - - websys_dom.skip_mutations = true; - with_server_data(server_data, || { - virtual_dom.rebuild(&mut websys_dom); - }); - websys_dom.skip_mutations = false; - - let rx = websys_dom - .rehydrate(&virtual_dom) - .expect("Failed to rehydrate"); - - hydration_receiver = Some(rx); - } else { - tracing::error!("Hydration data is missing"); - virtual_dom.rebuild(&mut websys_dom); - websys_dom.flush_edits(); + "#)] + extern "C" { + fn get_initial_hydration_data() -> js_sys::Uint8Array; } + let hydration_data = get_initial_hydration_data().to_vec(); + let server_data = HTMLDataCursor::from_serialized(&hydration_data); + // If the server serialized an error into the root suspense boundary, throw it into the root scope + if let Some(error) = server_data.error() { + virtual_dom.in_runtime(|| dioxus_core::ScopeId::APP.throw_error(error)); + } + with_server_data(server_data, || { + virtual_dom.rebuild(&mut websys_dom); + }); + websys_dom.skip_mutations = false; + + let rx = websys_dom.rehydrate(&virtual_dom).unwrap(); + hydration_receiver = Some(rx); } #[cfg(not(feature = "hydrate"))] { @@ -193,10 +192,23 @@ pub async fn run(mut virtual_dom: VirtualDom, web_config: Config) -> ! { websys_dom.rehydrate_streaming(hydration_data, &mut virtual_dom); } + // Todo: This is currently disabled because it has a negative impact on response times for events but it could be re-enabled for tasks + // Jank free rendering + // + // 1. wait for the browser to give us "idle" time + // 2. During idle time, diff the dom + // 3. Stop diffing if the deadline is exceeded + // 4. Wait for the animation frame to patch the dom + + // wait for the mainthread to schedule us in + // let deadline = work_loop.wait_for_idle_time().await; + // run the virtualdom work phase until the frame deadline is reached virtual_dom.render_immediate(&mut websys_dom); - // Flush all pending edits to the dom in one swoop + // wait for the animation frame to fire so we can apply our changes + // work_loop.wait_for_raf().await; + websys_dom.flush_edits(); } } diff --git a/packages/web/src/web_sys_bind/file_engine.rs b/packages/web/src/web_sys_bind/file_engine.rs deleted file mode 100644 index f0a0ff6988..0000000000 --- a/packages/web/src/web_sys_bind/file_engine.rs +++ /dev/null @@ -1,133 +0,0 @@ -use std::any::Any; - -use dioxus_html::FileEngine; -use futures_channel::oneshot; -use js_sys::Uint8Array; -use wasm_bindgen::{prelude::Closure, JsCast}; -use web_sys::{File, FileList, FileReader}; - -/// A file engine for the web platform -pub struct WebFileEngine { - file_reader: FileReader, - file_list: FileList, -} - -impl WebFileEngine { - /// Create a new file engine from a file list - pub fn new(file_list: FileList) -> Option { - Some(Self { - file_list, - file_reader: FileReader::new().ok()?, - }) - } - - fn len(&self) -> usize { - self.file_list.length() as usize - } - - fn get(&self, index: usize) -> Option { - self.file_list.item(index as u32) - } - - fn find(&self, name: &str) -> Option { - (0..self.len()) - .filter_map(|i| self.get(i)) - .find(|f| f.name() == name) - } -} - -#[async_trait::async_trait(?Send)] -impl FileEngine for WebFileEngine { - fn files(&self) -> Vec { - (0..self.len()) - .filter_map(|i| self.get(i).map(|f| f.name())) - .collect() - } - - async fn file_size(&self, file: &str) -> Option { - let file = self.find(file)?; - Some(file.size() as u64) - } - - // read a file to bytes - async fn read_file(&self, file: &str) -> Option> { - let file = self.find(file)?; - - let file_reader = self.file_reader.clone(); - let (rx, tx) = oneshot::channel(); - let on_load: Closure = Closure::new({ - let mut rx = Some(rx); - move || { - let result = file_reader.result(); - let _ = rx - .take() - .expect("multiple files read without refreshing the channel") - .send(result); - } - }); - - self.file_reader - .set_onload(Some(on_load.as_ref().unchecked_ref())); - on_load.forget(); - self.file_reader.read_as_array_buffer(&file).ok()?; - - if let Ok(Ok(js_val)) = tx.await { - let as_u8_arr = Uint8Array::new(&js_val); - let as_u8_vec = as_u8_arr.to_vec(); - - Some(as_u8_vec) - } else { - None - } - } - - // read a file to string - async fn read_file_to_string(&self, file: &str) -> Option { - let file = self.find(file)?; - - let file_reader = self.file_reader.clone(); - let (rx, tx) = oneshot::channel(); - let on_load: Closure = Closure::new({ - let mut rx = Some(rx); - move || { - let result = file_reader.result(); - let _ = rx - .take() - .expect("multiple files read without refreshing the channel") - .send(result); - } - }); - - self.file_reader - .set_onload(Some(on_load.as_ref().unchecked_ref())); - on_load.forget(); - self.file_reader.read_as_text(&file).ok()?; - - if let Ok(Ok(js_val)) = tx.await { - js_val.as_string() - } else { - None - } - } - - async fn get_native_file(&self, file: &str) -> Option> { - let file = self.find(file)?; - Some(Box::new(file)) - } -} - -/// Helper trait for WebFileEngine -#[async_trait::async_trait(?Send)] -pub trait WebFileEngineExt { - /// returns web_sys::File - async fn get_web_file(&self, file: &str) -> Option; -} - -#[async_trait::async_trait(?Send)] -impl WebFileEngineExt for std::sync::Arc { - async fn get_web_file(&self, file: &str) -> Option { - let native_file = self.get_native_file(file).await?; - let ret = native_file.downcast::().ok()?; - Some(*ret) - } -} From 0a635a1d748ea4ae4231169de688b56faf426196 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Fri, 4 Oct 2024 08:17:41 -0500 Subject: [PATCH 125/139] move more of the head element logic into the new crate --- Cargo.lock | 13 +++ Cargo.toml | 47 ++++----- packages/document/docs/eval.md | 125 +++++++++++++++++++++++ packages/document/docs/head.md | 59 +++++++++++ packages/document/src/elements/link.rs | 123 ++++++++++++++++++++++ packages/document/src/elements/meta.rs | 68 ++++++++++++ packages/document/src/elements/mod.rs | 102 ++++++++++++++++++ packages/document/src/elements/script.rs | 110 ++++++++++++++++++++ packages/document/src/elements/style.rs | 90 ++++++++++++++++ packages/document/src/elements/title.rs | 52 ++++++++++ packages/document/src/eval.rs | 2 +- packages/document/src/lib.rs | 5 +- packages/document/src/meta.rs | 0 packages/document/src/script.rs | 0 packages/document/src/title.rs | 0 packages/html/src/document/mod.rs | 33 ------ 16 files changed, 769 insertions(+), 60 deletions(-) create mode 100644 packages/document/docs/eval.md create mode 100644 packages/document/docs/head.md create mode 100644 packages/document/src/elements/link.rs create mode 100644 packages/document/src/elements/meta.rs create mode 100644 packages/document/src/elements/mod.rs create mode 100644 packages/document/src/elements/script.rs create mode 100644 packages/document/src/elements/style.rs create mode 100644 packages/document/src/elements/title.rs delete mode 100644 packages/document/src/meta.rs delete mode 100644 packages/document/src/script.rs delete mode 100644 packages/document/src/title.rs diff --git a/Cargo.lock b/Cargo.lock index 22c4eb7859..891038012d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2626,6 +2626,19 @@ dependencies = [ "serde", ] +[[package]] +name = "dioxus-document" +version = "0.6.0-alpha.2" +dependencies = [ + "dioxus-core", + "dioxus-core-macro", + "dioxus-core-types", + "futures-channel", + "serde", + "serde_json", + "tracing", +] + [[package]] name = "dioxus-examples" version = "0.6.0-alpha.2" diff --git a/Cargo.toml b/Cargo.toml index b408c9d09e..cf06fe0ca9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,39 +1,40 @@ [workspace] resolver = "2" members = [ - "packages/dioxus", - "packages/dioxus-lib", - "packages/core", - "packages/core-types", - "packages/cli", + "packages/autofmt", + "packages/check", "packages/cli-config", - "packages/core-macro", + "packages/cli", "packages/config-macro", - "packages/router-macro", + "packages/core-macro", + "packages/core-types", + "packages/core", + "packages/desktop", + "packages/devtools-types", + "packages/devtools", + "packages/dioxus-lib", + "packages/dioxus", + "packages/document", "packages/extension", - "packages/router", - "packages/html", - "packages/html-internal-macro", + "packages/fullstack", + "packages/generational-box", "packages/hooks", - "packages/web", - "packages/ssr", - "packages/desktop", - "packages/mobile", + "packages/html-internal-macro", + "packages/html", "packages/interpreter", + "packages/lazy-js-bundle", "packages/liveview", - "packages/autofmt", - "packages/check", - "packages/rsx", + "packages/mobile", + "packages/router-macro", + "packages/router", "packages/rsx-hotreload", "packages/rsx-rosetta", - "packages/generational-box", - "packages/signals", - "packages/devtools", - "packages/devtools-types", - "packages/fullstack", + "packages/rsx", "packages/server-macro", + "packages/signals", + "packages/ssr", "packages/static-generation", - "packages/lazy-js-bundle", + "packages/web", # Full project examples "example-projects/fullstack-hackernews", diff --git a/packages/document/docs/eval.md b/packages/document/docs/eval.md new file mode 100644 index 0000000000..673a13f14d --- /dev/null +++ b/packages/document/docs/eval.md @@ -0,0 +1,125 @@ +# Communicating with JavaScript + +You can use the `eval` function to execute JavaScript code in your application with the desktop, mobile, web or liveview renderers. Eval takes a block of JavaScript code (that may be asynchronous) and returns a `UseEval` object that you can use to send data to the JavaScript code and receive data from it. + +
      + +## Safety + +Please be careful when executing JavaScript code with `eval`. You should only execute code that you trust. **This applies especially to web targets, where the JavaScript context has access to most, if not all of your application data.** Running untrusted code can lead to a [cross-site scripting](https://developer.mozilla.org/en-US/docs/Glossary/Cross-site_scripting) (XSS) vulnerability. + +
      + +```rust +use dioxus::prelude::*; + +fn App() -> Element { + rsx! { + button { + onclick: move |_| async move { + // Eval is a global function you can use anywhere inside Dioxus. It will execute the given JavaScript code. + let result = eval(r#"console.log("Hello World"); + return "Hello World";"#); + + // You can use the `await` keyword to wait for the result of the JavaScript code. + println!("{:?}", result.await); + }, + "Log Hello World" + } + } +} +``` + +## Sending data to JavaScript + +When you execute JavaScript code with `eval`, you can pass data to it by formatting the value into the JavaScript code or sending values to the `UseEval` channel. + +```rust +use dioxus::prelude::*; + +fn app() -> Element { + rsx! { + button { + onclick: move |_| { + // You can pass initial data to the eval function by formatting it into the JavaScript code. + const LOOP_COUNT: usize = 10; + let eval = eval(&format!(r#"for(let i = 0; i < {LOOP_COUNT}; i++) {{ + // You can receive values asynchronously with the the `await dioxus.recv()` method. + let value = await dioxus.recv(); + console.log("Received", value); + }}"#)); + + // You can send values from rust to the JavaScript code with the `send` method on the object returned by `eval`. + for i in 0..LOOP_COUNT { + eval.send(i.into()).unwrap(); + } + }, + "Log Count" + } + } +} +``` + +## Sending data from JavaScript + +The `UseEval` struct also contains methods for receiving values you send from JavaScript. You can use the `dioxus.send()` method to send values to the JavaScript code and the `UseEval::recv()` method to receive values from the JavaScript code. + +```rust +use dioxus::prelude::*; + +fn app() -> Element { + rsx! { + button { + onclick: move |_| async move { + // You can send values from rust to the JavaScript code by using the `send` method on the object returned by `eval`. + let mut eval = eval(r#"for(let i = 0; i < 10; i++) { + // You can send values asynchronously with the `dioxus.send()` method. + dioxus.send(i); + }"#); + + // You can receive values from the JavaScript code with the `recv` method on the object returned by `eval`. + for _ in 0..10 { + let value = eval.recv().await.unwrap(); + println!("Received {}", value); + } + }, + "Log Count" + } + } +} +``` + +## Interacting with the DOM with Eval + +You can also use the `eval` function to execute JavaScript code that reads or modifies the DOM. If you want to interact with the mounted DOM, you need to use `eval` inside the [`dioxus_hooks::use_effect`] hook which runs after the component has been mounted. + +```rust +use dioxus::prelude::*; + +const SCRIPT: &str = r#" + let element = document.getElementById("my-element"); + element.innerHTML = "Hello World"; + return element.getAttribute("data-count"); +"#; + +fn app() -> Element { + // ❌ You shouldn't run eval in the body of a component. This will run before the component has been mounted + // eval(SCRIPT); + + // ✅ You should run eval inside an effect or event. This will run after the component has been mounted + use_effect(move || { + spawn(async { + let count = eval(SCRIPT).await; + println!("Count is {:?}", count); + }); + }); + + + rsx! { + div { + id: "my-element", + "data-count": "123", + } + } +} +``` diff --git a/packages/document/docs/head.md b/packages/document/docs/head.md new file mode 100644 index 0000000000..94dcb99374 --- /dev/null +++ b/packages/document/docs/head.md @@ -0,0 +1,59 @@ +# Modifying the Head + +Dioxus includes a series of components that render into the head of the page: + +- [Title](crate::Title) +- [Meta](crate::Meta) +- [head::Link](crate::head::Link) +- [Script](crate::Script) +- [Style](crate::Style) + +Each of these components can be used to add extra information to the head of the page. For example, you can use the `Title` component to set the title of the page, or the `Meta` component to add extra metadata to the page. + +## Limitations + +Components that render into the head of the page do have a few key limitations: + +- With the exception of the `Title` component, all components that render into the head cannot be modified after the first time they are rendered. +- Components that render into the head will not be removed even after the component is removed from the tree. + +## Example + +```rust, no_run +# use dioxus::prelude::*; +fn RedirectToDioxusHomepageWithoutJS() -> Element { + rsx! { + // You can use the meta component to render a meta tag into the head of the page + // This meta tag will redirect the user to the dioxuslabs homepage in 10 seconds + Meta { + http_equiv: "refresh", + content: "10;url=https://dioxuslabs.com", + } + } +} +``` + +## Fullstack Rendering + +Head components are compatible with fullstack rendering, but only head components that are rendered in the initial render (before suspense boundaries resolve) will be rendered into the head. + +If you have any important metadata that you want to render into the head, make sure to render it outside of any pending suspense boundaries. + +```rust, no_run +# use dioxus::prelude::*; +# #[component] +# fn LoadData(children: Element) -> Element { unimplemented!() } +fn App() -> Element { + rsx! { + // This will render in SSR + Title { "My Page" } + SuspenseBoundary { + fallback: |_| rsx! { "Loading..." }, + LoadData { + // This will only be rendered on the client after hydration so it may not be visible to search engines + Meta { name: "description", content: "My Page" } + } + } + } +} +``` diff --git a/packages/document/src/elements/link.rs b/packages/document/src/elements/link.rs new file mode 100644 index 0000000000..05fec6f468 --- /dev/null +++ b/packages/document/src/elements/link.rs @@ -0,0 +1,123 @@ +use crate::document; + +use super::*; + +#[derive(Clone, Props, PartialEq)] +pub struct LinkProps { + pub rel: Option, + pub media: Option, + pub title: Option, + pub disabled: Option, + pub r#as: Option, + pub sizes: Option, + /// Links are deduplicated by their href attribute + pub href: Option, + pub crossorigin: Option, + pub referrerpolicy: Option, + pub fetchpriority: Option, + pub hreflang: Option, + pub integrity: Option, + pub r#type: Option, + pub blocking: Option, +} + +impl LinkProps { + pub(crate) fn attributes(&self) -> Vec<(&'static str, String)> { + let mut attributes = Vec::new(); + if let Some(rel) = &self.rel { + attributes.push(("rel", rel.clone())); + } + if let Some(media) = &self.media { + attributes.push(("media", media.clone())); + } + if let Some(title) = &self.title { + attributes.push(("title", title.clone())); + } + if let Some(disabled) = &self.disabled { + attributes.push(("disabled", disabled.to_string())); + } + if let Some(r#as) = &self.r#as { + attributes.push(("as", r#as.clone())); + } + if let Some(sizes) = &self.sizes { + attributes.push(("sizes", sizes.clone())); + } + if let Some(href) = &self.href { + attributes.push(("href", href.clone())); + } + if let Some(crossorigin) = &self.crossorigin { + attributes.push(("crossOrigin", crossorigin.clone())); + } + if let Some(referrerpolicy) = &self.referrerpolicy { + attributes.push(("referrerPolicy", referrerpolicy.clone())); + } + if let Some(fetchpriority) = &self.fetchpriority { + attributes.push(("fetchPriority", fetchpriority.clone())); + } + if let Some(hreflang) = &self.hreflang { + attributes.push(("hrefLang", hreflang.clone())); + } + if let Some(integrity) = &self.integrity { + attributes.push(("integrity", integrity.clone())); + } + if let Some(r#type) = &self.r#type { + attributes.push(("type", r#type.clone())); + } + if let Some(blocking) = &self.blocking { + attributes.push(("blocking", blocking.clone())); + } + attributes + } +} + +/// Render a [`link`](crate::elements::link) tag into the head of the page. +/// +/// > The [Link](https://docs.rs/dioxus-router/latest/dioxus_router/components/fn.Link.html) component in dioxus router and this component are completely different. +/// > This component links resources in the head of the page, while the router component creates clickable links in the body of the page. +/// +/// # Example +/// ```rust, no_run +/// # use dioxus::prelude::*; +/// fn RedBackground() -> Element { +/// rsx! { +/// // You can use the meta component to render a meta tag into the head of the page +/// // This meta tag will redirect the user to the dioxuslabs homepage in 10 seconds +/// head::Link { +/// href: asset!("./assets/style.css"), +/// rel: "stylesheet", +/// } +/// } +/// } +/// ``` +/// +///
      +/// +/// Any updates to the props after the first render will not be reflected in the head. +/// +///
      +#[doc(alias = "")] +#[component] +pub fn Link(props: LinkProps) -> Element { + use_update_warning(&props, "Link {}"); + + use_hook(|| { + if let Some(href) = &props.href { + if !should_insert_link(href) { + return; + } + } + let document = document(); + document.create_link(props); + }); + + VNode::empty() +} + +#[derive(Default, Clone)] +struct LinkContext(DeduplicationContext); + +fn should_insert_link(href: &str) -> bool { + get_or_insert_root_context::() + .0 + .should_insert(href) +} diff --git a/packages/document/src/elements/meta.rs b/packages/document/src/elements/meta.rs new file mode 100644 index 0000000000..ea695c6082 --- /dev/null +++ b/packages/document/src/elements/meta.rs @@ -0,0 +1,68 @@ +use super::*; + +/// Props for the [`Meta`] component +#[derive(Clone, Props, PartialEq)] +pub struct MetaProps { + pub property: Option, + pub name: Option, + pub charset: Option, + pub http_equiv: Option, + pub content: Option, +} + +impl MetaProps { + pub(crate) fn attributes(&self) -> Vec<(&'static str, String)> { + let mut attributes = Vec::new(); + if let Some(property) = &self.property { + attributes.push(("property", property.clone())); + } + if let Some(name) = &self.name { + attributes.push(("name", name.clone())); + } + if let Some(charset) = &self.charset { + attributes.push(("charset", charset.clone())); + } + if let Some(http_equiv) = &self.http_equiv { + attributes.push(("http-equiv", http_equiv.clone())); + } + if let Some(content) = &self.content { + attributes.push(("content", content.clone())); + } + attributes + } +} + +/// Render a [`meta`](crate::elements::meta) tag into the head of the page. +/// +/// # Example +/// +/// ```rust, no_run +/// # use dioxus::prelude::*; +/// fn RedirectToDioxusHomepageWithoutJS() -> Element { +/// rsx! { +/// // You can use the meta component to render a meta tag into the head of the page +/// // This meta tag will redirect the user to the dioxuslabs homepage in 10 seconds +/// Meta { +/// http_equiv: "refresh", +/// content: "10;url=https://dioxuslabs.com", +/// } +/// } +/// } +/// ``` +/// +///
      +/// +/// Any updates to the props after the first render will not be reflected in the head. +/// +///
      +#[component] +pub fn Meta(props: MetaProps) -> Element { + use_update_warning(&props, "Meta {}"); + + use_hook(|| { + let document = crate::document(); + document.create_meta(props); + }); + + VNode::empty() +} diff --git a/packages/document/src/elements/mod.rs b/packages/document/src/elements/mod.rs new file mode 100644 index 0000000000..62f5c4ad9e --- /dev/null +++ b/packages/document/src/elements/mod.rs @@ -0,0 +1,102 @@ +#![doc = include_str!("../../docs/head.md")] + +use std::{cell::RefCell, collections::HashSet, rc::Rc}; + +use dioxus_core::{prelude::*, DynamicNode}; +use dioxus_core_macro::*; + +mod link; +pub use link::*; +mod meta; +pub use meta::*; +mod script; +pub use script::*; +mod style; +pub use style::*; +mod title; +pub use title::*; + +/// Warn the user if they try to change props on a element that is injected into the head +#[allow(unused)] +fn use_update_warning(value: &T, name: &'static str) { + #[cfg(debug_assertions)] + { + let cloned_value = value.clone(); + let initial = use_hook(move || value.clone()); + + if initial != cloned_value { + tracing::warn!("Changing the props of `{name}` is not supported "); + } + } +} + +fn extract_single_text_node(children: &Element, component: &str) -> Option { + let vnode = match children { + Element::Ok(vnode) => vnode, + Element::Err(err) => { + tracing::error!("Error while rendering {component}: {err}"); + return None; + } + }; + // The title's children must be in one of two forms: + // 1. rsx! { "static text" } + // 2. rsx! { "title: {dynamic_text}" } + match vnode.template { + // rsx! { "static text" } + Template { + roots: &[TemplateNode::Text { text }], + node_paths: &[], + attr_paths: &[], + .. + } => Some(text.to_string()), + // rsx! { "title: {dynamic_text}" } + Template { + roots: &[TemplateNode::Dynamic { id }], + node_paths: &[&[0]], + attr_paths: &[], + .. + } => { + let node = &vnode.dynamic_nodes[id]; + match node { + DynamicNode::Text(text) => Some(text.value.clone()), + _ => { + tracing::error!("Error while rendering {component}: The children of {component} must be a single text node. It cannot be a component, if statement, loop, or a fragment"); + None + } + } + } + _ => { + tracing::error!( + "Error while rendering title: The children of title must be a single text node" + ); + None + } + } +} + +fn get_or_insert_root_context() -> T { + match ScopeId::ROOT.has_context::() { + Some(context) => context, + None => { + let context = T::default(); + ScopeId::ROOT.provide_context(context.clone()); + context + } + } +} + +#[derive(Default, Clone)] +struct DeduplicationContext(Rc>>); + +impl DeduplicationContext { + fn should_insert(&self, href: &str) -> bool { + let mut set = self.0.borrow_mut(); + let present = set.contains(href); + if !present { + set.insert(href.to_string()); + true + } else { + false + } + } +} diff --git a/packages/document/src/elements/script.rs b/packages/document/src/elements/script.rs new file mode 100644 index 0000000000..03131151b0 --- /dev/null +++ b/packages/document/src/elements/script.rs @@ -0,0 +1,110 @@ +use crate::document; + +use super::*; + +#[derive(Clone, Props, PartialEq)] +pub struct ScriptProps { + /// The contents of the script tag. If present, the children must be a single text node. + pub children: Element, + /// Scripts are deduplicated by their src attribute + pub src: Option, + pub defer: Option, + pub crossorigin: Option, + pub fetchpriority: Option, + pub integrity: Option, + pub nomodule: Option, + pub nonce: Option, + pub referrerpolicy: Option, + pub r#type: Option, +} + +impl ScriptProps { + pub(crate) fn attributes(&self) -> Vec<(&'static str, String)> { + let mut attributes = Vec::new(); + if let Some(defer) = &self.defer { + attributes.push(("defer", defer.to_string())); + } + if let Some(crossorigin) = &self.crossorigin { + attributes.push(("crossorigin", crossorigin.clone())); + } + if let Some(fetchpriority) = &self.fetchpriority { + attributes.push(("fetchpriority", fetchpriority.clone())); + } + if let Some(integrity) = &self.integrity { + attributes.push(("integrity", integrity.clone())); + } + if let Some(nomodule) = &self.nomodule { + attributes.push(("nomodule", nomodule.to_string())); + } + if let Some(nonce) = &self.nonce { + attributes.push(("nonce", nonce.clone())); + } + if let Some(referrerpolicy) = &self.referrerpolicy { + attributes.push(("referrerpolicy", referrerpolicy.clone())); + } + if let Some(r#type) = &self.r#type { + attributes.push(("type", r#type.clone())); + } + if let Some(src) = &self.src { + attributes.push(("src", src.clone())); + } + attributes + } + + pub fn script_contents(&self) -> Option { + extract_single_text_node(&self.children, "Script") + } +} + +/// Render a [`script`](crate::elements::script) tag into the head of the page. +/// +/// +/// If present, the children of the script component must be a single static or formatted string. If there are more children or the children contain components, conditionals, loops, or fragments, the script will not be added. +/// +/// +/// Any scripts you add will be deduplicated by their `src` attribute (if present). +/// +/// # Example +/// ```rust, no_run +/// # use dioxus::prelude::*; +/// fn LoadScript() -> Element { +/// rsx! { +/// // You can use the Script component to render a script tag into the head of the page +/// Script { +/// src: asset!("./assets/script.js"), +/// } +/// } +/// } +/// ``` +/// +///
      +/// +/// Any updates to the props after the first render will not be reflected in the head. +/// +///
      +#[component] +pub fn Script(props: ScriptProps) -> Element { + use_update_warning(&props, "Script {}"); + + use_hook(|| { + if let Some(src) = &props.src { + if !should_insert_script(src) { + return; + } + } + + let document = document(); + document.create_script(props); + }); + + VNode::empty() +} + +#[derive(Default, Clone)] +struct ScriptContext(DeduplicationContext); + +fn should_insert_script(src: &str) -> bool { + get_or_insert_root_context::() + .0 + .should_insert(src) +} diff --git a/packages/document/src/elements/style.rs b/packages/document/src/elements/style.rs new file mode 100644 index 0000000000..178848c1e6 --- /dev/null +++ b/packages/document/src/elements/style.rs @@ -0,0 +1,90 @@ +use crate::document; + +use super::*; + +#[derive(Clone, Props, PartialEq)] +pub struct StyleProps { + /// Styles are deduplicated by their href attribute + pub href: Option, + pub media: Option, + pub nonce: Option, + pub title: Option, + /// The contents of the style tag. If present, the children must be a single text node. + pub children: Element, +} + +impl StyleProps { + pub(crate) fn attributes(&self) -> Vec<(&'static str, String)> { + let mut attributes = Vec::new(); + if let Some(href) = &self.href { + attributes.push(("href", href.clone())); + } + if let Some(media) = &self.media { + attributes.push(("media", media.clone())); + } + if let Some(nonce) = &self.nonce { + attributes.push(("nonce", nonce.clone())); + } + if let Some(title) = &self.title { + attributes.push(("title", title.clone())); + } + attributes + } + + pub fn style_contents(&self) -> Option { + extract_single_text_node(&self.children, "Title") + } +} + +/// Render a [`style`](crate::elements::style) tag into the head of the page. +/// +/// If present, the children of the style component must be a single static or formatted string. If there are more children or the children contain components, conditionals, loops, or fragments, the style will not be added. +/// +/// # Example +/// ```rust, no_run +/// # use dioxus::prelude::*; +/// fn RedBackground() -> Element { +/// rsx! { +/// // You can use the style component to render a style tag into the head of the page +/// // This style tag will set the background color of the page to red +/// Style { +/// r#" +/// body {{ +/// background-color: red; +/// }} +/// "# +/// } +/// } +/// } +/// ``` +/// +///
      +/// +/// Any updates to the props after the first render will not be reflected in the head. +/// +///
      +#[component] +pub fn Style(props: StyleProps) -> Element { + use_update_warning(&props, "Style {}"); + + use_hook(|| { + if let Some(href) = &props.href { + if !should_insert_style(href) { + return; + } + } + let document = document(); + document.create_style(props); + }); + + VNode::empty() +} + +#[derive(Default, Clone)] +struct StyleContext(DeduplicationContext); + +fn should_insert_style(href: &str) -> bool { + get_or_insert_root_context::() + .0 + .should_insert(href) +} diff --git a/packages/document/src/elements/title.rs b/packages/document/src/elements/title.rs new file mode 100644 index 0000000000..9b4a65a82c --- /dev/null +++ b/packages/document/src/elements/title.rs @@ -0,0 +1,52 @@ +use crate::document; + +use super::*; + +#[derive(Clone, Props, PartialEq)] +pub struct TitleProps { + /// The contents of the title tag. The children must be a single text node. + children: Element, +} + +/// Render the title of the page. On web renderers, this will set the [title](crate::elements::title) in the head. On desktop, it will set the window title. +/// +/// Unlike most head components, the Title can be modified after the first render. Only the latest update to the title will be reflected if multiple title components are rendered. +/// +/// +/// The children of the title component must be a single static or formatted string. If there are more children or the children contain components, conditionals, loops, or fragments, the title will not be updated. +/// +/// # Example +/// +/// ```rust, no_run +/// # use dioxus::prelude::*; +/// fn App() -> Element { +/// rsx! { +/// // You can use the Title component to render a title tag into the head of the page or window +/// Title { "My Page" } +/// } +/// } +/// ``` +#[component] +pub fn Title(props: TitleProps) -> Element { + let children = props.children; + let Some(text) = extract_single_text_node(&children, "Title") else { + return VNode::empty(); + }; + + // Update the title as it changes. NOTE: We don't use use_effect here because we need this to run on the server + let document = use_hook(document); + let last_text = use_hook(|| { + // Set the title initially + document.set_title(text.clone()); + Rc::new(RefCell::new(text.clone())) + }); + + // If the text changes, update the title + let mut last_text = last_text.borrow_mut(); + if text != *last_text { + document.set_title(text.clone()); + *last_text = text; + } + + VNode::empty() +} diff --git a/packages/document/src/eval.rs b/packages/document/src/eval.rs index 9d40cda64c..bc0a79c31c 100644 --- a/packages/document/src/eval.rs +++ b/packages/document/src/eval.rs @@ -37,7 +37,7 @@ impl Eval { pub async fn recv_as(self) -> Result { let res = self.recv().await?; - serde_json::from_value(res).map_err(|e| EvalError::Deserialization(e)) + serde_json::from_value(res).map_err(EvalError::Deserialization) } } diff --git a/packages/document/src/lib.rs b/packages/document/src/lib.rs index 01b41d4f4c..ca53906ebd 100644 --- a/packages/document/src/lib.rs +++ b/packages/document/src/lib.rs @@ -1,15 +1,14 @@ use std::rc::Rc; mod document; +mod elements; mod error; mod eval; -mod head; -mod title; pub use document::*; +pub use elements::*; pub use error::*; pub use eval::*; -pub use head::*; /// Get the document provider for the current platform or a no-op provider if the platform doesn't document functionality. pub fn document() -> Rc { diff --git a/packages/document/src/meta.rs b/packages/document/src/meta.rs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/packages/document/src/script.rs b/packages/document/src/script.rs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/packages/document/src/title.rs b/packages/document/src/title.rs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/packages/html/src/document/mod.rs b/packages/html/src/document/mod.rs index f142005966..4f145f1533 100644 --- a/packages/html/src/document/mod.rs +++ b/packages/html/src/document/mod.rs @@ -44,39 +44,6 @@ pub trait Document { /// Create a new evaluator for the document that evaluates JavaScript and facilitates communication between JavaScript and Rust. fn new_evaluator(&self, js: String) -> GenerationalBox>; - /// Set the title of the document - fn set_title(&self, title: String) { - self.new_evaluator(format!("document.title = {title:?};")); - } - - /// Create a new meta tag - fn create_meta(&self, props: MetaProps) { - let attributes = props.attributes(); - let js = create_element_in_head("meta", &attributes, None); - self.new_evaluator(js); - } - - /// Create a new script tag - fn create_script(&self, props: ScriptProps) { - let attributes = props.attributes(); - let js = create_element_in_head("script", &attributes, props.script_contents()); - self.new_evaluator(js); - } - - /// Create a new style tag - fn create_style(&self, props: StyleProps) { - let attributes = props.attributes(); - let js = create_element_in_head("style", &attributes, props.style_contents()); - self.new_evaluator(js); - } - - /// Create a new link tag - fn create_link(&self, props: head::LinkProps) { - let attributes = props.attributes(); - let js = create_element_in_head("link", &attributes, None); - self.new_evaluator(js); - } - /// Get a reference to the document as `dyn Any` fn as_any(&self) -> &dyn std::any::Any; } From 7da7ba1b0b4ca6bdfa53dc0626e5a151c51389a7 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Fri, 4 Oct 2024 08:29:49 -0500 Subject: [PATCH 126/139] merge latest bug fixes into the new head components --- Cargo.lock | 1 + packages/document/Cargo.toml | 1 + packages/document/src/document.rs | 79 +++- packages/document/src/elements/link.rs | 8 +- packages/document/src/elements/meta.rs | 9 +- packages/document/src/elements/mod.rs | 52 ++- packages/document/src/elements/script.rs | 12 +- packages/document/src/elements/style.rs | 12 +- packages/document/src/elements/title.rs | 9 +- packages/html/src/document/head.rs | 562 ----------------------- packages/html/src/document/mod.rs | 3 - 11 files changed, 150 insertions(+), 598 deletions(-) delete mode 100644 packages/html/src/document/head.rs diff --git a/Cargo.lock b/Cargo.lock index 55c3b6815b..1441682d39 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2657,6 +2657,7 @@ dependencies = [ "dioxus-core", "dioxus-core-macro", "dioxus-core-types", + "dioxus-html", "futures-channel", "serde", "serde_json", diff --git a/packages/document/Cargo.toml b/packages/document/Cargo.toml index 3289d9874d..fda5f58bda 100644 --- a/packages/document/Cargo.toml +++ b/packages/document/Cargo.toml @@ -7,6 +7,7 @@ version.workspace = true dioxus-core = { workspace = true } dioxus-core-types = { workspace = true } dioxus-core-macro = { workspace = true } +dioxus-html = { workspace = true } tracing = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } diff --git a/packages/document/src/document.rs b/packages/document/src/document.rs index f19b89c324..ec5d554bbc 100644 --- a/packages/document/src/document.rs +++ b/packages/document/src/document.rs @@ -5,6 +5,46 @@ use super::*; /// A context for the document pub type DocumentContext = Arc; +fn format_string_for_js(s: &str) -> String { + let escaped = s + .replace('\\', "\\\\") + .replace('\n', "\\n") + .replace('\r', "\\r") + .replace('"', "\\\""); + format!("\"{escaped}\"") +} + +fn format_attributes(attributes: &[(&str, String)]) -> String { + let mut formatted = String::from("["); + for (key, value) in attributes { + formatted.push_str(&format!( + "[{}, {}],", + format_string_for_js(key), + format_string_for_js(value) + )); + } + if formatted.ends_with(',') { + formatted.pop(); + } + formatted.push(']'); + formatted +} + +fn create_element_in_head( + tag: &str, + attributes: &[(&str, String)], + children: Option, +) -> String { + let helpers = include_str!("../js/head.js"); + let attributes = format_attributes(attributes); + let children = children + .as_deref() + .map(format_string_for_js) + .unwrap_or("null".to_string()); + let tag = format_string_for_js(tag); + format!(r#"{helpers};window.createElementInHead({tag}, {attributes}, {children});"#) +} + /// A provider for document-related functionality. /// /// Provides things like a history API, a title, a way to run JS, and some other basics/essentials used @@ -23,7 +63,9 @@ pub trait Document: 'static { fn eval(&self, js: String) -> Eval; /// Set the title of the document - fn set_title(&self, title: String); + fn set_title(&self, title: String) { + self.eval(format!("document.title = {title:?};")); + } /// Create a new element in the head fn create_head_element( @@ -35,22 +77,49 @@ pub trait Document: 'static { /// Create a new meta tag in the head fn create_meta(&self, props: MetaProps) { - self.create_head_element("meta", props.attributes(), None); + let attributes = props.attributes(); + self.create_head_element("meta", &attributes, None); } /// Create a new script tag in the head fn create_script(&self, props: ScriptProps) { - self.create_head_element("script", props.attributes(), props.script_contents()); + let attributes = props.attributes(); + match (&props.src, props.script_contents()) { + // The script has inline contents, render it as a script tag + (_, Ok(contents)) => self.create_head_element("script", &attributes, Some(contents)), + // The script has a src, render it as a script tag without a body + (Some(_), _) => self.create_head_element("script", &attributes, None), + // The script has neither contents nor src, log an error + (None, Err(err)) => { + err.log("Script"); + return; + } + } } /// Create a new style tag in the head fn create_style(&self, props: StyleProps) { - self.create_head_element("style", props.attributes(), props.style_contents()); + let mut attributes = props.attributes(); + match (&props.href, props.style_contents()) { + // The style has inline contents, render it as a style tag + (_, Ok(contents)) => self.create_head_element("style", attributes, Some(contents)), + // The style has a src, render it as a link tag + (Some(_), _) => { + attributes.push(("type", "text/css".into())); + self.create_head_element("link", attributes, None) + } + // The style has neither contents nor src, log an error + (None, Err(err)) => { + err.log("Style"); + return; + } + }; } /// Create a new link tag in the head fn create_link(&self, props: LinkProps) { - self.create_head_element("link", props.attributes(), None); + let attributes = props.attributes(); + self.create_head_element("link", &attributes, None); } /// Get the path of the current URL. diff --git a/packages/document/src/elements/link.rs b/packages/document/src/elements/link.rs index 05fec6f468..a56630ffa4 100644 --- a/packages/document/src/elements/link.rs +++ b/packages/document/src/elements/link.rs @@ -1,7 +1,9 @@ -use crate::document; - use super::*; +use crate::document; +use dioxus_core_macro::*; +use dioxus_html as dioxus_elements; +#[non_exhaustive] #[derive(Clone, Props, PartialEq)] pub struct LinkProps { pub rel: Option, @@ -19,6 +21,8 @@ pub struct LinkProps { pub integrity: Option, pub r#type: Option, pub blocking: Option, + #[props(extends = link, extends = GlobalAttributes)] + pub additional_attributes: Vec, } impl LinkProps { diff --git a/packages/document/src/elements/meta.rs b/packages/document/src/elements/meta.rs index ea695c6082..764d280598 100644 --- a/packages/document/src/elements/meta.rs +++ b/packages/document/src/elements/meta.rs @@ -1,5 +1,9 @@ use super::*; +use crate::document; +use dioxus_core_macro::*; +use dioxus_html as dioxus_elements; +#[non_exhaustive] /// Props for the [`Meta`] component #[derive(Clone, Props, PartialEq)] pub struct MetaProps { @@ -8,6 +12,8 @@ pub struct MetaProps { pub charset: Option, pub http_equiv: Option, pub content: Option, + #[props(extends = meta, extends = GlobalAttributes)] + pub additional_attributes: Vec, } impl MetaProps { @@ -56,11 +62,12 @@ impl MetaProps { /// /// #[component] +#[doc(alias = "")] pub fn Meta(props: MetaProps) -> Element { use_update_warning(&props, "Meta {}"); use_hook(|| { - let document = crate::document(); + let document = document(); document.create_meta(props); }); diff --git a/packages/document/src/elements/mod.rs b/packages/document/src/elements/mod.rs index 62f5c4ad9e..9f0d614225 100644 --- a/packages/document/src/elements/mod.rs +++ b/packages/document/src/elements/mod.rs @@ -30,12 +30,42 @@ fn use_update_warning(value: &T, name: &'static } } -fn extract_single_text_node(children: &Element, component: &str) -> Option { +/// An error that can occur when extracting a single text node from a component +pub enum ExtractSingleTextNodeError<'a> { + /// The node contained an render error, so we can't extract the text node + RenderError(&'a RenderError), + /// There was only one child, but it wasn't a text node + NonTextNode, + /// There is multiple child nodes + NonTemplate, +} + +impl ExtractSingleTextNodeError<'_> { + /// Log a warning depending on the error + pub fn log(&self, component: &str) { + match self { + ExtractSingleTextNodeError::RenderError(err) => { + tracing::error!("Error while rendering {component}: {err}"); + } + ExtractSingleTextNodeError::NonTextNode => { + tracing::error!( + "Error while rendering {component}: The children of {component} must be a single text node" + ); + } + ExtractSingleTextNodeError::NonTemplate => { + tracing::error!( + "Error while rendering {component}: The children of {component} must be a single text node" + ); + } + } + } +} + +fn extract_single_text_node(children: &Element) -> Result> { let vnode = match children { Element::Ok(vnode) => vnode, Element::Err(err) => { - tracing::error!("Error while rendering {component}: {err}"); - return None; + return Err(ExtractSingleTextNodeError::RenderError(err)); } }; // The title's children must be in one of two forms: @@ -48,7 +78,7 @@ fn extract_single_text_node(children: &Element, component: &str) -> Option Some(text.to_string()), + } => Ok(text.to_string()), // rsx! { "title: {dynamic_text}" } Template { roots: &[TemplateNode::Dynamic { id }], @@ -58,19 +88,11 @@ fn extract_single_text_node(children: &Element, component: &str) -> Option { let node = &vnode.dynamic_nodes[id]; match node { - DynamicNode::Text(text) => Some(text.value.clone()), - _ => { - tracing::error!("Error while rendering {component}: The children of {component} must be a single text node. It cannot be a component, if statement, loop, or a fragment"); - None - } + DynamicNode::Text(text) => Ok(text.value.clone()), + _ => Err(ExtractSingleTextNodeError::NonTextNode), } } - _ => { - tracing::error!( - "Error while rendering title: The children of title must be a single text node" - ); - None - } + _ => Err(ExtractSingleTextNodeError::NonTemplate), } } diff --git a/packages/document/src/elements/script.rs b/packages/document/src/elements/script.rs index 03131151b0..01a25d8930 100644 --- a/packages/document/src/elements/script.rs +++ b/packages/document/src/elements/script.rs @@ -1,7 +1,9 @@ -use crate::document; - use super::*; +use crate::document; +use dioxus_core_macro::*; +use dioxus_html as dioxus_elements; +#[non_exhaustive] #[derive(Clone, Props, PartialEq)] pub struct ScriptProps { /// The contents of the script tag. If present, the children must be a single text node. @@ -16,6 +18,8 @@ pub struct ScriptProps { pub nonce: Option, pub referrerpolicy: Option, pub r#type: Option, + #[props(extends = script, extends = GlobalAttributes)] + pub additional_attributes: Vec, } impl ScriptProps { @@ -51,8 +55,8 @@ impl ScriptProps { attributes } - pub fn script_contents(&self) -> Option { - extract_single_text_node(&self.children, "Script") + pub fn script_contents(&self) -> Result> { + extract_single_text_node(&self.children) } } diff --git a/packages/document/src/elements/style.rs b/packages/document/src/elements/style.rs index 178848c1e6..388d43836b 100644 --- a/packages/document/src/elements/style.rs +++ b/packages/document/src/elements/style.rs @@ -1,7 +1,9 @@ -use crate::document; - use super::*; +use crate::document; +use dioxus_core_macro::*; +use dioxus_html as dioxus_elements; +#[non_exhaustive] #[derive(Clone, Props, PartialEq)] pub struct StyleProps { /// Styles are deduplicated by their href attribute @@ -11,6 +13,8 @@ pub struct StyleProps { pub title: Option, /// The contents of the style tag. If present, the children must be a single text node. pub children: Element, + #[props(extends = style, extends = GlobalAttributes)] + pub additional_attributes: Vec, } impl StyleProps { @@ -31,8 +35,8 @@ impl StyleProps { attributes } - pub fn style_contents(&self) -> Option { - extract_single_text_node(&self.children, "Title") + pub fn style_contents(&self) -> Result> { + extract_single_text_node(&self.children) } } diff --git a/packages/document/src/elements/title.rs b/packages/document/src/elements/title.rs index 9b4a65a82c..efb95f82ee 100644 --- a/packages/document/src/elements/title.rs +++ b/packages/document/src/elements/title.rs @@ -27,10 +27,15 @@ pub struct TitleProps { /// } /// ``` #[component] +#[doc(alias = "")] pub fn Title(props: TitleProps) -> Element { let children = props.children; - let Some(text) = extract_single_text_node(&children, "Title") else { - return VNode::empty(); + let text = match extract_single_text_node(&children) { + Ok(text) => text, + Err(err) => { + err.log("Title"); + return VNode::empty(); + } }; // Update the title as it changes. NOTE: We don't use use_effect here because we need this to run on the server diff --git a/packages/html/src/document/head.rs b/packages/html/src/document/head.rs deleted file mode 100644 index c4b29c17f7..0000000000 --- a/packages/html/src/document/head.rs +++ /dev/null @@ -1,562 +0,0 @@ -#![doc = include_str!("../../docs/head.md")] - -use std::{cell::RefCell, collections::HashSet, rc::Rc}; - -use crate as dioxus_elements; -use dioxus_core::{prelude::*, DynamicNode}; -use dioxus_core_macro::*; - -/// Warn the user if they try to change props on a element that is injected into the head -#[allow(unused)] -fn use_update_warning<T: PartialEq + Clone + 'static>(value: &T, name: &'static str) { - #[cfg(debug_assertions)] - { - let cloned_value = value.clone(); - let initial = use_hook(move || value.clone()); - - if initial != cloned_value { - tracing::warn!("Changing the props of `{name}` is not supported "); - } - } -} - -/// An error that can occur when extracting a single text node from a component -pub enum ExtractSingleTextNodeError<'a> { - /// The node contained an render error, so we can't extract the text node - RenderError(&'a RenderError), - /// There was only one child, but it wasn't a text node - NonTextNode, - /// There is multiple child nodes - NonTemplate, -} - -impl ExtractSingleTextNodeError<'_> { - /// Log a warning depending on the error - pub fn log(&self, component: &str) { - match self { - ExtractSingleTextNodeError::RenderError(err) => { - tracing::error!("Error while rendering {component}: {err}"); - } - ExtractSingleTextNodeError::NonTextNode => { - tracing::error!( - "Error while rendering {component}: The children of {component} must be a single text node" - ); - } - ExtractSingleTextNodeError::NonTemplate => { - tracing::error!( - "Error while rendering {component}: The children of {component} must be a single text node" - ); - } - } - } -} - -fn extract_single_text_node(children: &Element) -> Result<String, ExtractSingleTextNodeError<'_>> { - let vnode = match children { - Element::Ok(vnode) => vnode, - Element::Err(err) => { - return Err(ExtractSingleTextNodeError::RenderError(err)); - } - }; - // The title's children must be in one of two forms: - // 1. rsx! { "static text" } - // 2. rsx! { "title: {dynamic_text}" } - match vnode.template { - // rsx! { "static text" } - Template { - roots: &[TemplateNode::Text { text }], - node_paths: &[], - attr_paths: &[], - .. - } => Ok(text.to_string()), - // rsx! { "title: {dynamic_text}" } - Template { - roots: &[TemplateNode::Dynamic { id }], - node_paths: &[&[0]], - attr_paths: &[], - .. - } => { - let node = &vnode.dynamic_nodes[id]; - match node { - DynamicNode::Text(text) => Ok(text.value.clone()), - _ => Err(ExtractSingleTextNodeError::NonTextNode), - } - } - _ => Err(ExtractSingleTextNodeError::NonTemplate), - } -} - -#[derive(Clone, Props, PartialEq)] -pub struct TitleProps { - /// The contents of the title tag. The children must be a single text node. - children: Element, -} - -/// Render the title of the page. On web renderers, this will set the [title](crate::elements::title) in the head. On desktop, it will set the window title. -/// -/// Unlike most head components, the Title can be modified after the first render. Only the latest update to the title will be reflected if multiple title components are rendered. -/// -/// -/// The children of the title component must be a single static or formatted string. If there are more children or the children contain components, conditionals, loops, or fragments, the title will not be updated. -/// -/// # Example -/// -/// ```rust, no_run -/// # use dioxus::prelude::*; -/// fn App() -> Element { -/// rsx! { -/// // You can use the Title component to render a title tag into the head of the page or window -/// Title { "My Page" } -/// } -/// } -/// ``` -#[component] -pub fn Title(props: TitleProps) -> Element { - let children = props.children; - let text = match extract_single_text_node(&children) { - Ok(text) => text, - Err(err) => { - err.log("Title"); - return VNode::empty(); - } - }; - - // Update the title as it changes. NOTE: We don't use use_effect here because we need this to run on the server - let document = use_hook(document); - let last_text = use_hook(|| { - // Set the title initially - document.set_title(text.clone()); - Rc::new(RefCell::new(text.clone())) - }); - - // If the text changes, update the title - let mut last_text = last_text.borrow_mut(); - if text != *last_text { - document.set_title(text.clone()); - *last_text = text; - } - - VNode::empty() -} - -#[non_exhaustive] -/// Props for the [`Meta`] component -#[derive(Clone, Props, PartialEq)] -pub struct MetaProps { - pub property: Option<String>, - pub name: Option<String>, - pub charset: Option<String>, - pub http_equiv: Option<String>, - pub content: Option<String>, - #[props(extends = meta, extends = GlobalAttributes)] - pub additional_attributes: Vec<Attribute>, -} - -impl MetaProps { - pub(crate) fn attributes(&self) -> Vec<(&'static str, String)> { - let mut attributes = Vec::new(); - if let Some(property) = &self.property { - attributes.push(("property", property.clone())); - } - if let Some(name) = &self.name { - attributes.push(("name", name.clone())); - } - if let Some(charset) = &self.charset { - attributes.push(("charset", charset.clone())); - } - if let Some(http_equiv) = &self.http_equiv { - attributes.push(("http-equiv", http_equiv.clone())); - } - if let Some(content) = &self.content { - attributes.push(("content", content.clone())); - } - attributes - } -} - -/// Render a [`meta`](crate::elements::meta) tag into the head of the page. -/// -/// # Example -/// -/// ```rust, no_run -/// # use dioxus::prelude::*; -/// fn RedirectToDioxusHomepageWithoutJS() -> Element { -/// rsx! { -/// // You can use the meta component to render a meta tag into the head of the page -/// // This meta tag will redirect the user to the dioxuslabs homepage in 10 seconds -/// Meta { -/// http_equiv: "refresh", -/// content: "10;url=https://dioxuslabs.com", -/// } -/// } -/// } -/// ``` -/// -/// <div class="warning"> -/// -/// Any updates to the props after the first render will not be reflected in the head. -/// -/// </div> -#[component] -pub fn Meta(props: MetaProps) -> Element { - use_update_warning(&props, "Meta {}"); - - use_hook(|| { - let document = document(); - document.create_meta(props); - }); - - VNode::empty() -} - -#[non_exhaustive] -#[derive(Clone, Props, PartialEq)] -pub struct ScriptProps { - /// The contents of the script tag. If present, the children must be a single text node. - pub children: Element, - /// Scripts are deduplicated by their src attribute - pub src: Option<String>, - pub defer: Option<bool>, - pub crossorigin: Option<String>, - pub fetchpriority: Option<String>, - pub integrity: Option<String>, - pub nomodule: Option<bool>, - pub nonce: Option<String>, - pub referrerpolicy: Option<String>, - pub r#type: Option<String>, - #[props(extends = script, extends = GlobalAttributes)] - pub additional_attributes: Vec<Attribute>, -} - -impl ScriptProps { - pub(crate) fn attributes(&self) -> Vec<(&'static str, String)> { - let mut attributes = Vec::new(); - if let Some(defer) = &self.defer { - attributes.push(("defer", defer.to_string())); - } - if let Some(crossorigin) = &self.crossorigin { - attributes.push(("crossorigin", crossorigin.clone())); - } - if let Some(fetchpriority) = &self.fetchpriority { - attributes.push(("fetchpriority", fetchpriority.clone())); - } - if let Some(integrity) = &self.integrity { - attributes.push(("integrity", integrity.clone())); - } - if let Some(nomodule) = &self.nomodule { - attributes.push(("nomodule", nomodule.to_string())); - } - if let Some(nonce) = &self.nonce { - attributes.push(("nonce", nonce.clone())); - } - if let Some(referrerpolicy) = &self.referrerpolicy { - attributes.push(("referrerpolicy", referrerpolicy.clone())); - } - if let Some(r#type) = &self.r#type { - attributes.push(("type", r#type.clone())); - } - if let Some(src) = &self.src { - attributes.push(("src", src.clone())); - } - attributes - } - - pub fn script_contents(&self) -> Result<String, ExtractSingleTextNodeError<'_>> { - extract_single_text_node(&self.children) - } -} - -/// Render a [`script`](crate::elements::script) tag into the head of the page. -/// -/// -/// If present, the children of the script component must be a single static or formatted string. If there are more children or the children contain components, conditionals, loops, or fragments, the script will not be added. -/// -/// -/// Any scripts you add will be deduplicated by their `src` attribute (if present). -/// -/// # Example -/// ```rust, no_run -/// # use dioxus::prelude::*; -/// fn LoadScript() -> Element { -/// rsx! { -/// // You can use the Script component to render a script tag into the head of the page -/// Script { -/// src: asset!("./assets/script.js"), -/// } -/// } -/// } -/// ``` -/// -/// <div class="warning"> -/// -/// Any updates to the props after the first render will not be reflected in the head. -/// -/// </div> -#[component] -pub fn Script(props: ScriptProps) -> Element { - use_update_warning(&props, "Script {}"); - - use_hook(|| { - if let Some(src) = &props.src { - if !should_insert_script(src) { - return; - } - } - - let document = document(); - document.create_script(props); - }); - - VNode::empty() -} - -#[non_exhaustive] -#[derive(Clone, Props, PartialEq)] -pub struct StyleProps { - /// Styles are deduplicated by their href attribute - pub href: Option<String>, - pub media: Option<String>, - pub nonce: Option<String>, - pub title: Option<String>, - /// The contents of the style tag. If present, the children must be a single text node. - pub children: Element, - #[props(extends = style, extends = GlobalAttributes)] - pub additional_attributes: Vec<Attribute>, -} - -impl StyleProps { - pub(crate) fn attributes(&self) -> Vec<(&'static str, String)> { - let mut attributes = Vec::new(); - if let Some(href) = &self.href { - attributes.push(("href", href.clone())); - } - if let Some(media) = &self.media { - attributes.push(("media", media.clone())); - } - if let Some(nonce) = &self.nonce { - attributes.push(("nonce", nonce.clone())); - } - if let Some(title) = &self.title { - attributes.push(("title", title.clone())); - } - attributes - } - - pub fn style_contents(&self) -> Result<String, ExtractSingleTextNodeError<'_>> { - extract_single_text_node(&self.children) - } -} - -/// Render a [`style`](crate::elements::style) tag into the head of the page. -/// -/// If present, the children of the style component must be a single static or formatted string. If there are more children or the children contain components, conditionals, loops, or fragments, the style will not be added. -/// -/// # Example -/// ```rust, no_run -/// # use dioxus::prelude::*; -/// fn RedBackground() -> Element { -/// rsx! { -/// // You can use the style component to render a style tag into the head of the page -/// // This style tag will set the background color of the page to red -/// Style { -/// r#" -/// body {{ -/// background-color: red; -/// }} -/// "# -/// } -/// } -/// } -/// ``` -/// -/// <div class="warning"> -/// -/// Any updates to the props after the first render will not be reflected in the head. -/// -/// </div> -#[component] -pub fn Style(props: StyleProps) -> Element { - use_update_warning(&props, "Style {}"); - - use_hook(|| { - if let Some(href) = &props.href { - if !should_insert_style(href) { - return; - } - } - let document = document(); - document.create_style(props); - }); - - VNode::empty() -} - -use super::*; - -#[non_exhaustive] -#[derive(Clone, Props, PartialEq)] -pub struct LinkProps { - pub rel: Option<String>, - pub media: Option<String>, - pub title: Option<String>, - pub disabled: Option<bool>, - pub r#as: Option<String>, - pub sizes: Option<String>, - /// Links are deduplicated by their href attribute - pub href: Option<String>, - pub crossorigin: Option<String>, - pub referrerpolicy: Option<String>, - pub fetchpriority: Option<String>, - pub hreflang: Option<String>, - pub integrity: Option<String>, - pub r#type: Option<String>, - pub blocking: Option<String>, - #[props(extends = link, extends = GlobalAttributes)] - pub additional_attributes: Vec<Attribute>, -} - -impl LinkProps { - pub(crate) fn attributes(&self) -> Vec<(&'static str, String)> { - let mut attributes = Vec::new(); - if let Some(rel) = &self.rel { - attributes.push(("rel", rel.clone())); - } - if let Some(media) = &self.media { - attributes.push(("media", media.clone())); - } - if let Some(title) = &self.title { - attributes.push(("title", title.clone())); - } - if let Some(disabled) = &self.disabled { - attributes.push(("disabled", disabled.to_string())); - } - if let Some(r#as) = &self.r#as { - attributes.push(("as", r#as.clone())); - } - if let Some(sizes) = &self.sizes { - attributes.push(("sizes", sizes.clone())); - } - if let Some(href) = &self.href { - attributes.push(("href", href.clone())); - } - if let Some(crossorigin) = &self.crossorigin { - attributes.push(("crossOrigin", crossorigin.clone())); - } - if let Some(referrerpolicy) = &self.referrerpolicy { - attributes.push(("referrerPolicy", referrerpolicy.clone())); - } - if let Some(fetchpriority) = &self.fetchpriority { - attributes.push(("fetchPriority", fetchpriority.clone())); - } - if let Some(hreflang) = &self.hreflang { - attributes.push(("hrefLang", hreflang.clone())); - } - if let Some(integrity) = &self.integrity { - attributes.push(("integrity", integrity.clone())); - } - if let Some(r#type) = &self.r#type { - attributes.push(("type", r#type.clone())); - } - if let Some(blocking) = &self.blocking { - attributes.push(("blocking", blocking.clone())); - } - attributes - } -} - -/// Render a [`link`](crate::elements::link) tag into the head of the page. -/// -/// > The [Link](https://docs.rs/dioxus-router/latest/dioxus_router/components/fn.Link.html) component in dioxus router and this component are completely different. -/// > This component links resources in the head of the page, while the router component creates clickable links in the body of the page. -/// -/// # Example -/// ```rust, no_run -/// # use dioxus::prelude::*; -/// fn RedBackground() -> Element { -/// rsx! { -/// // You can use the meta component to render a meta tag into the head of the page -/// // This meta tag will redirect the user to the dioxuslabs homepage in 10 seconds -/// head::Link { -/// href: asset!("./assets/style.css"), -/// rel: "stylesheet", -/// } -/// } -/// } -/// ``` -/// -/// <div class="warning"> -/// -/// Any updates to the props after the first render will not be reflected in the head. -/// -/// </div> -#[doc(alias = "<link>")] -#[component] -pub fn Link(props: LinkProps) -> Element { - use_update_warning(&props, "Link {}"); - - use_hook(|| { - if let Some(href) = &props.href { - if !should_insert_link(href) { - return; - } - } - let document = document(); - document.create_link(props); - }); - - VNode::empty() -} - -fn get_or_insert_root_context<T: Default + Clone + 'static>() -> T { - match ScopeId::ROOT.has_context::<T>() { - Some(context) => context, - None => { - let context = T::default(); - ScopeId::ROOT.provide_context(context.clone()); - context - } - } -} - -#[derive(Default, Clone)] -struct LinkContext(DeduplicationContext); - -fn should_insert_link(href: &str) -> bool { - get_or_insert_root_context::<LinkContext>() - .0 - .should_insert(href) -} - -#[derive(Default, Clone)] -struct ScriptContext(DeduplicationContext); - -fn should_insert_script(src: &str) -> bool { - get_or_insert_root_context::<ScriptContext>() - .0 - .should_insert(src) -} - -#[derive(Default, Clone)] -struct StyleContext(DeduplicationContext); - -fn should_insert_style(href: &str) -> bool { - get_or_insert_root_context::<StyleContext>() - .0 - .should_insert(href) -} - -#[derive(Default, Clone)] -struct DeduplicationContext(Rc<RefCell<HashSet<String>>>); - -impl DeduplicationContext { - fn should_insert(&self, href: &str) -> bool { - let mut set = self.0.borrow_mut(); - let present = set.contains(href); - if !present { - set.insert(href.to_string()); - true - } else { - false - } - } -} diff --git a/packages/html/src/document/mod.rs b/packages/html/src/document/mod.rs index 5fd205a3dd..7cef066ca6 100644 --- a/packages/html/src/document/mod.rs +++ b/packages/html/src/document/mod.rs @@ -11,9 +11,6 @@ use generational_box::{AnyStorage, GenerationalBox, UnsyncStorage}; mod eval; pub use eval::*; -pub mod head; -pub use head::{Meta, MetaProps, Script, ScriptProps, Style, StyleProps, Title, TitleProps}; - fn format_attributes(attributes: &[(&str, String)]) -> String { let mut formatted = String::from("["); for (key, value) in attributes { From 3a68c0848568a11da63dd58c61f8676657128e5c Mon Sep 17 00:00:00 2001 From: Evan Almloff <evanalmloff@gmail.com> Date: Fri, 4 Oct 2024 08:36:42 -0500 Subject: [PATCH 127/139] add a default head method through eval --- Cargo.lock | 1 + packages/document/Cargo.toml | 3 +++ packages/document/build.rs | 7 +++++++ packages/document/src/document.rs | 12 +++++++----- packages/document/src/elements/link.rs | 1 - packages/document/src/elements/meta.rs | 1 - packages/document/src/elements/script.rs | 1 - packages/document/src/elements/style.rs | 1 - packages/document/src/js/hash.txt | 1 + packages/document/src/js/head.js | 1 + packages/document/src/ts/.gitignore | 3 +++ packages/document/src/ts/head.ts | 19 +++++++++++++++++++ packages/document/tsconfig.json | 18 ++++++++++++++++++ 13 files changed, 60 insertions(+), 9 deletions(-) create mode 100644 packages/document/build.rs create mode 100644 packages/document/src/js/hash.txt create mode 100644 packages/document/src/js/head.js create mode 100644 packages/document/src/ts/.gitignore create mode 100644 packages/document/src/ts/head.ts create mode 100644 packages/document/tsconfig.json diff --git a/Cargo.lock b/Cargo.lock index 1441682d39..ae58858c4a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2659,6 +2659,7 @@ dependencies = [ "dioxus-core-types", "dioxus-html", "futures-channel", + "lazy-js-bundle", "serde", "serde_json", "tracing", diff --git a/packages/document/Cargo.toml b/packages/document/Cargo.toml index fda5f58bda..6a39e94fc2 100644 --- a/packages/document/Cargo.toml +++ b/packages/document/Cargo.toml @@ -12,3 +12,6 @@ tracing = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } futures-channel = { workspace = true } + +[build-dependencies] +lazy-js-bundle = { workspace = true } diff --git a/packages/document/build.rs b/packages/document/build.rs new file mode 100644 index 0000000000..8d5edfa581 --- /dev/null +++ b/packages/document/build.rs @@ -0,0 +1,7 @@ +fn main() { + // If any TS files change, re-run the build script + lazy_js_bundle::LazyTypeScriptBindings::new() + .with_watching("./src/ts") + .with_binding("./src/ts/head.ts", "./src/js/head.js") + .run(); +} diff --git a/packages/document/src/document.rs b/packages/document/src/document.rs index ec5d554bbc..b394354703 100644 --- a/packages/document/src/document.rs +++ b/packages/document/src/document.rs @@ -35,7 +35,7 @@ fn create_element_in_head( attributes: &[(&str, String)], children: Option<String>, ) -> String { - let helpers = include_str!("../js/head.js"); + let helpers = include_str!("./js/head.js"); let attributes = format_attributes(attributes); let children = children .as_deref() @@ -71,9 +71,11 @@ pub trait Document: 'static { fn create_head_element( &self, name: &str, - attributes: Vec<(&str, String)>, + attributes: &[(&str, String)], contents: Option<String>, - ); + ) { + self.eval(create_element_in_head(name, attributes, contents)); + } /// Create a new meta tag in the head fn create_meta(&self, props: MetaProps) { @@ -102,11 +104,11 @@ pub trait Document: 'static { let mut attributes = props.attributes(); match (&props.href, props.style_contents()) { // The style has inline contents, render it as a style tag - (_, Ok(contents)) => self.create_head_element("style", attributes, Some(contents)), + (_, Ok(contents)) => self.create_head_element("style", &attributes, Some(contents)), // The style has a src, render it as a link tag (Some(_), _) => { attributes.push(("type", "text/css".into())); - self.create_head_element("link", attributes, None) + self.create_head_element("link", &attributes, None) } // The style has neither contents nor src, log an error (None, Err(err)) => { diff --git a/packages/document/src/elements/link.rs b/packages/document/src/elements/link.rs index a56630ffa4..7fc3a9a481 100644 --- a/packages/document/src/elements/link.rs +++ b/packages/document/src/elements/link.rs @@ -1,6 +1,5 @@ use super::*; use crate::document; -use dioxus_core_macro::*; use dioxus_html as dioxus_elements; #[non_exhaustive] diff --git a/packages/document/src/elements/meta.rs b/packages/document/src/elements/meta.rs index 764d280598..a4227589b8 100644 --- a/packages/document/src/elements/meta.rs +++ b/packages/document/src/elements/meta.rs @@ -1,6 +1,5 @@ use super::*; use crate::document; -use dioxus_core_macro::*; use dioxus_html as dioxus_elements; #[non_exhaustive] diff --git a/packages/document/src/elements/script.rs b/packages/document/src/elements/script.rs index 01a25d8930..b285b275dc 100644 --- a/packages/document/src/elements/script.rs +++ b/packages/document/src/elements/script.rs @@ -1,6 +1,5 @@ use super::*; use crate::document; -use dioxus_core_macro::*; use dioxus_html as dioxus_elements; #[non_exhaustive] diff --git a/packages/document/src/elements/style.rs b/packages/document/src/elements/style.rs index 388d43836b..1f386e332d 100644 --- a/packages/document/src/elements/style.rs +++ b/packages/document/src/elements/style.rs @@ -1,6 +1,5 @@ use super::*; use crate::document; -use dioxus_core_macro::*; use dioxus_html as dioxus_elements; #[non_exhaustive] diff --git a/packages/document/src/js/hash.txt b/packages/document/src/js/hash.txt new file mode 100644 index 0000000000..1b62ecdb3d --- /dev/null +++ b/packages/document/src/js/hash.txt @@ -0,0 +1 @@ +[8375185156499858125] \ No newline at end of file diff --git a/packages/document/src/js/head.js b/packages/document/src/js/head.js new file mode 100644 index 0000000000..f4d2c362aa --- /dev/null +++ b/packages/document/src/js/head.js @@ -0,0 +1 @@ +var createElementInHead=function(tag,attributes,children){const element=document.createElement(tag);for(let[key,value]of attributes)element.setAttribute(key,value);if(children)element.appendChild(document.createTextNode(children));document.head.appendChild(element)};window.createElementInHead=createElementInHead; diff --git a/packages/document/src/ts/.gitignore b/packages/document/src/ts/.gitignore new file mode 100644 index 0000000000..68b79b0d33 --- /dev/null +++ b/packages/document/src/ts/.gitignore @@ -0,0 +1,3 @@ +# please dont accidentally run tsc and commit your js in this dir. +*.js + diff --git a/packages/document/src/ts/head.ts b/packages/document/src/ts/head.ts new file mode 100644 index 0000000000..d01c0aaffb --- /dev/null +++ b/packages/document/src/ts/head.ts @@ -0,0 +1,19 @@ +// Helper functions for working with the document head + +function createElementInHead( + tag: string, + attributes: [string, string][], + children: string | null +): void { + const element = document.createElement(tag); + for (const [key, value] of attributes) { + element.setAttribute(key, value); + } + if (children) { + element.appendChild(document.createTextNode(children)); + } + document.head.appendChild(element); +} + +// @ts-ignore +window.createElementInHead = createElementInHead; diff --git a/packages/document/tsconfig.json b/packages/document/tsconfig.json new file mode 100644 index 0000000000..11cda68e3f --- /dev/null +++ b/packages/document/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "module": "CommonJS", + "lib": [ + "ES2015", + "DOM", + "dom", + "dom.iterable", + "ESNext" + ], + "noImplicitAny": true, + "removeComments": true, + "preserveConstEnums": true, + }, + "exclude": [ + "**/*.spec.ts" + ] +} From 872101445567344fa1309a9d8dc066e5d457282e Mon Sep 17 00:00:00 2001 From: Evan Almloff <evanalmloff@gmail.com> Date: Fri, 4 Oct 2024 13:32:45 -0500 Subject: [PATCH 128/139] move over eval logic --- Cargo.lock | 1 + packages/document/Cargo.toml | 1 + packages/document/src/error.rs | 6 +-- packages/document/src/eval.rs | 61 ++++++++++++++-------- packages/{html => document}/src/ts/eval.ts | 0 packages/html/build.rs | 7 --- packages/html/src/js/hash.txt | 1 - packages/html/src/js/head.js | 1 - packages/html/src/ts/.gitignore | 3 -- packages/html/src/ts/head.ts | 19 ------- packages/web/src/ts/eval.ts | 2 +- 11 files changed, 45 insertions(+), 57 deletions(-) rename packages/{html => document}/src/ts/eval.ts (100%) delete mode 100644 packages/html/build.rs delete mode 100644 packages/html/src/js/hash.txt delete mode 100644 packages/html/src/js/head.js delete mode 100644 packages/html/src/ts/.gitignore delete mode 100644 packages/html/src/ts/head.ts diff --git a/Cargo.lock b/Cargo.lock index ae58858c4a..3a3f0076e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2659,6 +2659,7 @@ dependencies = [ "dioxus-core-types", "dioxus-html", "futures-channel", + "futures-util", "lazy-js-bundle", "serde", "serde_json", diff --git a/packages/document/Cargo.toml b/packages/document/Cargo.toml index 6a39e94fc2..31dad16721 100644 --- a/packages/document/Cargo.toml +++ b/packages/document/Cargo.toml @@ -12,6 +12,7 @@ tracing = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } futures-channel = { workspace = true } +futures-util.workspace = true [build-dependencies] lazy-js-bundle = { workspace = true } diff --git a/packages/document/src/error.rs b/packages/document/src/error.rs index 896e8e3aee..322b178656 100644 --- a/packages/document/src/error.rs +++ b/packages/document/src/error.rs @@ -17,8 +17,8 @@ pub enum EvalError { /// Represents an error communicating between JavaScript and Rust. Communication(String), - /// Represents an error deserializing the result of an eval - Deserialization(serde_json::Error), + /// Represents an error serializing or deserializing the result of an eval + Serialization(serde_json::Error), } impl Display for EvalError { @@ -28,7 +28,7 @@ impl Display for EvalError { EvalError::Finished => write!(f, "EvalError::Finished - eval has already ran"), EvalError::InvalidJs(_) => write!(f, "EvalError::InvalidJs - the provided javascript is invalid"), EvalError::Communication(_) => write!(f, "EvalError::Communication - there was an error trying to communicate with between javascript and rust"), - EvalError::Deserialization(_) => write!(f, "EvalError::Deserialization - there was an error trying to deserialize the result of an eval"), + EvalError::Serialization(_) => write!(f, "EvalError::Serialization - there was an error trying to serialize or deserialize the result of an eval"), } } } diff --git a/packages/document/src/eval.rs b/packages/document/src/eval.rs index bc0a79c31c..d871de3819 100644 --- a/packages/document/src/eval.rs +++ b/packages/document/src/eval.rs @@ -1,6 +1,6 @@ #![doc = include_str!("../docs/eval.md")] - use crate::error::EvalError; +use futures_util::StreamExt; use std::{ future::{Future, IntoFuture}, pin::Pin, @@ -8,36 +8,53 @@ use std::{ #[doc = include_str!("../docs/eval.md")] pub struct Eval { - rx: futures_channel::oneshot::Receiver<Result<serde_json::Value, EvalError>>, + resolve: futures_channel::oneshot::Receiver<Result<serde_json::Value, EvalError>>, + sender: futures_channel::mpsc::UnboundedSender<Result<serde_json::Value, EvalError>>, + receiver: futures_channel::mpsc::UnboundedReceiver<Result<serde_json::Value, EvalError>>, } impl Eval { - /// Create this eval from a oneshot channel that, when resolved, will return the result of the eval - pub fn new( - rx: futures_channel::oneshot::Receiver<Result<serde_json::Value, EvalError>>, + /// Create this eval from: + /// - A oneshot channel that, when resolved, will return the result of the eval + /// - The sender and receiver for the eval channel + pub fn from_parts( + resolve: futures_channel::oneshot::Receiver<Result<serde_json::Value, EvalError>>, + sender: futures_channel::mpsc::UnboundedSender<Result<serde_json::Value, EvalError>>, + receiver: futures_channel::mpsc::UnboundedReceiver<Result<serde_json::Value, EvalError>>, ) -> Self { - Self { rx } + Self { + resolve, + sender, + receiver, + } } - /// Create this eval and return the tx that will be used to resolve the eval - pub fn from_parts() -> ( - futures_channel::oneshot::Sender<Result<serde_json::Value, EvalError>>, - Self, - ) { - let (tx, rx) = futures_channel::oneshot::channel(); - (tx, Self::new(rx)) + /// Wait until the javascript task is finished and return the result + pub async fn join<T: serde::de::DeserializeOwned>(self) -> Result<T, EvalError> { + let json_value = self + .resolve + .await + .map_err(|_| EvalError::Communication("eval channel closed".to_string()))??; + serde_json::from_value(json_value).map_err(EvalError::Serialization) } - /// Poll this eval until it resolves - pub async fn recv(self) -> Result<serde_json::Value, EvalError> { - self.rx - .await - .map_err(|_| EvalError::Communication("eval channel closed".to_string()))? + /// Send a message to the javascript task + pub fn send(&self, data: impl serde::Serialize) -> Result<(), EvalError> { + self.sender + .unbounded_send(Ok( + serde_json::to_value(data).map_err(EvalError::Serialization)? + )) + .map_err(|_| EvalError::Communication("eval channel closed".to_string())) } - pub async fn recv_as<T: serde::de::DeserializeOwned>(self) -> Result<T, EvalError> { - let res = self.recv().await?; - serde_json::from_value(res).map_err(EvalError::Deserialization) + /// Receive a message from the javascript task + pub async fn recv<T: serde::de::DeserializeOwned>(&mut self) -> Result<T, EvalError> { + let json_value = self + .receiver + .next() + .await + .ok_or_else(|| EvalError::Communication("eval channel closed".to_string()))??; + serde_json::from_value(json_value).map_err(EvalError::Serialization) } } @@ -46,6 +63,6 @@ impl IntoFuture for Eval { type IntoFuture = Pin<Box<dyn Future<Output = Self::Output>>>; fn into_future(self) -> Self::IntoFuture { - Box::pin(self.recv().into_future()) + Box::pin(self.join().into_future()) } } diff --git a/packages/html/src/ts/eval.ts b/packages/document/src/ts/eval.ts similarity index 100% rename from packages/html/src/ts/eval.ts rename to packages/document/src/ts/eval.ts diff --git a/packages/html/build.rs b/packages/html/build.rs deleted file mode 100644 index 8d5edfa581..0000000000 --- a/packages/html/build.rs +++ /dev/null @@ -1,7 +0,0 @@ -fn main() { - // If any TS files change, re-run the build script - lazy_js_bundle::LazyTypeScriptBindings::new() - .with_watching("./src/ts") - .with_binding("./src/ts/head.ts", "./src/js/head.js") - .run(); -} diff --git a/packages/html/src/js/hash.txt b/packages/html/src/js/hash.txt deleted file mode 100644 index faaeb89f2e..0000000000 --- a/packages/html/src/js/hash.txt +++ /dev/null @@ -1 +0,0 @@ -[206827801705263822, 8375185156499858125] \ No newline at end of file diff --git a/packages/html/src/js/head.js b/packages/html/src/js/head.js deleted file mode 100644 index f4d2c362aa..0000000000 --- a/packages/html/src/js/head.js +++ /dev/null @@ -1 +0,0 @@ -var createElementInHead=function(tag,attributes,children){const element=document.createElement(tag);for(let[key,value]of attributes)element.setAttribute(key,value);if(children)element.appendChild(document.createTextNode(children));document.head.appendChild(element)};window.createElementInHead=createElementInHead; diff --git a/packages/html/src/ts/.gitignore b/packages/html/src/ts/.gitignore deleted file mode 100644 index 68b79b0d33..0000000000 --- a/packages/html/src/ts/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# please dont accidentally run tsc and commit your js in this dir. -*.js - diff --git a/packages/html/src/ts/head.ts b/packages/html/src/ts/head.ts deleted file mode 100644 index d01c0aaffb..0000000000 --- a/packages/html/src/ts/head.ts +++ /dev/null @@ -1,19 +0,0 @@ -// Helper functions for working with the document head - -function createElementInHead( - tag: string, - attributes: [string, string][], - children: string | null -): void { - const element = document.createElement(tag); - for (const [key, value] of attributes) { - element.setAttribute(key, value); - } - if (children) { - element.appendChild(document.createTextNode(children)); - } - document.head.appendChild(element); -} - -// @ts-ignore -window.createElementInHead = createElementInHead; diff --git a/packages/web/src/ts/eval.ts b/packages/web/src/ts/eval.ts index 3c87d0d252..51396b63a6 100644 --- a/packages/web/src/ts/eval.ts +++ b/packages/web/src/ts/eval.ts @@ -2,7 +2,7 @@ import { DioxusChannel, Channel, WeakDioxusChannel, -} from "../../../html/src/ts/eval"; +} from "../../../document/src/ts/eval"; export class WebDioxusChannel extends DioxusChannel { js_to_rust: Channel; From 584d9fe7ae7c8d8b509d7d6b1a2d8094b73cbad7 Mon Sep 17 00:00:00 2001 From: Evan Almloff <evanalmloff@gmail.com> Date: Mon, 7 Oct 2024 07:28:52 -0500 Subject: [PATCH 129/139] revert a few unrelated changes --- packages/core-types/src/formatter.rs | 14 ------------ packages/core/src/hotreload_utils.rs | 34 ++++++++++++++-------------- packages/document/src/js/hash.txt | 2 +- packages/web/src/js/hash.txt | 2 +- 4 files changed, 19 insertions(+), 33 deletions(-) delete mode 100644 packages/core-types/src/formatter.rs diff --git a/packages/core-types/src/formatter.rs b/packages/core-types/src/formatter.rs deleted file mode 100644 index a4df243729..0000000000 --- a/packages/core-types/src/formatter.rs +++ /dev/null @@ -1,14 +0,0 @@ -use std::borrow::Cow; - -/// Take this type and format it into a Cow<'static, str> -/// -/// This trait exists so libraries like manganis can implement this type for asssets without depending -/// on dioxus-core, which can be heavy in proc macros and build scripts. -/// -/// We don't want a blanket impl for T: Display because that might conflict for the other integral data -/// types of AttributeValue -/// -/// Todo: we might be able to specialize without this just with Display. -pub trait DioxusFormattable { - fn format(&self) -> Cow<'static, str>; -} diff --git a/packages/core/src/hotreload_utils.rs b/packages/core/src/hotreload_utils.rs index 8527c170e6..e94ce0e505 100644 --- a/packages/core/src/hotreload_utils.rs +++ b/packages/core/src/hotreload_utils.rs @@ -113,23 +113,23 @@ pub enum FmtSegment { }, } -/// let __pool = DynamicValuePool::new( -/// vec![...], -/// vec![...], -/// vec![...], -/// ); -/// VNode::new( -/// None, -/// Template { -/// name: "...", -/// roots: &[...], -/// node_paths: &[..], -/// attr_paths: &[...], -/// }, -/// Box::new([...]), -/// Box::new([...]), -/// ) -/// +// let __pool = DynamicValuePool::new( +// vec![...], +// vec![...], +// vec![...], +// ); +// VNode::new( +// None, +// Template { +// name: "...", +// roots: &[...], +// node_paths: &[..], +// attr_paths: &[...], +// }, +// Box::new([...]), +// Box::new([...]), +// ) + // Open questions: // - How do we handle type coercion for different sized component property integers? // - Should non-string hot literals go through the centralized pool? diff --git a/packages/document/src/js/hash.txt b/packages/document/src/js/hash.txt index 1b62ecdb3d..faaeb89f2e 100644 --- a/packages/document/src/js/hash.txt +++ b/packages/document/src/js/hash.txt @@ -1 +1 @@ -[8375185156499858125] \ No newline at end of file +[206827801705263822, 8375185156499858125] \ No newline at end of file diff --git a/packages/web/src/js/hash.txt b/packages/web/src/js/hash.txt index f397e27fa1..aa450e1801 100644 --- a/packages/web/src/js/hash.txt +++ b/packages/web/src/js/hash.txt @@ -1 +1 @@ -[3479327739946104450] \ No newline at end of file +[1614426347475783279] \ No newline at end of file From e26058fed31bf70d8e7a10c474e53996bfd36a37 Mon Sep 17 00:00:00 2001 From: Evan Almloff <evanalmloff@gmail.com> Date: Mon, 7 Oct 2024 07:38:15 -0500 Subject: [PATCH 130/139] remove the old document trait --- Cargo.lock | 3 + Cargo.toml | 1 + packages/desktop/Cargo.toml | 1 + packages/desktop/src/document.rs | 6 +- packages/desktop/src/js/hash.txt | 2 +- packages/desktop/src/ts/native_eval.ts | 2 +- packages/desktop/src/webview.rs | 3 +- packages/document/src/document.rs | 10 +- packages/document/src/eval.rs | 135 +++++++++++++++++++++++ packages/html/src/document/eval.rs | 134 ----------------------- packages/html/src/document/mod.rs | 143 ------------------------- packages/html/src/lib.rs | 8 -- packages/liveview/Cargo.toml | 1 + packages/liveview/src/eval.rs | 2 +- packages/web/Cargo.toml | 1 + packages/web/src/devtools.rs | 2 +- packages/web/src/document.rs | 2 +- 17 files changed, 152 insertions(+), 304 deletions(-) delete mode 100644 packages/html/src/document/eval.rs delete mode 100644 packages/html/src/document/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 3a3f0076e9..c123925fa9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2591,6 +2591,7 @@ dependencies = [ "dioxus-cli-config", "dioxus-core", "dioxus-devtools", + "dioxus-document", "dioxus-hooks", "dioxus-html", "dioxus-interpreter-js", @@ -2858,6 +2859,7 @@ dependencies = [ "dioxus-cli-config", "dioxus-core", "dioxus-devtools", + "dioxus-document", "dioxus-html", "dioxus-interpreter-js", "futures-channel", @@ -3101,6 +3103,7 @@ dependencies = [ "dioxus-core", "dioxus-core-types", "dioxus-devtools", + "dioxus-document", "dioxus-html", "dioxus-interpreter-js", "dioxus-signals", diff --git a/Cargo.toml b/Cargo.toml index cf06fe0ca9..41dceef551 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -77,6 +77,7 @@ dioxus-core-macro = { path = "packages/core-macro", version = "0.6.0-alpha.0" } dioxus-config-macro = { path = "packages/config-macro", version = "0.6.0-alpha.0" } dioxus-router = { path = "packages/router", version = "0.6.0-alpha.0" } dioxus-router-macro = { path = "packages/router-macro", version = "0.6.0-alpha.0" } +dioxus-document = { path = "packages/document", version = "0.6.0-alpha.0", default-features = false } dioxus-html = { path = "packages/html", version = "0.6.0-alpha.0", default-features = false } dioxus-html-internal-macro = { path = "packages/html-internal-macro", version = "0.6.0-alpha.0" } dioxus-hooks = { path = "packages/hooks", version = "0.6.0-alpha.0" } diff --git a/packages/desktop/Cargo.toml b/packages/desktop/Cargo.toml index 08f281caf1..cc33f624eb 100644 --- a/packages/desktop/Cargo.toml +++ b/packages/desktop/Cargo.toml @@ -17,6 +17,7 @@ dioxus-html = { workspace = true, features = [ "file_engine", "document", ] } +dioxus-document = { workspace = true } dioxus-signals = { workspace = true, optional = true } dioxus-interpreter-js = { workspace = true, features = ["binary-protocol", "serialize"] } dioxus-cli-config = { workspace = true } diff --git a/packages/desktop/src/document.rs b/packages/desktop/src/document.rs index 26c724ff4b..b4d1c34c73 100644 --- a/packages/desktop/src/document.rs +++ b/packages/desktop/src/document.rs @@ -1,4 +1,4 @@ -use dioxus_html::document::{Document, EvalError, Evaluator}; +use dioxus_document::{Document, EvalError, Evaluator}; use generational_box::{AnyStorage, GenerationalBox, UnsyncStorage}; use crate::{query::Query, DesktopContext}; @@ -25,10 +25,6 @@ impl Document for DesktopDocument { fn set_title(&self, title: String) { self.desktop_ctx.window.set_title(&title); } - - fn as_any(&self) -> &dyn std::any::Any { - self - } } /// Represents a desktop-target's JavaScript evaluator. diff --git a/packages/desktop/src/js/hash.txt b/packages/desktop/src/js/hash.txt index 6287f751bc..28e23755df 100644 --- a/packages/desktop/src/js/hash.txt +++ b/packages/desktop/src/js/hash.txt @@ -1 +1 @@ -[11927251734412729446] \ No newline at end of file +[8429135317751680054] \ No newline at end of file diff --git a/packages/desktop/src/ts/native_eval.ts b/packages/desktop/src/ts/native_eval.ts index 10c4d6aab0..54e384e913 100644 --- a/packages/desktop/src/ts/native_eval.ts +++ b/packages/desktop/src/ts/native_eval.ts @@ -2,7 +2,7 @@ import { Channel, DioxusChannel, WeakDioxusChannel, -} from "../../../html/src/ts/eval"; +} from "../../../document/src/ts/eval"; // In dioxus desktop, eval needs to use the window object to store global state because we evaluate separate snippets of javascript in the browser declare global { diff --git a/packages/desktop/src/webview.rs b/packages/desktop/src/webview.rs index 21aa8adbe9..a9cf819bd1 100644 --- a/packages/desktop/src/webview.rs +++ b/packages/desktop/src/webview.rs @@ -13,8 +13,9 @@ use crate::{ Config, DesktopContext, DesktopService, }; use dioxus_core::{Runtime, ScopeId, VirtualDom}; +use dioxus_document::Document; use dioxus_hooks::to_owned; -use dioxus_html::{prelude::Document, HasFileData, HtmlEvent, PlatformEventData}; +use dioxus_html::{HasFileData, HtmlEvent, PlatformEventData}; use futures_util::{pin_mut, FutureExt}; use std::cell::OnceCell; use std::sync::Arc; diff --git a/packages/document/src/document.rs b/packages/document/src/document.rs index b394354703..68e7f47a6d 100644 --- a/packages/document/src/document.rs +++ b/packages/document/src/document.rs @@ -92,10 +92,7 @@ pub trait Document: 'static { // The script has a src, render it as a script tag without a body (Some(_), _) => self.create_head_element("script", &attributes, None), // The script has neither contents nor src, log an error - (None, Err(err)) => { - err.log("Script"); - return; - } + (None, Err(err)) => err.log("Script"), } } @@ -111,10 +108,7 @@ pub trait Document: 'static { self.create_head_element("link", &attributes, None) } // The style has neither contents nor src, log an error - (None, Err(err)) => { - err.log("Style"); - return; - } + (None, Err(err)) => err.log("Style"), }; } diff --git a/packages/document/src/eval.rs b/packages/document/src/eval.rs index d871de3819..b31e065d4f 100644 --- a/packages/document/src/eval.rs +++ b/packages/document/src/eval.rs @@ -66,3 +66,138 @@ impl IntoFuture for Eval { Box::pin(self.join().into_future()) } } + +// #![allow(clippy::await_holding_refcell_ref)] +// #![doc = include_str!("../../docs/eval.md")] + +// use dioxus_core::prelude::*; +// use generational_box::GenerationalBox; +// use std::error::Error; +// use std::fmt::Display; +// use std::future::{poll_fn, Future, IntoFuture}; +// use std::pin::Pin; +// use std::rc::Rc; +// use std::task::{Context, Poll}; + +// use super::document; + +// /// The platform's evaluator. +// pub trait Evaluator { +// /// Sends a message to the evaluated JavaScript. +// fn send(&self, data: serde_json::Value) -> Result<(), EvalError>; +// /// Receive any queued messages from the evaluated JavaScript. +// fn poll_recv( +// &mut self, +// context: &mut Context<'_>, +// ) -> Poll<Result<serde_json::Value, EvalError>>; +// /// Gets the return value of the JavaScript +// fn poll_join( +// &mut self, +// context: &mut Context<'_>, +// ) -> Poll<Result<serde_json::Value, EvalError>>; +// } + +// type EvalCreator = Rc<dyn Fn(&str) -> UseEval>; + +// /// Get a struct that can execute any JavaScript. +// /// +// /// # Safety +// /// +// /// Please be very careful with this function. A script with too many dynamic +// /// parts is practically asking for a hacker to find an XSS vulnerability in +// /// it. **This applies especially to web targets, where the JavaScript context +// /// has access to most, if not all of your application data.** +// #[must_use] +// pub fn eval_provider() -> EvalCreator { +// let eval_provider = document(); + +// Rc::new(move |script: &str| UseEval::new(eval_provider.new_evaluator(script.to_string()))) +// as Rc<dyn Fn(&str) -> UseEval> +// } + +// #[doc = include_str!("../../docs/eval.md")] +// #[doc(alias = "javascript")] +// pub fn eval(script: &str) -> UseEval { +// let document = use_hook(document); +// UseEval::new(document.new_evaluator(script.to_string())) +// } + +// /// A wrapper around the target platform's evaluator that lets you send and receive data from JavaScript spawned by [`eval`]. +// /// +// #[doc = include_str!("../../docs/eval.md")] +// #[derive(Clone, Copy)] +// pub struct UseEval { +// evaluator: GenerationalBox<Box<dyn Evaluator>>, +// } + +// impl UseEval { +// /// Creates a new UseEval +// pub fn new(evaluator: GenerationalBox<Box<dyn Evaluator + 'static>>) -> Self { +// Self { evaluator } +// } + +// /// Sends a [`serde_json::Value`] to the evaluated JavaScript. +// pub fn send(&self, data: serde_json::Value) -> Result<(), EvalError> { +// match self.evaluator.try_read() { +// Ok(evaluator) => evaluator.send(data), +// Err(_) => Err(EvalError::Finished), +// } +// } + +// /// Gets an UnboundedReceiver to receive messages from the evaluated JavaScript. +// pub async fn recv(&mut self) -> Result<serde_json::Value, EvalError> { +// poll_fn(|cx| match self.evaluator.try_write() { +// Ok(mut evaluator) => evaluator.poll_recv(cx), +// Err(_) => Poll::Ready(Err(EvalError::Finished)), +// }) +// .await +// } + +// /// Gets the return value of the evaluated JavaScript. +// pub async fn join(self) -> Result<serde_json::Value, EvalError> { +// poll_fn(|cx| match self.evaluator.try_write() { +// Ok(mut evaluator) => evaluator.poll_join(cx), +// Err(_) => Poll::Ready(Err(EvalError::Finished)), +// }) +// .await +// } +// } + +// impl IntoFuture for UseEval { +// type Output = Result<serde_json::Value, EvalError>; +// type IntoFuture = Pin<Box<dyn Future<Output = Self::Output>>>; + +// fn into_future(self) -> Self::IntoFuture { +// Box::pin(self.join()) +// } +// } + +// /// Represents an error when evaluating JavaScript +// #[derive(Debug)] +// #[non_exhaustive] +// pub enum EvalError { +// /// The platform does not support evaluating JavaScript. +// Unsupported, + +// /// The provided JavaScript has already been ran. +// Finished, + +// /// The provided JavaScript is not valid and can't be ran. +// InvalidJs(String), + +// /// Represents an error communicating between JavaScript and Rust. +// Communication(String), +// } + +// impl Display for EvalError { +// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +// match self { +// EvalError::Unsupported => write!(f, "EvalError::Unsupported - eval is not supported on the current platform"), +// EvalError::Finished => write!(f, "EvalError::Finished - eval has already ran"), +// EvalError::InvalidJs(_) => write!(f, "EvalError::InvalidJs - the provided javascript is invalid"), +// EvalError::Communication(_) => write!(f, "EvalError::Communication - there was an error trying to communicate with between javascript and rust"), +// } +// } +// } + +// impl Error for EvalError {} diff --git a/packages/html/src/document/eval.rs b/packages/html/src/document/eval.rs deleted file mode 100644 index cd9d2aa0f6..0000000000 --- a/packages/html/src/document/eval.rs +++ /dev/null @@ -1,134 +0,0 @@ -#![allow(clippy::await_holding_refcell_ref)] -#![doc = include_str!("../../docs/eval.md")] - -use dioxus_core::prelude::*; -use generational_box::GenerationalBox; -use std::error::Error; -use std::fmt::Display; -use std::future::{poll_fn, Future, IntoFuture}; -use std::pin::Pin; -use std::rc::Rc; -use std::task::{Context, Poll}; - -use super::document; - -/// The platform's evaluator. -pub trait Evaluator { - /// Sends a message to the evaluated JavaScript. - fn send(&self, data: serde_json::Value) -> Result<(), EvalError>; - /// Receive any queued messages from the evaluated JavaScript. - fn poll_recv( - &mut self, - context: &mut Context<'_>, - ) -> Poll<Result<serde_json::Value, EvalError>>; - /// Gets the return value of the JavaScript - fn poll_join( - &mut self, - context: &mut Context<'_>, - ) -> Poll<Result<serde_json::Value, EvalError>>; -} - -type EvalCreator = Rc<dyn Fn(&str) -> UseEval>; - -/// Get a struct that can execute any JavaScript. -/// -/// # Safety -/// -/// Please be very careful with this function. A script with too many dynamic -/// parts is practically asking for a hacker to find an XSS vulnerability in -/// it. **This applies especially to web targets, where the JavaScript context -/// has access to most, if not all of your application data.** -#[must_use] -pub fn eval_provider() -> EvalCreator { - let eval_provider = document(); - - Rc::new(move |script: &str| UseEval::new(eval_provider.new_evaluator(script.to_string()))) - as Rc<dyn Fn(&str) -> UseEval> -} - -#[doc = include_str!("../../docs/eval.md")] -#[doc(alias = "javascript")] -pub fn eval(script: &str) -> UseEval { - let document = use_hook(document); - UseEval::new(document.new_evaluator(script.to_string())) -} - -/// A wrapper around the target platform's evaluator that lets you send and receive data from JavaScript spawned by [`eval`]. -/// -#[doc = include_str!("../../docs/eval.md")] -#[derive(Clone, Copy)] -pub struct UseEval { - evaluator: GenerationalBox<Box<dyn Evaluator>>, -} - -impl UseEval { - /// Creates a new UseEval - pub fn new(evaluator: GenerationalBox<Box<dyn Evaluator + 'static>>) -> Self { - Self { evaluator } - } - - /// Sends a [`serde_json::Value`] to the evaluated JavaScript. - pub fn send(&self, data: serde_json::Value) -> Result<(), EvalError> { - match self.evaluator.try_read() { - Ok(evaluator) => evaluator.send(data), - Err(_) => Err(EvalError::Finished), - } - } - - /// Gets an UnboundedReceiver to receive messages from the evaluated JavaScript. - pub async fn recv(&mut self) -> Result<serde_json::Value, EvalError> { - poll_fn(|cx| match self.evaluator.try_write() { - Ok(mut evaluator) => evaluator.poll_recv(cx), - Err(_) => Poll::Ready(Err(EvalError::Finished)), - }) - .await - } - - /// Gets the return value of the evaluated JavaScript. - pub async fn join(self) -> Result<serde_json::Value, EvalError> { - poll_fn(|cx| match self.evaluator.try_write() { - Ok(mut evaluator) => evaluator.poll_join(cx), - Err(_) => Poll::Ready(Err(EvalError::Finished)), - }) - .await - } -} - -impl IntoFuture for UseEval { - type Output = Result<serde_json::Value, EvalError>; - type IntoFuture = Pin<Box<dyn Future<Output = Self::Output>>>; - - fn into_future(self) -> Self::IntoFuture { - Box::pin(self.join()) - } -} - -/// Represents an error when evaluating JavaScript -#[derive(Debug)] -#[non_exhaustive] -pub enum EvalError { - /// The platform does not support evaluating JavaScript. - Unsupported, - - /// The provided JavaScript has already been ran. - Finished, - - /// The provided JavaScript is not valid and can't be ran. - InvalidJs(String), - - /// Represents an error communicating between JavaScript and Rust. - Communication(String), -} - -impl Display for EvalError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - EvalError::Unsupported => write!(f, "EvalError::Unsupported - eval is not supported on the current platform"), - EvalError::Finished => write!(f, "EvalError::Finished - eval has already ran"), - EvalError::InvalidJs(_) => write!(f, "EvalError::InvalidJs - the provided javascript is invalid"), - EvalError::Communication(_) => write!(f, "EvalError::Communication - there was an error trying to communicate with between javascript and rust"), - } - } -} - -impl Error for EvalError {} diff --git a/packages/html/src/document/mod.rs b/packages/html/src/document/mod.rs deleted file mode 100644 index 7cef066ca6..0000000000 --- a/packages/html/src/document/mod.rs +++ /dev/null @@ -1,143 +0,0 @@ -// API inspired by Reacts implementation of head only elements. We use components here instead of elements to simplify internals. - -use std::{ - rc::Rc, - task::{Context, Poll}, -}; - -use generational_box::{AnyStorage, GenerationalBox, UnsyncStorage}; - -#[allow(unused)] -mod eval; -pub use eval::*; - -fn format_attributes(attributes: &[(&str, String)]) -> String { - let mut formatted = String::from("["); - for (key, value) in attributes { - formatted.push_str(&format!("[{key:?}, {value:?}],")); - } - if formatted.ends_with(',') { - formatted.pop(); - } - formatted.push(']'); - formatted -} - -fn create_element_in_head( - tag: &str, - attributes: &[(&str, String)], - children: Option<String>, -) -> String { - let helpers = include_str!("../js/head.js"); - let attributes = format_attributes(attributes); - let children = children - .map(|c| format!("\"{c}\"")) - .unwrap_or("null".to_string()); - format!(r#"{helpers};window.createElementInHead("{tag}", {attributes}, {children});"#) -} - -/// A provider for document-related functionality. By default most methods are driven through [`eval`]. -pub trait Document { - /// Create a new evaluator for the document that evaluates JavaScript and facilitates communication between JavaScript and Rust. - fn new_evaluator(&self, js: String) -> GenerationalBox<Box<dyn Evaluator>>; - - /// Set the title of the document - fn set_title(&self, title: String) { - self.new_evaluator(format!("document.title = {title:?};")); - } - - /// Create a new meta tag - fn create_meta(&self, props: MetaProps) { - let attributes = props.attributes(); - let js = create_element_in_head("meta", &attributes, None); - self.new_evaluator(js); - } - - /// Create a new script tag - fn create_script(&self, props: ScriptProps) { - let attributes = props.attributes(); - let js = match (&props.src, props.script_contents()) { - // The script has inline contents, render it as a script tag - (_, Ok(contents)) => create_element_in_head("script", &attributes, Some(contents)), - // The script has a src, render it as a script tag without a body - (Some(_), _) => create_element_in_head("script", &attributes, None), - // The script has neither contents nor src, log an error - (None, Err(err)) => { - err.log("Script"); - return; - } - }; - self.new_evaluator(js); - } - - /// Create a new style tag - fn create_style(&self, props: StyleProps) { - let mut attributes = props.attributes(); - let js = match (&props.href, props.style_contents()) { - // The style has inline contents, render it as a style tag - (_, Ok(contents)) => create_element_in_head("style", &attributes, Some(contents)), - // The style has a src, render it as a link tag - (Some(_), _) => { - attributes.push(("type", "text/css".into())); - create_element_in_head("link", &attributes, None) - } - // The style has neither contents nor src, log an error - (None, Err(err)) => { - err.log("Style"); - return; - } - }; - self.new_evaluator(js); - } - - /// Create a new link tag - fn create_link(&self, props: head::LinkProps) { - let attributes = props.attributes(); - let js = create_element_in_head("link", &attributes, None); - self.new_evaluator(js); - } - - /// Get a reference to the document as `dyn Any` - fn as_any(&self) -> &dyn std::any::Any; -} - -/// The default No-Op document -pub struct NoOpDocument; - -impl Document for NoOpDocument { - fn new_evaluator(&self, _js: String) -> GenerationalBox<Box<dyn Evaluator>> { - tracing::error!("Eval is not supported on this platform. If you are using dioxus fullstack, you can wrap your code with `client! {{}}` to only include the code that runs eval in the client bundle."); - UnsyncStorage::owner().insert(Box::new(NoOpEvaluator)) - } - - fn as_any(&self) -> &dyn std::any::Any { - self - } -} - -struct NoOpEvaluator; -impl Evaluator for NoOpEvaluator { - fn send(&self, _data: serde_json::Value) -> Result<(), EvalError> { - Err(EvalError::Unsupported) - } - fn poll_recv( - &mut self, - _context: &mut Context<'_>, - ) -> Poll<Result<serde_json::Value, EvalError>> { - Poll::Ready(Err(EvalError::Unsupported)) - } - fn poll_join( - &mut self, - _context: &mut Context<'_>, - ) -> Poll<Result<serde_json::Value, EvalError>> { - Poll::Ready(Err(EvalError::Unsupported)) - } -} - -/// Get the document provider for the current platform or a no-op provider if the platform doesn't document functionality. -pub fn document() -> Rc<dyn Document> { - dioxus_core::prelude::try_consume_context::<Rc<dyn Document>>() - // Create a NoOp provider that always logs an error when trying to evaluate - // That way, we can still compile and run the code without a real provider - .unwrap_or_else(|| Rc::new(NoOpDocument) as Rc<dyn Document>) -} diff --git a/packages/html/src/lib.rs b/packages/html/src/lib.rs index 0067a90683..80e3a7c369 100644 --- a/packages/html/src/lib.rs +++ b/packages/html/src/lib.rs @@ -41,9 +41,6 @@ pub use elements::*; pub use events::*; pub use render_template::*; -#[cfg(feature = "document")] -pub mod document; - pub mod extensions { pub use crate::attribute_groups::{GlobalAttributesExtension, SvgAttributesExtension}; pub use crate::elements::extensions::*; @@ -51,11 +48,6 @@ pub mod extensions { pub mod prelude { pub use crate::attribute_groups::{GlobalAttributesExtension, SvgAttributesExtension}; - #[cfg(feature = "document")] - pub use crate::document::{ - self, document, eval, head, Document, Meta, MetaProps, Script, ScriptProps, Style, - StyleProps, Title, TitleProps, UseEval, - }; pub use crate::elements::extensions::*; pub use crate::events::*; pub use crate::point_interaction::*; diff --git a/packages/liveview/Cargo.toml b/packages/liveview/Cargo.toml index 8659443957..1844bddb17 100644 --- a/packages/liveview/Cargo.toml +++ b/packages/liveview/Cargo.toml @@ -23,6 +23,7 @@ tokio-util = { version = "0.7.4", features = ["rt"] } serde = { version = "1.0.151", features = ["derive"] } serde_json = "1.0.91" dioxus-html = { workspace = true, features = ["serialize", "document", "mounted"] } +dioxus-document = { workspace = true } rustc-hash = { workspace = true } dioxus-core = { workspace = true, features = ["serialize"] } dioxus-interpreter-js = { workspace = true, features = ["binary-protocol"] } diff --git a/packages/liveview/src/eval.rs b/packages/liveview/src/eval.rs index 6821d6502f..a159110049 100644 --- a/packages/liveview/src/eval.rs +++ b/packages/liveview/src/eval.rs @@ -1,5 +1,5 @@ use dioxus_core::ScopeId; -use dioxus_html::document::{Document, EvalError, Evaluator}; +use dioxus_document::{Document, EvalError, Evaluator}; use generational_box::{AnyStorage, GenerationalBox, UnsyncStorage}; use std::rc::Rc; diff --git a/packages/web/Cargo.toml b/packages/web/Cargo.toml index f79b2692a8..c7014127c1 100644 --- a/packages/web/Cargo.toml +++ b/packages/web/Cargo.toml @@ -13,6 +13,7 @@ keywords = ["dom", "ui", "gui", "react", "wasm"] dioxus-core = { workspace = true } dioxus-core-types = { workspace = true } dioxus-html = { workspace = true } +dioxus-document = { workspace = true } dioxus-devtools = { workspace = true } dioxus-signals = { workspace = true } dioxus-interpreter-js = { workspace = true, features = [ diff --git a/packages/web/src/devtools.rs b/packages/web/src/devtools.rs index 48a6f8ea0c..3f88b805ef 100644 --- a/packages/web/src/devtools.rs +++ b/packages/web/src/devtools.rs @@ -8,7 +8,7 @@ use std::time::Duration; use dioxus_core::ScopeId; use dioxus_devtools::{DevserverMsg, HotReloadMsg}; -use dioxus_html::prelude::eval; +use dioxus_document::prelude::eval; use futures_channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender}; use js_sys::JsString; use wasm_bindgen::JsCast; diff --git a/packages/web/src/document.rs b/packages/web/src/document.rs index 62127c7c7a..dea755ac19 100644 --- a/packages/web/src/document.rs +++ b/packages/web/src/document.rs @@ -1,5 +1,5 @@ use dioxus_core::ScopeId; -use dioxus_html::document::{Document, EvalError, Evaluator}; +use dioxus_document::{Document, EvalError, Evaluator}; use generational_box::{AnyStorage, GenerationalBox, UnsyncStorage}; use js_sys::Function; use serde::Serialize; From af4dfc7718b47c47a4b5845329edc62173012d96 Mon Sep 17 00:00:00 2001 From: Evan Almloff <evanalmloff@gmail.com> Date: Mon, 7 Oct 2024 09:51:46 -0500 Subject: [PATCH 131/139] implement document for each platform --- Cargo.lock | 3 + .../ecommerce-site/src/components/loading.rs | 2 +- example-projects/ecommerce-site/src/main.rs | 2 +- example-projects/file-explorer/src/main.rs | 4 +- .../fullstack-hackernews/src/main.rs | 2 +- examples/all_events.rs | 2 +- examples/calculator.rs | 2 +- examples/calculator_mutable.rs | 2 +- examples/clock.rs | 2 +- examples/control_focus.rs | 2 +- examples/counters.rs | 2 +- examples/crm.rs | 4 +- examples/dynamic_asset.rs | 2 +- examples/file_upload.rs | 2 +- examples/flat_router.rs | 2 +- examples/global.rs | 2 +- examples/image_generator_openai.rs | 2 +- examples/link.rs | 2 +- examples/overlay.rs | 2 +- examples/read_size.rs | 2 +- examples/reducer.rs | 2 +- examples/resize.rs | 2 +- examples/router.rs | 2 +- examples/todomvc.rs | 2 +- examples/weather_app.rs | 2 +- examples/window_event.rs | 2 +- packages/cli/src/builder/prepare_html.rs | 2 +- packages/core/tests/suspense.rs | 2 +- packages/desktop/Cargo.toml | 1 - packages/desktop/headless_tests/eval.rs | 12 +- packages/desktop/headless_tests/rendering.rs | 2 +- packages/desktop/headless_tests/utils.rs | 2 +- packages/desktop/src/document.rs | 6 +- packages/dioxus-lib/Cargo.toml | 3 +- packages/dioxus-lib/src/lib.rs | 3 + packages/dioxus/Cargo.toml | 3 +- packages/dioxus/src/lib.rs | 8 + packages/document/Cargo.toml | 1 + packages/document/docs/head.md | 2 +- packages/document/src/document.rs | 247 ------------------ packages/document/src/elements/link.rs | 2 +- packages/document/src/eval.rs | 209 +++------------ packages/fullstack/src/document/server.rs | 14 +- packages/fullstack/src/document/web.rs | 21 +- packages/fullstack/src/launch.rs | 2 +- packages/fullstack/src/render.rs | 13 +- packages/html/Cargo.toml | 6 +- packages/html/docs/head.md | 2 +- packages/liveview/Cargo.toml | 2 +- packages/liveview/src/eval.rs | 10 +- .../playwright-tests/fullstack/src/main.rs | 2 +- .../nested-suspense/src/main.rs | 2 +- packages/playwright-tests/web/src/main.rs | 10 +- packages/router/src/history/liveview.rs | 9 +- packages/static-generation/src/ssg.rs | 1 + packages/web/Cargo.toml | 2 +- packages/web/src/devtools.rs | 2 +- packages/web/src/document.rs | 10 +- 58 files changed, 142 insertions(+), 528 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c123925fa9..3609517ae1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2391,6 +2391,7 @@ dependencies = [ "dioxus-core-macro", "dioxus-desktop", "dioxus-devtools", + "dioxus-document", "dioxus-fullstack", "dioxus-hooks", "dioxus-html", @@ -2661,6 +2662,7 @@ dependencies = [ "dioxus-html", "futures-channel", "futures-util", + "generational-box", "lazy-js-bundle", "serde", "serde_json", @@ -2844,6 +2846,7 @@ dependencies = [ "dioxus-config-macro", "dioxus-core", "dioxus-core-macro", + "dioxus-document", "dioxus-hooks", "dioxus-html", "dioxus-rsx", diff --git a/example-projects/ecommerce-site/src/components/loading.rs b/example-projects/ecommerce-site/src/components/loading.rs index 7718f2185c..cd387ba61c 100644 --- a/example-projects/ecommerce-site/src/components/loading.rs +++ b/example-projects/ecommerce-site/src/components/loading.rs @@ -3,7 +3,7 @@ use dioxus::prelude::*; #[component] pub(crate) fn ChildrenOrLoading(children: Element) -> Element { rsx! { - head::Link { + document::Link { rel: "stylesheet", href: asset!("./public/loading.css") } diff --git a/example-projects/ecommerce-site/src/main.rs b/example-projects/ecommerce-site/src/main.rs index 7323055d02..55336f85d5 100644 --- a/example-projects/ecommerce-site/src/main.rs +++ b/example-projects/ecommerce-site/src/main.rs @@ -17,7 +17,7 @@ mod api; fn main() { launch(|| { rsx! { - head::Link { + document::Link { rel: "stylesheet", href: asset!("./public/tailwind.css") } diff --git a/example-projects/file-explorer/src/main.rs b/example-projects/file-explorer/src/main.rs index aa44850f5d..d4ceebc071 100644 --- a/example-projects/file-explorer/src/main.rs +++ b/example-projects/file-explorer/src/main.rs @@ -21,12 +21,12 @@ fn app() -> Element { let mut files = use_signal(Files::new); rsx! { - head::Link { + document::Link { rel: "stylesheet", href: asset!("./assets/fileexplorer.css") } div { - head::Link { href: "https://fonts.googleapis.com/icon?family=Material+Icons", rel: "stylesheet" } + document::Link { href: "https://fonts.googleapis.com/icon?family=Material+Icons", rel: "stylesheet" } header { i { class: "material-icons icon-menu", "menu" } h1 { "Files: " {files.read().current()} } diff --git a/example-projects/fullstack-hackernews/src/main.rs b/example-projects/fullstack-hackernews/src/main.rs index 62bf9d8f7b..afd5d342c3 100644 --- a/example-projects/fullstack-hackernews/src/main.rs +++ b/example-projects/fullstack-hackernews/src/main.rs @@ -36,7 +36,7 @@ pub fn App() -> Element { #[component] fn Homepage(story: ReadOnlySignal<PreviewState>) -> Element { rsx! { - head::Link { rel: "stylesheet", href: asset!("./assets/hackernews.css") } + document::Link { rel: "stylesheet", href: asset!("./assets/hackernews.css") } div { display: "flex", flex_direction: "row", width: "100%", div { width: "50%", diff --git a/examples/all_events.rs b/examples/all_events.rs index 2a04ee2bb3..17f6070d16 100644 --- a/examples/all_events.rs +++ b/examples/all_events.rs @@ -26,7 +26,7 @@ fn app() -> Element { }; rsx! { - head::Link { rel: "stylesheet", href: STYLE } + document::Link { rel: "stylesheet", href: STYLE } div { id: "container", // focusing is necessary to catch keyboard events div { id: "receiver", tabindex: 0, diff --git a/examples/calculator.rs b/examples/calculator.rs index 9bf2a04618..aa551c1c8b 100644 --- a/examples/calculator.rs +++ b/examples/calculator.rs @@ -54,7 +54,7 @@ fn app() -> Element { }; rsx! { - head::Link { rel: "stylesheet", href: STYLE } + document::Link { rel: "stylesheet", href: STYLE } div { id: "wrapper", div { class: "app", div { class: "calculator", tabindex: "0", onkeydown: handle_key_down_event, diff --git a/examples/calculator_mutable.rs b/examples/calculator_mutable.rs index 2f933ebbea..8acb8fa2e2 100644 --- a/examples/calculator_mutable.rs +++ b/examples/calculator_mutable.rs @@ -29,7 +29,7 @@ fn app() -> Element { let mut state = use_signal(Calculator::new); rsx! { - head::Link { rel: "stylesheet", href: asset!("./examples/assets/calculator.css") } + document::Link { rel: "stylesheet", href: asset!("./examples/assets/calculator.css") } div { id: "wrapper", div { class: "app", div { diff --git a/examples/clock.rs b/examples/clock.rs index dc168a5ec2..b3a0715aaf 100644 --- a/examples/clock.rs +++ b/examples/clock.rs @@ -36,7 +36,7 @@ fn app() -> Element { ); rsx! { - head::Link { rel: "stylesheet", href: STYLE } + document::Link { rel: "stylesheet", href: STYLE } div { id: "app", div { id: "title", "Carpe diem 🎉" } div { id: "clock-display", "{time}" } diff --git a/examples/control_focus.rs b/examples/control_focus.rs index c69293c56d..ed85071cd4 100644 --- a/examples/control_focus.rs +++ b/examples/control_focus.rs @@ -40,7 +40,7 @@ fn app() -> Element { }); rsx! { - head::Link { rel: "stylesheet", href: STYLE } + document::Link { rel: "stylesheet", href: STYLE } h1 { "Input Roulette" } button { onclick: move |_| running.toggle(), "Toggle roulette" } div { id: "roulette-grid", diff --git a/examples/counters.rs b/examples/counters.rs index f2d8c4203c..4e42f93ee7 100644 --- a/examples/counters.rs +++ b/examples/counters.rs @@ -16,7 +16,7 @@ fn app() -> Element { let sum = use_memo(move || counters.read().iter().copied().sum::<i32>()); rsx! { - head::Link { rel: "stylesheet", href: STYLE } + document::Link { rel: "stylesheet", href: STYLE } div { id: "controls", button { onclick: move |_| counters.write().push(0), "Add counter" } diff --git a/examples/crm.rs b/examples/crm.rs index 21f07466cd..1b644fa588 100644 --- a/examples/crm.rs +++ b/examples/crm.rs @@ -20,13 +20,13 @@ fn main() { })) .launch(|| { rsx! { - head::Link { + document::Link { rel: "stylesheet", href: asset!("https://unpkg.com/purecss@2.0.6/build/pure-min.css"), integrity: "sha384-Uu6IeWbM+gzNVXJcM9XV3SohHtmWE+3VGi496jvgX1jyvDTXfdK+rfZc8C1Aehk5", crossorigin: "anonymous" } - head::Link { rel: "stylesheet", href: asset!("./examples/assets/crm.css") } + document::Link { rel: "stylesheet", href: asset!("./examples/assets/crm.css") } h1 { "Dioxus CRM Example" } Router::<Route> {} } diff --git a/examples/dynamic_asset.rs b/examples/dynamic_asset.rs index 14a33c4809..b2ed16d296 100644 --- a/examples/dynamic_asset.rs +++ b/examples/dynamic_asset.rs @@ -24,7 +24,7 @@ fn app() -> Element { }); rsx! { - head::Link { rel: "stylesheet", href: STYLE } + document::Link { rel: "stylesheet", href: STYLE } h1 { "Dynamic Assets" } img { src: "/logos/logo.png" } } diff --git a/examples/file_upload.rs b/examples/file_upload.rs index 9fb74072bb..9a65367a4a 100644 --- a/examples/file_upload.rs +++ b/examples/file_upload.rs @@ -43,7 +43,7 @@ fn app() -> Element { }; rsx! { - head::Link { rel: "stylesheet", href: STYLE } + document::Link { rel: "stylesheet", href: STYLE } h1 { "File Upload Example" } p { "Drop a .txt, .rs, or .js file here to read it" } diff --git a/examples/flat_router.rs b/examples/flat_router.rs index 79a9d2a3f2..5c49f0e368 100644 --- a/examples/flat_router.rs +++ b/examples/flat_router.rs @@ -14,7 +14,7 @@ const STYLE: &str = asset!("./examples/assets/flat_router.css"); fn main() { launch(|| { rsx! { - head::Link { rel: "stylesheet", href: STYLE } + document::Link { rel: "stylesheet", href: STYLE } Router::<Route> {} } }) diff --git a/examples/global.rs b/examples/global.rs index 0a08485b02..e82c327d83 100644 --- a/examples/global.rs +++ b/examples/global.rs @@ -18,7 +18,7 @@ fn main() { fn app() -> Element { rsx! { - head::Link { rel: "stylesheet", href: STYLE } + document::Link { rel: "stylesheet", href: STYLE } Increment {} Decrement {} Reset {} diff --git a/examples/image_generator_openai.rs b/examples/image_generator_openai.rs index 474c46de90..71c9eb598c 100644 --- a/examples/image_generator_openai.rs +++ b/examples/image_generator_openai.rs @@ -36,7 +36,7 @@ fn app() -> Element { }); rsx! { - head::Link { rel: "stylesheet", href: "https://unpkg.com/bulma@0.9.0/css/bulma.min.css" } + document::Link { rel: "stylesheet", href: "https://unpkg.com/bulma@0.9.0/css/bulma.min.css" } div { class: "container", div { class: "columns", div { class: "column", diff --git a/examples/link.rs b/examples/link.rs index ff29b65626..88ec0f7223 100644 --- a/examples/link.rs +++ b/examples/link.rs @@ -16,7 +16,7 @@ fn main() { fn app() -> Element { rsx! ( - head::Link { rel: "stylesheet", href: STYLE } + document::Link { rel: "stylesheet", href: STYLE } Router::<Route> {} ) } diff --git a/examples/overlay.rs b/examples/overlay.rs index e4e2e80592..026935c6eb 100644 --- a/examples/overlay.rs +++ b/examples/overlay.rs @@ -20,7 +20,7 @@ fn app() -> Element { _ = use_global_shortcut("cmd+g", move || show_overlay.toggle()); rsx! { - head::Link { + document::Link { rel: "stylesheet", href: asset!("./examples/assets/overlay.css"), } diff --git a/examples/read_size.rs b/examples/read_size.rs index 1965597cfa..c06791d46e 100644 --- a/examples/read_size.rs +++ b/examples/read_size.rs @@ -28,7 +28,7 @@ fn app() -> Element { }; rsx!( - head::Link { rel: "stylesheet", href: asset!("./examples/assets/read_size.css") } + document::Link { rel: "stylesheet", href: asset!("./examples/assets/read_size.css") } div { width: "50%", height: "50%", diff --git a/examples/reducer.rs b/examples/reducer.rs index 7c434e5858..1d2f90096c 100644 --- a/examples/reducer.rs +++ b/examples/reducer.rs @@ -17,7 +17,7 @@ fn app() -> Element { let mut state = use_signal(|| PlayerState { is_playing: false }); rsx!( - head::Link { rel: "stylesheet", href: STYLE } + document::Link { rel: "stylesheet", href: STYLE } h1 {"Select an option"} // Add some cute animations if the radio is playing! diff --git a/examples/resize.rs b/examples/resize.rs index eace774a0b..c55cc8bb54 100644 --- a/examples/resize.rs +++ b/examples/resize.rs @@ -15,7 +15,7 @@ fn app() -> Element { let mut dimensions = use_signal(Size2D::zero); rsx!( - head::Link { rel: "stylesheet", href: asset!("./examples/assets/read_size.css") } + document::Link { rel: "stylesheet", href: asset!("./examples/assets/read_size.css") } div { width: "50%", height: "50%", diff --git a/examples/router.rs b/examples/router.rs index 13197f57e2..7a2c630d16 100644 --- a/examples/router.rs +++ b/examples/router.rs @@ -13,7 +13,7 @@ const STYLE: &str = asset!("./examples/assets/router.css"); fn main() { launch(|| { rsx! { - head::Link { rel: "stylesheet", href: STYLE } + document::Link { rel: "stylesheet", href: STYLE } Router::<Route> {} } }); diff --git a/examples/todomvc.rs b/examples/todomvc.rs index ea2e528a12..deb3ddad12 100644 --- a/examples/todomvc.rs +++ b/examples/todomvc.rs @@ -63,7 +63,7 @@ fn app() -> Element { }; rsx! { - head::Link { rel: "stylesheet", href: STYLE } + document::Link { rel: "stylesheet", href: STYLE } section { class: "todoapp", TodoHeader { todos } section { class: "main", diff --git a/examples/weather_app.rs b/examples/weather_app.rs index 395fd81f19..5bc258ff77 100644 --- a/examples/weather_app.rs +++ b/examples/weather_app.rs @@ -19,7 +19,7 @@ fn app() -> Element { let current_weather = use_resource(move || async move { get_weather(&country()).await }); rsx! { - head::Link { rel: "stylesheet", href: "https://unpkg.com/tailwindcss@^2.0/dist/tailwind.min.css" } + document::Link { rel: "stylesheet", href: "https://unpkg.com/tailwindcss@^2.0/dist/tailwind.min.css" } div { class: "mx-auto p-4 bg-gray-100 h-screen flex justify-center", div { class: "flex items-center justify-center flex-row", div { class: "flex items-start justify-center flex-row", diff --git a/examples/window_event.rs b/examples/window_event.rs index cb91d2a5b2..b8ce600f64 100644 --- a/examples/window_event.rs +++ b/examples/window_event.rs @@ -26,7 +26,7 @@ fn main() { fn app() -> Element { rsx!( - head::Link { href: "https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css", rel: "stylesheet" } + document::Link { href: "https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css", rel: "stylesheet" } Header {} div { class: "container mx-auto", div { class: "grid grid-cols-5", diff --git a/packages/cli/src/builder/prepare_html.rs b/packages/cli/src/builder/prepare_html.rs index 7d99dc0e16..fedfa0a2f6 100644 --- a/packages/cli/src/builder/prepare_html.rs +++ b/packages/cli/src/builder/prepare_html.rs @@ -156,7 +156,7 @@ impl BuildRequest { }; match variant { ResourceType::Style => format!( - " head::Link {{ rel: \"stylesheet\", href: asset!(css(\"{}\")) }}", + " document::Link {{ rel: \"stylesheet\", href: asset!(css(\"{}\")) }}", path.display() ), ResourceType::Script => { diff --git a/packages/core/tests/suspense.rs b/packages/core/tests/suspense.rs index f2775ab70d..cfabe841fe 100644 --- a/packages/core/tests/suspense.rs +++ b/packages/core/tests/suspense.rs @@ -517,7 +517,7 @@ fn nested_suspense_resolves_client() { let title = use_resource(move || async_content(0)).suspend()?(); rsx! { - Title { "{title.title}" } + document::Title { "{title.title}" } } } diff --git a/packages/desktop/Cargo.toml b/packages/desktop/Cargo.toml index cc33f624eb..976c0f3a7d 100644 --- a/packages/desktop/Cargo.toml +++ b/packages/desktop/Cargo.toml @@ -15,7 +15,6 @@ dioxus-html = { workspace = true, features = [ "serialize", "mounted", "file_engine", - "document", ] } dioxus-document = { workspace = true } dioxus-signals = { workspace = true, optional = true } diff --git a/packages/desktop/headless_tests/eval.rs b/packages/desktop/headless_tests/eval.rs index 4e9c9df795..1a348c753d 100644 --- a/packages/desktop/headless_tests/eval.rs +++ b/packages/desktop/headless_tests/eval.rs @@ -16,23 +16,23 @@ static EVALS_RETURNED: GlobalSignal<usize> = Signal::global(|| 0); fn app() -> Element { // Double 100 values in the value use_future(|| async { - let mut eval = eval( + let mut eval = document::eval( r#"for (let i = 0; i < 100; i++) { let value = await dioxus.recv(); dioxus.send(value*2); }"#, ); for i in 0..100 { - eval.send(serde_json::Value::from(i)).unwrap(); - let value = eval.recv().await.unwrap(); - assert_eq!(value, serde_json::Value::from(i * 2)); + eval.send(i).unwrap(); + let value: i32 = eval.recv().await.unwrap(); + assert_eq!(value, i * 2); EVALS_RECEIVED.with_mut(|x| *x += 1); } }); // Make sure returning no value resolves the future use_future(|| async { - let eval = eval(r#"return;"#); + let eval = document::eval(r#"return;"#); eval.await.unwrap(); EVALS_RETURNED.with_mut(|x| *x += 1); @@ -40,7 +40,7 @@ fn app() -> Element { // Return a value from the future use_future(|| async { - let eval = eval( + let eval = document::eval( r#" return [1, 2, 3]; "#, diff --git a/packages/desktop/headless_tests/rendering.rs b/packages/desktop/headless_tests/rendering.rs index ace615a020..cb67e7bec6 100644 --- a/packages/desktop/headless_tests/rendering.rs +++ b/packages/desktop/headless_tests/rendering.rs @@ -16,7 +16,7 @@ fn use_inner_html(id: &'static str) -> Option<String> { spawn(async move { tokio::time::sleep(std::time::Duration::from_millis(500)).await; - let res = eval(&format!( + let res = document::eval(&format!( r#"let element = document.getElementById('{}'); return element.innerHTML"#, id diff --git a/packages/desktop/headless_tests/utils.rs b/packages/desktop/headless_tests/utils.rs index 1f15cdd536..11b28f2068 100644 --- a/packages/desktop/headless_tests/utils.rs +++ b/packages/desktop/headless_tests/utils.rs @@ -50,7 +50,7 @@ pub fn mock_event_with_extra(id: &'static str, value: &'static str, extra: &'sta "# ); - eval(&js).await.unwrap(); + document::eval(&js).await.unwrap(); }); }) } diff --git a/packages/desktop/src/document.rs b/packages/desktop/src/document.rs index b4d1c34c73..fbc9fbfb2f 100644 --- a/packages/desktop/src/document.rs +++ b/packages/desktop/src/document.rs @@ -1,4 +1,4 @@ -use dioxus_document::{Document, EvalError, Evaluator}; +use dioxus_document::{Document, Eval, EvalError, Evaluator}; use generational_box::{AnyStorage, GenerationalBox, UnsyncStorage}; use crate::{query::Query, DesktopContext}; @@ -18,8 +18,8 @@ impl DesktopDocument { } impl Document for DesktopDocument { - fn new_evaluator(&self, js: String) -> GenerationalBox<Box<dyn Evaluator>> { - DesktopEvaluator::create(self.desktop_ctx.clone(), js) + fn eval(&self, js: String) -> Eval { + Eval::new(DesktopEvaluator::create(self.desktop_ctx.clone(), js)) } fn set_title(&self, title: String) { diff --git a/packages/dioxus-lib/Cargo.toml b/packages/dioxus-lib/Cargo.toml index 86144a9bfe..5ae189e0e0 100644 --- a/packages/dioxus-lib/Cargo.toml +++ b/packages/dioxus-lib/Cargo.toml @@ -13,6 +13,7 @@ rust-version = "1.79.0" [dependencies] dioxus-core = { workspace = true } dioxus-html = { workspace = true, optional = true } +dioxus-document = { workspace = true, optional = true } dioxus-core-macro = { workspace = true, optional = true } dioxus-config-macro = { workspace = true, optional = true } dioxus-hooks = { workspace = true, optional = true } @@ -26,7 +27,7 @@ dioxus = { workspace = true } default = ["macro", "html", "signals", "hooks"] signals = ["dep:dioxus-signals"] macro = ["dep:dioxus-core-macro", "dep:dioxus-rsx", "dep:dioxus-config-macro"] -html = ["dep:dioxus-html"] +html = ["dep:dioxus-html", "dep:dioxus-document"] hooks = ["dep:dioxus-hooks"] [package.metadata.docs.rs] diff --git a/packages/dioxus-lib/src/lib.rs b/packages/dioxus-lib/src/lib.rs index abf5140d65..5aabbdf6a4 100644 --- a/packages/dioxus-lib/src/lib.rs +++ b/packages/dioxus-lib/src/lib.rs @@ -16,6 +16,9 @@ pub mod events { #[cfg(feature = "html")] pub use dioxus_html as html; +#[cfg(feature = "html")] +pub use dioxus_document as document; + #[cfg(feature = "macro")] pub use dioxus_rsx as rsx; diff --git a/packages/dioxus/Cargo.toml b/packages/dioxus/Cargo.toml index 5db3dda5e3..8c8638a8e5 100644 --- a/packages/dioxus/Cargo.toml +++ b/packages/dioxus/Cargo.toml @@ -13,6 +13,7 @@ rust-version = "1.79.0" [dependencies] dioxus-core = { workspace = true } dioxus-html = { workspace = true, default-features = false, optional = true } +dioxus-document = { workspace = true, optional = true } dioxus-core-macro = { workspace = true, optional = true } dioxus-config-macro = { workspace = true, optional = true } dioxus-hooks = { workspace = true, optional = true } @@ -44,7 +45,7 @@ devtools = ["dep:dioxus-devtools", "dioxus-web?/devtools", "dioxus-fullstack?/de mounted = ["dioxus-web?/mounted", "dioxus-html?/mounted"] file_engine = ["dioxus-web?/file_engine"] asset = ["dep:manganis", "dioxus-core/manganis"] -document = ["dioxus-web?/document", "dioxus-html?/document"] +document = ["dioxus-web?/document", "dioxus-document"] launch = ["dep:dioxus-config-macro"] router = ["dep:dioxus-router"] diff --git a/packages/dioxus/src/lib.rs b/packages/dioxus/src/lib.rs index 24f231aeca..801c228277 100644 --- a/packages/dioxus/src/lib.rs +++ b/packages/dioxus/src/lib.rs @@ -51,6 +51,10 @@ pub mod events { pub use dioxus_html::prelude::*; } +#[cfg(feature = "document")] +#[cfg_attr(docsrs, doc(cfg(feature = "document")))] +pub use dioxus_document as document; + #[cfg(feature = "html")] #[cfg_attr(docsrs, doc(cfg(feature = "html")))] pub use dioxus_html as html; @@ -60,6 +64,10 @@ pub use dioxus_html as html; pub use dioxus_core_macro as core_macro; pub mod prelude { + #[cfg(feature = "document")] + #[cfg_attr(docsrs, doc(cfg(feature = "document")))] + pub use dioxus_document as document; + #[cfg(feature = "launch")] #[cfg_attr(docsrs, doc(cfg(feature = "launch")))] pub use crate::launch::*; diff --git a/packages/document/Cargo.toml b/packages/document/Cargo.toml index 31dad16721..52470e7435 100644 --- a/packages/document/Cargo.toml +++ b/packages/document/Cargo.toml @@ -13,6 +13,7 @@ serde = { workspace = true } serde_json = { workspace = true } futures-channel = { workspace = true } futures-util.workspace = true +generational-box.workspace = true [build-dependencies] lazy-js-bundle = { workspace = true } diff --git a/packages/document/docs/head.md b/packages/document/docs/head.md index 94dcb99374..ae726924de 100644 --- a/packages/document/docs/head.md +++ b/packages/document/docs/head.md @@ -4,7 +4,7 @@ Dioxus includes a series of components that render into the head of the page: - [Title](crate::Title) - [Meta](crate::Meta) -- [head::Link](crate::head::Link) +- [document::Link](crate::document::Link) - [Script](crate::Script) - [Style](crate::Style) diff --git a/packages/document/src/document.rs b/packages/document/src/document.rs index 68e7f47a6d..dbdb46907b 100644 --- a/packages/document/src/document.rs +++ b/packages/document/src/document.rs @@ -117,251 +117,4 @@ pub trait Document: 'static { let attributes = props.attributes(); self.create_head_element("link", &attributes, None); } - - /// Get the path of the current URL. - /// - /// **Must start** with `/`. **Must _not_ contain** the prefix. - /// - /// ```rust - /// # use dioxus_router::prelude::*; - /// # use dioxus::prelude::*; - /// # #[component] - /// # fn Index() -> Element { VNode::empty() } - /// # #[component] - /// # fn OtherPage() -> Element { VNode::empty() } - /// #[derive(Clone, Routable, Debug, PartialEq)] - /// enum Route { - /// #[route("/")] - /// Index {}, - /// #[route("/some-other-page")] - /// OtherPage {}, - /// } - /// let mut history = MemoryHistory::<Route>::default(); - /// assert_eq!(history.current_route().to_string(), "/"); - /// - /// history.push(Route::OtherPage {}); - /// assert_eq!(history.current_route().to_string(), "/some-other-page"); - /// ``` - #[must_use] - fn current_route(&self) -> String; - - /// Get the current path prefix of the URL. - /// - /// Not all [`HistoryProvider`]s need a prefix feature. It is meant for environments where a - /// dioxus-router-core-routed application is not running on `/`. The [`HistoryProvider`] is responsible - /// for removing the prefix from the dioxus-router-core-internal path, and also for adding it back in - /// during navigation. This functions value is only used for creating `href`s (e.g. for SSR or - /// display (but not navigation) in a web app). - fn base_route(&self) -> Option<String> { - None - } - - /// Check whether there is a previous page to navigate back to. - /// - /// If a [`HistoryProvider`] cannot know this, it should return [`true`]. - /// - /// ```rust - /// # use dioxus_router::prelude::*; - /// # use dioxus::prelude::*; - /// # #[component] - /// # fn Index() -> Element { VNode::empty() } - /// # fn Other() -> Element { VNode::empty() } - /// #[derive(Clone, Routable, Debug, PartialEq)] - /// enum Route { - /// #[route("/")] - /// Index {}, - /// #[route("/other")] - /// Other {}, - /// } - /// let mut history = MemoryHistory::<Route>::default(); - /// assert_eq!(history.can_go_back(), false); - /// - /// history.push(Route::Other {}); - /// assert_eq!(history.can_go_back(), true); - /// ``` - #[must_use] - fn can_go_back(&self) -> bool { - true - } - - /// Go back to a previous page. - /// - /// If a [`HistoryProvider`] cannot go to a previous page, it should do nothing. This method - /// might be called, even if `can_go_back` returns [`false`]. - /// - /// ```rust - /// # use dioxus_router::prelude::*; - /// # use dioxus::prelude::*; - /// # #[component] - /// # fn Index() -> Element { VNode::empty() } - /// # #[component] - /// # fn OtherPage() -> Element { VNode::empty() } - /// #[derive(Clone, Routable, Debug, PartialEq)] - /// enum Route { - /// #[route("/")] - /// Index {}, - /// #[route("/some-other-page")] - /// OtherPage {}, - /// } - /// let mut history = MemoryHistory::<Route>::default(); - /// assert_eq!(history.current_route().to_string(), "/"); - /// - /// history.go_back(); - /// assert_eq!(history.current_route().to_string(), "/"); - /// - /// history.push(Route::OtherPage {}); - /// assert_eq!(history.current_route().to_string(), "/some-other-page"); - /// - /// history.go_back(); - /// assert_eq!(history.current_route().to_string(), "/"); - /// ``` - fn go_back(&self); - - /// Check whether there is a future page to navigate forward to. - /// - /// If a [`HistoryProvider`] cannot know this, it should return [`true`]. - /// - /// ```rust - /// # use dioxus_router::prelude::*; - /// # use dioxus::prelude::*; - /// # #[component] - /// # fn Index() -> Element { VNode::empty() } - /// # #[component] - /// # fn OtherPage() -> Element { VNode::empty() } - /// #[derive(Clone, Routable, Debug, PartialEq)] - /// enum Route { - /// #[route("/")] - /// Index {}, - /// #[route("/some-other-page")] - /// OtherPage {}, - /// } - /// let mut history = MemoryHistory::<Route>::default(); - /// assert_eq!(history.can_go_forward(), false); - /// - /// history.push(Route::OtherPage {}); - /// assert_eq!(history.can_go_forward(), false); - /// - /// history.go_back(); - /// assert_eq!(history.can_go_forward(), true); - /// ``` - #[must_use] - fn can_go_forward(&self) -> bool { - true - } - - /// Go forward to a future page. - /// - /// If a [`HistoryProvider`] cannot go to a previous page, it should do nothing. This method - /// might be called, even if `can_go_forward` returns [`false`]. - /// - /// ```rust - /// # use dioxus_router::prelude::*; - /// # use dioxus::prelude::*; - /// # #[component] - /// # fn Index() -> Element { VNode::empty() } - /// # #[component] - /// # fn OtherPage() -> Element { VNode::empty() } - /// #[derive(Clone, Routable, Debug, PartialEq)] - /// enum Route { - /// #[route("/")] - /// Index {}, - /// #[route("/some-other-page")] - /// OtherPage {}, - /// } - /// let mut history = MemoryHistory::<Route>::default(); - /// history.push(Route::OtherPage {}); - /// assert_eq!(history.current_route(), Route::OtherPage {}); - /// - /// history.go_back(); - /// assert_eq!(history.current_route(), Route::Index {}); - /// - /// history.go_forward(); - /// assert_eq!(history.current_route(), Route::OtherPage {}); - /// ``` - fn go_forward(&self); - - /// Go to another page. - /// - /// This should do three things: - /// 1. Merge the current URL with the `path` parameter (which may also include a query part). - /// 2. Remove the previous URL to the navigation history. - /// 3. Clear the navigation future. - /// - /// ```rust - /// # use dioxus_router::prelude::*; - /// # use dioxus::prelude::*; - /// # #[component] - /// # fn Index() -> Element { VNode::empty() } - /// # #[component] - /// # fn OtherPage() -> Element { VNode::empty() } - /// #[derive(Clone, Routable, Debug, PartialEq)] - /// enum Route { - /// #[route("/")] - /// Index {}, - /// #[route("/some-other-page")] - /// OtherPage {}, - /// } - /// let mut history = MemoryHistory::<Route>::default(); - /// assert_eq!(history.current_route(), Route::Index {}); - /// - /// history.push(Route::OtherPage {}); - /// assert_eq!(history.current_route(), Route::OtherPage {}); - /// assert!(history.can_go_back()); - /// ``` - fn push_route(&self, route: String); - - /// Replace the current page with another one. - /// - /// This should merge the current URL with the `path` parameter (which may also include a query - /// part). In contrast to the `push` function, the navigation history and future should stay - /// untouched. - /// - /// ```rust - /// # use dioxus_router::prelude::*; - /// # use dioxus::prelude::*; - /// # #[component] - /// # fn Index() -> Element { VNode::empty() } - /// # #[component] - /// # fn OtherPage() -> Element { VNode::empty() } - /// #[derive(Clone, Routable, Debug, PartialEq)] - /// enum Route { - /// #[route("/")] - /// Index {}, - /// #[route("/some-other-page")] - /// OtherPage {}, - /// } - /// let mut history = MemoryHistory::<Route>::default(); - /// assert_eq!(history.current_route(), Route::Index {}); - /// - /// history.replace(Route::OtherPage {}); - /// assert_eq!(history.current_route(), Route::OtherPage {}); - /// assert!(!history.can_go_back()); - /// ``` - fn replace_route(&self, path: String); - - /// Navigate to an external URL. - /// - /// This should navigate to an external URL, which isn't controlled by the router. If a - /// [`HistoryProvider`] cannot do that, it should return [`false`], otherwise [`true`]. - /// - /// Returning [`false`] will cause the router to handle the external navigation failure. - #[allow(unused_variables)] - fn navigate_external(&self, url: String) -> bool { - false - } - - /// Provide the [`HistoryProvider`] with an update callback. - /// - /// Some [`HistoryProvider`]s may receive URL updates from outside the router. When such - /// updates are received, they should call `callback`, which will cause the router to update. - #[allow(unused_variables)] - fn updater(&self, callback: Arc<dyn Fn()>) {} - - /// Whether the history provider is synchronous or asynchronous. - /// - /// Generally we only support synchronous history providers, but some platforms like liveview - /// may need to be asynchronous. - fn is_synchronous(&self) -> bool { - true - } } diff --git a/packages/document/src/elements/link.rs b/packages/document/src/elements/link.rs index 7fc3a9a481..27fd3d1ebb 100644 --- a/packages/document/src/elements/link.rs +++ b/packages/document/src/elements/link.rs @@ -85,7 +85,7 @@ impl LinkProps { /// rsx! { /// // You can use the meta component to render a meta tag into the head of the page /// // This meta tag will redirect the user to the dioxuslabs homepage in 10 seconds -/// head::Link { +/// document::Link { /// href: asset!("./assets/style.css"), /// rel: "stylesheet", /// } diff --git a/packages/document/src/eval.rs b/packages/document/src/eval.rs index b31e065d4f..6475c87ddc 100644 --- a/packages/document/src/eval.rs +++ b/packages/document/src/eval.rs @@ -1,59 +1,49 @@ #![doc = include_str!("../docs/eval.md")] + use crate::error::EvalError; -use futures_util::StreamExt; -use std::{ - future::{Future, IntoFuture}, - pin::Pin, -}; +use generational_box::GenerationalBox; +use std::future::{poll_fn, Future, IntoFuture}; +use std::pin::Pin; +use std::task::{Context, Poll}; #[doc = include_str!("../docs/eval.md")] pub struct Eval { - resolve: futures_channel::oneshot::Receiver<Result<serde_json::Value, EvalError>>, - sender: futures_channel::mpsc::UnboundedSender<Result<serde_json::Value, EvalError>>, - receiver: futures_channel::mpsc::UnboundedReceiver<Result<serde_json::Value, EvalError>>, + evaluator: GenerationalBox<Box<dyn Evaluator>>, } impl Eval { - /// Create this eval from: - /// - A oneshot channel that, when resolved, will return the result of the eval - /// - The sender and receiver for the eval channel - pub fn from_parts( - resolve: futures_channel::oneshot::Receiver<Result<serde_json::Value, EvalError>>, - sender: futures_channel::mpsc::UnboundedSender<Result<serde_json::Value, EvalError>>, - receiver: futures_channel::mpsc::UnboundedReceiver<Result<serde_json::Value, EvalError>>, - ) -> Self { - Self { - resolve, - sender, - receiver, - } + /// Create this eval from a dynamic evaluator + pub fn new(evaluator: GenerationalBox<Box<dyn Evaluator + 'static>>) -> Self { + Self { evaluator } } /// Wait until the javascript task is finished and return the result pub async fn join<T: serde::de::DeserializeOwned>(self) -> Result<T, EvalError> { - let json_value = self - .resolve - .await - .map_err(|_| EvalError::Communication("eval channel closed".to_string()))??; + let json_value = poll_fn(|cx| match self.evaluator.try_write() { + Ok(mut evaluator) => evaluator.poll_join(cx), + Err(_) => Poll::Ready(Err(EvalError::Finished)), + }) + .await?; serde_json::from_value(json_value).map_err(EvalError::Serialization) } /// Send a message to the javascript task pub fn send(&self, data: impl serde::Serialize) -> Result<(), EvalError> { - self.sender - .unbounded_send(Ok( - serde_json::to_value(data).map_err(EvalError::Serialization)? - )) - .map_err(|_| EvalError::Communication("eval channel closed".to_string())) + match self.evaluator.try_read() { + Ok(evaluator) => { + evaluator.send(serde_json::to_value(data).map_err(EvalError::Serialization)?) + } + Err(_) => Err(EvalError::Finished), + } } /// Receive a message from the javascript task pub async fn recv<T: serde::de::DeserializeOwned>(&mut self) -> Result<T, EvalError> { - let json_value = self - .receiver - .next() - .await - .ok_or_else(|| EvalError::Communication("eval channel closed".to_string()))??; + let json_value = poll_fn(|cx| match self.evaluator.try_write() { + Ok(mut evaluator) => evaluator.poll_recv(cx), + Err(_) => Poll::Ready(Err(EvalError::Finished)), + }) + .await?; serde_json::from_value(json_value).map_err(EvalError::Serialization) } } @@ -67,137 +57,18 @@ impl IntoFuture for Eval { } } -// #![allow(clippy::await_holding_refcell_ref)] -// #![doc = include_str!("../../docs/eval.md")] - -// use dioxus_core::prelude::*; -// use generational_box::GenerationalBox; -// use std::error::Error; -// use std::fmt::Display; -// use std::future::{poll_fn, Future, IntoFuture}; -// use std::pin::Pin; -// use std::rc::Rc; -// use std::task::{Context, Poll}; - -// use super::document; - -// /// The platform's evaluator. -// pub trait Evaluator { -// /// Sends a message to the evaluated JavaScript. -// fn send(&self, data: serde_json::Value) -> Result<(), EvalError>; -// /// Receive any queued messages from the evaluated JavaScript. -// fn poll_recv( -// &mut self, -// context: &mut Context<'_>, -// ) -> Poll<Result<serde_json::Value, EvalError>>; -// /// Gets the return value of the JavaScript -// fn poll_join( -// &mut self, -// context: &mut Context<'_>, -// ) -> Poll<Result<serde_json::Value, EvalError>>; -// } - -// type EvalCreator = Rc<dyn Fn(&str) -> UseEval>; - -// /// Get a struct that can execute any JavaScript. -// /// -// /// # Safety -// /// -// /// Please be very careful with this function. A script with too many dynamic -// /// parts is practically asking for a hacker to find an XSS vulnerability in -// /// it. **This applies especially to web targets, where the JavaScript context -// /// has access to most, if not all of your application data.** -// #[must_use] -// pub fn eval_provider() -> EvalCreator { -// let eval_provider = document(); - -// Rc::new(move |script: &str| UseEval::new(eval_provider.new_evaluator(script.to_string()))) -// as Rc<dyn Fn(&str) -> UseEval> -// } - -// #[doc = include_str!("../../docs/eval.md")] -// #[doc(alias = "javascript")] -// pub fn eval(script: &str) -> UseEval { -// let document = use_hook(document); -// UseEval::new(document.new_evaluator(script.to_string())) -// } - -// /// A wrapper around the target platform's evaluator that lets you send and receive data from JavaScript spawned by [`eval`]. -// /// -// #[doc = include_str!("../../docs/eval.md")] -// #[derive(Clone, Copy)] -// pub struct UseEval { -// evaluator: GenerationalBox<Box<dyn Evaluator>>, -// } - -// impl UseEval { -// /// Creates a new UseEval -// pub fn new(evaluator: GenerationalBox<Box<dyn Evaluator + 'static>>) -> Self { -// Self { evaluator } -// } - -// /// Sends a [`serde_json::Value`] to the evaluated JavaScript. -// pub fn send(&self, data: serde_json::Value) -> Result<(), EvalError> { -// match self.evaluator.try_read() { -// Ok(evaluator) => evaluator.send(data), -// Err(_) => Err(EvalError::Finished), -// } -// } - -// /// Gets an UnboundedReceiver to receive messages from the evaluated JavaScript. -// pub async fn recv(&mut self) -> Result<serde_json::Value, EvalError> { -// poll_fn(|cx| match self.evaluator.try_write() { -// Ok(mut evaluator) => evaluator.poll_recv(cx), -// Err(_) => Poll::Ready(Err(EvalError::Finished)), -// }) -// .await -// } - -// /// Gets the return value of the evaluated JavaScript. -// pub async fn join(self) -> Result<serde_json::Value, EvalError> { -// poll_fn(|cx| match self.evaluator.try_write() { -// Ok(mut evaluator) => evaluator.poll_join(cx), -// Err(_) => Poll::Ready(Err(EvalError::Finished)), -// }) -// .await -// } -// } - -// impl IntoFuture for UseEval { -// type Output = Result<serde_json::Value, EvalError>; -// type IntoFuture = Pin<Box<dyn Future<Output = Self::Output>>>; - -// fn into_future(self) -> Self::IntoFuture { -// Box::pin(self.join()) -// } -// } - -// /// Represents an error when evaluating JavaScript -// #[derive(Debug)] -// #[non_exhaustive] -// pub enum EvalError { -// /// The platform does not support evaluating JavaScript. -// Unsupported, - -// /// The provided JavaScript has already been ran. -// Finished, - -// /// The provided JavaScript is not valid and can't be ran. -// InvalidJs(String), - -// /// Represents an error communicating between JavaScript and Rust. -// Communication(String), -// } - -// impl Display for EvalError { -// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { -// match self { -// EvalError::Unsupported => write!(f, "EvalError::Unsupported - eval is not supported on the current platform"), -// EvalError::Finished => write!(f, "EvalError::Finished - eval has already ran"), -// EvalError::InvalidJs(_) => write!(f, "EvalError::InvalidJs - the provided javascript is invalid"), -// EvalError::Communication(_) => write!(f, "EvalError::Communication - there was an error trying to communicate with between javascript and rust"), -// } -// } -// } - -// impl Error for EvalError {} +/// The platform's evaluator. +pub trait Evaluator { + /// Sends a message to the evaluated JavaScript. + fn send(&self, data: serde_json::Value) -> Result<(), EvalError>; + /// Receive any queued messages from the evaluated JavaScript. + fn poll_recv( + &mut self, + context: &mut Context<'_>, + ) -> Poll<Result<serde_json::Value, EvalError>>; + /// Gets the return value of the JavaScript + fn poll_join( + &mut self, + context: &mut Context<'_>, + ) -> Poll<Result<serde_json::Value, EvalError>>; +} diff --git a/packages/fullstack/src/document/server.rs b/packages/fullstack/src/document/server.rs index 74cee98c64..bee2e461e3 100644 --- a/packages/fullstack/src/document/server.rs +++ b/packages/fullstack/src/document/server.rs @@ -4,9 +4,8 @@ use std::cell::RefCell; -use dioxus_lib::{html::document::*, prelude::*}; +use dioxus_lib::{document::*, prelude::*}; use dioxus_ssr::Renderer; -use generational_box::GenerationalBox; use once_cell::sync::Lazy; use parking_lot::RwLock; @@ -71,8 +70,9 @@ impl ServerDocument { } impl Document for ServerDocument { - fn new_evaluator(&self, js: String) -> GenerationalBox<Box<dyn Evaluator>> { - NoOpDocument.new_evaluator(js) + fn eval(&self, _: String) -> Eval { + let owner = Owner::default(); + Eval::new(owner.invalid()) } fn set_title(&self, title: String) { @@ -151,7 +151,7 @@ impl Document for ServerDocument { } } - fn create_link(&self, props: head::LinkProps) { + fn create_link(&self, props: LinkProps) { self.warn_if_streaming(); self.serialize_for_hydration(); self.0.borrow_mut().link.push(rsx! { @@ -173,8 +173,4 @@ impl Document for ServerDocument { } }) } - - fn as_any(&self) -> &dyn std::any::Any { - self - } } diff --git a/packages/fullstack/src/document/web.rs b/packages/fullstack/src/document/web.rs index 3169fa05a3..aa645afa46 100644 --- a/packages/fullstack/src/document/web.rs +++ b/packages/fullstack/src/document/web.rs @@ -1,7 +1,7 @@ #![allow(unused)] //! On the client, we use the [`WebDocument`] implementation to render the head for any elements that were not rendered on the server. -use dioxus_lib::events::Document; +use dioxus_lib::document::*; use dioxus_web::WebDocument; fn head_element_written_on_server() -> bool { @@ -14,11 +14,8 @@ fn head_element_written_on_server() -> bool { pub(crate) struct FullstackWebDocument; impl Document for FullstackWebDocument { - fn new_evaluator( - &self, - js: String, - ) -> generational_box::GenerationalBox<Box<dyn dioxus_lib::prelude::document::Evaluator>> { - WebDocument.new_evaluator(js) + fn eval(&self, js: String) -> Eval { + WebDocument.eval(js) } fn set_title(&self, title: String) { @@ -28,35 +25,31 @@ impl Document for FullstackWebDocument { WebDocument.set_title(title); } - fn create_meta(&self, props: dioxus_lib::prelude::MetaProps) { + fn create_meta(&self, props: MetaProps) { if head_element_written_on_server() { return; } WebDocument.create_meta(props); } - fn create_script(&self, props: dioxus_lib::prelude::ScriptProps) { + fn create_script(&self, props: ScriptProps) { if head_element_written_on_server() { return; } WebDocument.create_script(props); } - fn create_style(&self, props: dioxus_lib::prelude::StyleProps) { + fn create_style(&self, props: StyleProps) { if head_element_written_on_server() { return; } WebDocument.create_style(props); } - fn create_link(&self, props: dioxus_lib::prelude::head::LinkProps) { + fn create_link(&self, props: LinkProps) { if head_element_written_on_server() { return; } WebDocument.create_link(props); } - - fn as_any(&self) -> &dyn std::any::Any { - self - } } diff --git a/packages/fullstack/src/launch.rs b/packages/fullstack/src/launch.rs index e590efc41e..2107224c61 100644 --- a/packages/fullstack/src/launch.rs +++ b/packages/fullstack/src/launch.rs @@ -63,7 +63,7 @@ pub fn launch( let factory = move || { let mut vdom = factory(); let document = std::rc::Rc::new(crate::document::web::FullstackWebDocument) - as std::rc::Rc<dyn dioxus_lib::prelude::document::Document>; + as std::rc::Rc<dyn dioxus_lib::document::Document>; vdom.provide_root_context(document); vdom }; diff --git a/packages/fullstack/src/render.rs b/packages/fullstack/src/render.rs index 526985108c..a265bdeb71 100644 --- a/packages/fullstack/src/render.rs +++ b/packages/fullstack/src/render.rs @@ -1,7 +1,9 @@ //! A shared pool of renderers for efficient server side rendering. +use crate::document::ServerDocument; use crate::streaming::{Mount, StreamingRenderer}; use dioxus_interpreter_js::INITIALIZE_STREAMING_JS; use dioxus_isrg::{CachedRender, RenderFreshness}; +use dioxus_lib::document::Document; use dioxus_ssr::Renderer; use futures_channel::mpsc::Sender; use futures_util::{Stream, StreamExt}; @@ -165,6 +167,7 @@ impl SsrRendererPool { let join_handle = spawn_platform(move || async move { let mut virtual_dom = virtual_dom_factory(); let document = std::rc::Rc::new(crate::document::server::ServerDocument::default()); + virtual_dom.provide_root_context(document.clone()); virtual_dom.provide_root_context(document.clone() as std::rc::Rc<dyn Document>); // poll the future, which may call server_context() @@ -431,11 +434,8 @@ impl FullstackHTMLTemplate { let ServeConfig { index, .. } = &self.cfg; let title = { - let document: Option<std::rc::Rc<dyn Document>> = + let document: Option<std::rc::Rc<ServerDocument>> = virtual_dom.in_runtime(|| ScopeId::ROOT.consume_context()); - let document: Option<&crate::document::server::ServerDocument> = document - .as_ref() - .and_then(|document| document.as_any().downcast_ref()); // Collect any head content from the document provider and inject that into the head document.and_then(|document| document.title()) }; @@ -448,11 +448,8 @@ impl FullstackHTMLTemplate { } to.write_str(&index.head_after_title)?; - let document: Option<std::rc::Rc<dyn dioxus_lib::prelude::document::Document>> = + let document: Option<std::rc::Rc<ServerDocument>> = virtual_dom.in_runtime(|| ScopeId::ROOT.consume_context()); - let document: Option<&crate::document::server::ServerDocument> = document - .as_ref() - .and_then(|document| document.as_any().downcast_ref()); if let Some(document) = document { // Collect any head content from the document provider and inject that into the head document.render(to)?; diff --git a/packages/html/Cargo.toml b/packages/html/Cargo.toml index a123602542..362ebe4c27 100644 --- a/packages/html/Cargo.toml +++ b/packages/html/Cargo.toml @@ -41,7 +41,7 @@ tokio = { workspace = true, features = ["time"] } manganis = { workspace = true } [features] -default = ["serialize", "mounted", "document", "file_engine"] +default = ["serialize", "mounted", "file_engine"] serialize = [ "dep:serde", "dep:serde_json", @@ -51,10 +51,6 @@ serialize = [ "dioxus-core/serialize" ] mounted = [] -document = [ - "dep:serde", - "dep:serde_json" -] file_engine = [ "dep:async-trait", ] diff --git a/packages/html/docs/head.md b/packages/html/docs/head.md index 94dcb99374..ae726924de 100644 --- a/packages/html/docs/head.md +++ b/packages/html/docs/head.md @@ -4,7 +4,7 @@ Dioxus includes a series of components that render into the head of the page: - [Title](crate::Title) - [Meta](crate::Meta) -- [head::Link](crate::head::Link) +- [document::Link](crate::document::Link) - [Script](crate::Script) - [Style](crate::Style) diff --git a/packages/liveview/Cargo.toml b/packages/liveview/Cargo.toml index 1844bddb17..95b1b4dd6e 100644 --- a/packages/liveview/Cargo.toml +++ b/packages/liveview/Cargo.toml @@ -22,7 +22,7 @@ tokio-stream = { version = "0.1.11", features = ["net"] } tokio-util = { version = "0.7.4", features = ["rt"] } serde = { version = "1.0.151", features = ["derive"] } serde_json = "1.0.91" -dioxus-html = { workspace = true, features = ["serialize", "document", "mounted"] } +dioxus-html = { workspace = true, features = ["serialize", "mounted"] } dioxus-document = { workspace = true } rustc-hash = { workspace = true } dioxus-core = { workspace = true, features = ["serialize"] } diff --git a/packages/liveview/src/eval.rs b/packages/liveview/src/eval.rs index a159110049..4ba1765010 100644 --- a/packages/liveview/src/eval.rs +++ b/packages/liveview/src/eval.rs @@ -1,5 +1,5 @@ use dioxus_core::ScopeId; -use dioxus_document::{Document, EvalError, Evaluator}; +use dioxus_document::{Document, Eval, EvalError, Evaluator}; use generational_box::{AnyStorage, GenerationalBox, UnsyncStorage}; use std::rc::Rc; @@ -18,12 +18,8 @@ pub struct LiveviewDocument { } impl Document for LiveviewDocument { - fn new_evaluator(&self, js: String) -> GenerationalBox<Box<dyn Evaluator>> { - LiveviewEvaluator::create(self.query.clone(), js) - } - - fn as_any(&self) -> &dyn std::any::Any { - self + fn eval(&self, js: String) -> Eval { + Eval::new(LiveviewEvaluator::create(self.query.clone(), js)) } } diff --git a/packages/playwright-tests/fullstack/src/main.rs b/packages/playwright-tests/fullstack/src/main.rs index 59edd973ec..0bc7361b04 100644 --- a/packages/playwright-tests/fullstack/src/main.rs +++ b/packages/playwright-tests/fullstack/src/main.rs @@ -17,7 +17,7 @@ fn app() -> Element { rsx! { h1 { "hello axum! {count}" } - Title { "hello axum! {count}" } + document::Title { "hello axum! {count}" } button { class: "increment-button", onclick: move |_| count += 1, "Increment" } button { class: "server-button", diff --git a/packages/playwright-tests/nested-suspense/src/main.rs b/packages/playwright-tests/nested-suspense/src/main.rs index c8e275ff96..c60efbdbd9 100644 --- a/packages/playwright-tests/nested-suspense/src/main.rs +++ b/packages/playwright-tests/nested-suspense/src/main.rs @@ -44,7 +44,7 @@ fn LoadTitle() -> Element { .unwrap(); rsx! { - Title { "{title.title}" } + document::Title { "{title.title}" } } } diff --git a/packages/playwright-tests/web/src/main.rs b/packages/playwright-tests/web/src/main.rs index 126f729e4c..dda3b2af4b 100644 --- a/packages/playwright-tests/web/src/main.rs +++ b/packages/playwright-tests/web/src/main.rs @@ -9,7 +9,7 @@ fn app() -> Element { rsx! { div { "hello axum! {num}" - Title { "hello axum! {num}" } + document::Title { "hello axum! {num}" } button { class: "increment-button", onclick: move |_| num += 1, "Increment" } } svg { circle { cx: 50, cy: 50, r: 40, stroke: "green", fill: "yellow" } } @@ -24,7 +24,7 @@ fn app() -> Element { button { class: "eval-button", onclick: move |_| async move { - let mut eval = eval( + let mut eval = document::eval( r#" window.document.title = 'Hello from Dioxus Eval!'; // Receive and multiply 10 numbers @@ -38,9 +38,9 @@ fn app() -> Element { // Send 10 numbers for i in 0..10 { - eval.send(serde_json::Value::from(i)).unwrap(); - let value = eval.recv().await.unwrap(); - assert_eq!(value, serde_json::Value::from(i * 2)); + eval.send(i).unwrap(); + let value: i32 = eval.recv().await.unwrap(); + assert_eq!(value, i * 2); } let result = eval.recv().await; diff --git a/packages/router/src/history/liveview.rs b/packages/router/src/history/liveview.rs index 790b9d98e9..7504f78107 100644 --- a/packages/router/src/history/liveview.rs +++ b/packages/router/src/history/liveview.rs @@ -1,7 +1,7 @@ use super::HistoryProvider; use crate::routable::Routable; +use dioxus_lib::document::Eval; use dioxus_lib::prelude::*; -use document::UseEval; use serde::{Deserialize, Serialize}; use std::sync::{Mutex, RwLock}; use std::{collections::BTreeMap, rc::Rc, str::FromStr, sync::Arc}; @@ -168,11 +168,10 @@ where let updater_callback: Arc<RwLock<Arc<dyn Fn() + Send + Sync>>> = Arc::new(RwLock::new(Arc::new(|| {}))); - let eval_provider = document(); + let eval_provider = dioxus_lib::document::document(); - let create_eval = Rc::new(move |script: &str| { - UseEval::new(eval_provider.new_evaluator(script.to_string())) - }) as Rc<dyn Fn(&str) -> UseEval>; + let create_eval = Rc::new(move |script: &str| eval_provider.eval(script.to_string())) + as Rc<dyn Fn(&str) -> Eval>; // Listen to server actions spawn({ diff --git a/packages/static-generation/src/ssg.rs b/packages/static-generation/src/ssg.rs index a4cae763cf..a3856ddc4f 100644 --- a/packages/static-generation/src/ssg.rs +++ b/packages/static-generation/src/ssg.rs @@ -1,4 +1,5 @@ use dioxus_isrg::*; +use dioxus_lib::document::Document; use dioxus_lib::prelude::*; use dioxus_router::prelude::*; use dioxus_ssr::renderer; diff --git a/packages/web/Cargo.toml b/packages/web/Cargo.toml index c7014127c1..ec21e97d56 100644 --- a/packages/web/Cargo.toml +++ b/packages/web/Cargo.toml @@ -101,7 +101,7 @@ file_engine = [ "web-sys/FileReader" ] devtools = ["web-sys/MessageEvent", "web-sys/WebSocket", "web-sys/Location", "dep:serde_json", "dep:serde", "dioxus-core/serialize"] -document = ["dioxus-html/document", "dep:serde-wasm-bindgen", "dep:serde_json", "dep:serde"] +document = ["dep:serde-wasm-bindgen", "dep:serde_json", "dep:serde"] [dev-dependencies] dioxus = { workspace = true, default-features = true } diff --git a/packages/web/src/devtools.rs b/packages/web/src/devtools.rs index 3f88b805ef..438f9cab40 100644 --- a/packages/web/src/devtools.rs +++ b/packages/web/src/devtools.rs @@ -8,7 +8,7 @@ use std::time::Duration; use dioxus_core::ScopeId; use dioxus_devtools::{DevserverMsg, HotReloadMsg}; -use dioxus_document::prelude::eval; +use dioxus_document::eval; use futures_channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender}; use js_sys::JsString; use wasm_bindgen::JsCast; diff --git a/packages/web/src/document.rs b/packages/web/src/document.rs index dea755ac19..68509f55aa 100644 --- a/packages/web/src/document.rs +++ b/packages/web/src/document.rs @@ -1,5 +1,5 @@ use dioxus_core::ScopeId; -use dioxus_document::{Document, EvalError, Evaluator}; +use dioxus_document::{Document, Eval, EvalError, Evaluator}; use generational_box::{AnyStorage, GenerationalBox, UnsyncStorage}; use js_sys::Function; use serde::Serialize; @@ -64,12 +64,8 @@ pub fn init_document() { /// The web-target's document provider. pub struct WebDocument; impl Document for WebDocument { - fn new_evaluator(&self, js: String) -> GenerationalBox<Box<dyn Evaluator>> { - WebEvaluator::create(js) - } - - fn as_any(&self) -> &dyn std::any::Any { - self + fn eval(&self, js: String) -> Eval { + Eval::new(WebEvaluator::create(js)) } } From cc7956ec52cffd4289b9412582f7bc16b60a157a Mon Sep 17 00:00:00 2001 From: Evan Almloff <evanalmloff@gmail.com> Date: Mon, 7 Oct 2024 09:56:20 -0500 Subject: [PATCH 132/139] Update remaining examples --- examples/eval.rs | 6 +++--- examples/meta.rs | 10 +++++----- examples/title.rs | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/examples/eval.rs b/examples/eval.rs index 9cfd15e941..d0417ada03 100644 --- a/examples/eval.rs +++ b/examples/eval.rs @@ -19,7 +19,7 @@ fn app() -> Element { // The `eval` is available in the prelude - and simply takes a block of JS. // Dioxus' eval is interesting since it allows sending messages to and from the JS code using the `await dioxus.recv()` // builtin function. This allows you to create a two-way communication channel between Rust and JS. - let mut eval = eval( + let mut eval = document::eval( r#" dioxus.send("Hi from JS!"); let msg = await dioxus.recv(); @@ -29,10 +29,10 @@ fn app() -> Element { ); // Send a message to the JS code. - eval.send("Hi from Rust!".into()).unwrap(); + eval.send("Hi from Rust!").unwrap(); // Our line on the JS side will log the message and then return "hello world". - let res = eval.recv().await.unwrap(); + let res: String = eval.recv().await.unwrap(); // This will print "Hi from JS!" and "Hi from Rust!". println!("{:?}", eval.await); diff --git a/examples/meta.rs b/examples/meta.rs index fae916258f..6b09943126 100644 --- a/examples/meta.rs +++ b/examples/meta.rs @@ -12,23 +12,23 @@ fn app() -> Element { // You can use the Meta component to render a meta tag into the head of the page // Meta tags are useful to provide information about the page to search engines and social media sites // This example sets up meta tags for the open graph protocol for social media previews - Meta { + document::Meta { property: "og:title", content: "My Site", } - Meta { + document::Meta { property: "og:type", content: "website", } - Meta { + document::Meta { property: "og:url", content: "https://www.example.com", } - Meta { + document::Meta { property: "og:image", content: "https://example.com/image.jpg", } - Meta { + document::Meta { name: "description", content: "My Site is a site", } diff --git a/examples/title.rs b/examples/title.rs index ebe258963c..bc44095be1 100644 --- a/examples/title.rs +++ b/examples/title.rs @@ -14,7 +14,7 @@ fn app() -> Element { div { // You can set the title of the page with the Title component // In web applications, this sets the title in the head. On desktop, it sets the window title - Title { "My Application (Count {count})" } + document::Title { "My Application (Count {count})" } button { onclick: move |_| count += 1, "Up high!" } button { onclick: move |_| count -= 1, "Down low!" } } From 4013682d31b7365f925f280f2658cdf09e215a45 Mon Sep 17 00:00:00 2001 From: Evan Almloff <evanalmloff@gmail.com> Date: Mon, 7 Oct 2024 10:01:23 -0500 Subject: [PATCH 133/139] Fix tests --- packages/document/src/document.rs | 11 +++++++++++ packages/document/src/lib.rs | 11 +++++++++-- packages/fullstack/src/document/server.rs | 5 ++--- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/packages/document/src/document.rs b/packages/document/src/document.rs index dbdb46907b..3de8beeca7 100644 --- a/packages/document/src/document.rs +++ b/packages/document/src/document.rs @@ -118,3 +118,14 @@ pub trait Document: 'static { self.create_head_element("link", &attributes, None); } } + +/// A document that does nothing +#[derive(Default)] +pub struct NoOpDocument; + +impl Document for NoOpDocument { + fn eval(&self, _: String) -> Eval { + let owner = generational_box::Owner::default(); + Eval::new(owner.invalid()) + } +} diff --git a/packages/document/src/lib.rs b/packages/document/src/lib.rs index ca53906ebd..05429f394a 100644 --- a/packages/document/src/lib.rs +++ b/packages/document/src/lib.rs @@ -12,8 +12,15 @@ pub use eval::*; /// Get the document provider for the current platform or a no-op provider if the platform doesn't document functionality. pub fn document() -> Rc<dyn Document> { - dioxus_core::prelude::try_consume_context::<Rc<dyn Document>>() - .expect("A document should exist with this renderer") + match dioxus_core::prelude::try_consume_context::<Rc<dyn Document>>() { + Some(document) => document, + None => { + tracing::error!( + "Unable to find a document in the renderer. Using the default no-op document." + ); + Rc::new(NoOpDocument) + } + } } /// Evaluate some javascript in the current document diff --git a/packages/fullstack/src/document/server.rs b/packages/fullstack/src/document/server.rs index bee2e461e3..de602654fa 100644 --- a/packages/fullstack/src/document/server.rs +++ b/packages/fullstack/src/document/server.rs @@ -70,9 +70,8 @@ impl ServerDocument { } impl Document for ServerDocument { - fn eval(&self, _: String) -> Eval { - let owner = Owner::default(); - Eval::new(owner.invalid()) + fn eval(&self, js: String) -> Eval { + NoOpDocument.eval(js) } fn set_title(&self, title: String) { From 559505075acbd9a17d1328c0294bd1fb1afd639d Mon Sep 17 00:00:00 2001 From: Evan Almloff <evanalmloff@gmail.com> Date: Mon, 7 Oct 2024 15:58:17 -0500 Subject: [PATCH 134/139] Fix desktop crate doc tests --- Cargo.lock | 1 + packages/document/Cargo.toml | 3 +++ 2 files changed, 4 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 3609517ae1..f41244330b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2656,6 +2656,7 @@ dependencies = [ name = "dioxus-document" version = "0.6.0-alpha.2" dependencies = [ + "dioxus", "dioxus-core", "dioxus-core-macro", "dioxus-core-types", diff --git a/packages/document/Cargo.toml b/packages/document/Cargo.toml index 52470e7435..76f6f958dd 100644 --- a/packages/document/Cargo.toml +++ b/packages/document/Cargo.toml @@ -17,3 +17,6 @@ generational-box.workspace = true [build-dependencies] lazy-js-bundle = { workspace = true } + +[dev-dependencies] +dioxus = { workspace = true } From 3a848d28c1ea64236bb69f8a67752fa1a7886564 Mon Sep 17 00:00:00 2001 From: Evan Almloff <evanalmloff@gmail.com> Date: Mon, 7 Oct 2024 16:03:37 -0500 Subject: [PATCH 135/139] more doc test fixes --- packages/{html => document}/assets/script.js | 0 packages/{html => document}/assets/style.css | 0 packages/document/docs/eval.md | 18 +-- packages/document/docs/head.md | 6 +- packages/document/src/elements/meta.rs | 2 +- packages/document/src/elements/script.rs | 2 +- packages/document/src/elements/style.rs | 2 +- packages/document/src/elements/title.rs | 2 +- packages/html/docs/eval.md | 125 ------------------- packages/html/docs/head.md | 59 --------- 10 files changed, 16 insertions(+), 200 deletions(-) rename packages/{html => document}/assets/script.js (100%) rename packages/{html => document}/assets/style.css (100%) delete mode 100644 packages/html/docs/eval.md delete mode 100644 packages/html/docs/head.md diff --git a/packages/html/assets/script.js b/packages/document/assets/script.js similarity index 100% rename from packages/html/assets/script.js rename to packages/document/assets/script.js diff --git a/packages/html/assets/style.css b/packages/document/assets/style.css similarity index 100% rename from packages/html/assets/style.css rename to packages/document/assets/style.css diff --git a/packages/document/docs/eval.md b/packages/document/docs/eval.md index 673a13f14d..7608c9b3d9 100644 --- a/packages/document/docs/eval.md +++ b/packages/document/docs/eval.md @@ -1,6 +1,6 @@ # Communicating with JavaScript -You can use the `eval` function to execute JavaScript code in your application with the desktop, mobile, web or liveview renderers. Eval takes a block of JavaScript code (that may be asynchronous) and returns a `UseEval` object that you can use to send data to the JavaScript code and receive data from it. +You can use the `eval` function to execute JavaScript code in your application with the desktop, mobile, web or liveview renderers. Eval takes a block of JavaScript code (that may be asynchronous) and returns a `Eval` object that you can use to send data to the JavaScript code and receive data from it. <div class="warning"> @@ -18,7 +18,7 @@ fn App() -> Element { button { onclick: move |_| async move { // Eval is a global function you can use anywhere inside Dioxus. It will execute the given JavaScript code. - let result = eval(r#"console.log("Hello World"); + let result = document::eval(r#"console.log("Hello World"); return "Hello World";"#); // You can use the `await` keyword to wait for the result of the JavaScript code. @@ -32,7 +32,7 @@ fn App() -> Element { ## Sending data to JavaScript -When you execute JavaScript code with `eval`, you can pass data to it by formatting the value into the JavaScript code or sending values to the `UseEval` channel. +When you execute JavaScript code with `eval`, you can pass data to it by formatting the value into the JavaScript code or sending values to the `Eval` channel. ```rust use dioxus::prelude::*; @@ -43,7 +43,7 @@ fn app() -> Element { onclick: move |_| { // You can pass initial data to the eval function by formatting it into the JavaScript code. const LOOP_COUNT: usize = 10; - let eval = eval(&format!(r#"for(let i = 0; i < {LOOP_COUNT}; i++) {{ + let eval = document::eval(&format!(r#"for(let i = 0; i < {LOOP_COUNT}; i++) {{ // You can receive values asynchronously with the the `await dioxus.recv()` method. let value = await dioxus.recv(); console.log("Received", value); @@ -51,7 +51,7 @@ fn app() -> Element { // You can send values from rust to the JavaScript code with the `send` method on the object returned by `eval`. for i in 0..LOOP_COUNT { - eval.send(i.into()).unwrap(); + eval.send(i).unwrap(); } }, "Log Count" @@ -62,7 +62,7 @@ fn app() -> Element { ## Sending data from JavaScript -The `UseEval` struct also contains methods for receiving values you send from JavaScript. You can use the `dioxus.send()` method to send values to the JavaScript code and the `UseEval::recv()` method to receive values from the JavaScript code. +The `Eval` struct also contains methods for receiving values you send from JavaScript. You can use the `dioxus.send()` method to send values to the JavaScript code and the `Eval::recv()` method to receive values from the JavaScript code. ```rust use dioxus::prelude::*; @@ -72,14 +72,14 @@ fn app() -> Element { button { onclick: move |_| async move { // You can send values from rust to the JavaScript code by using the `send` method on the object returned by `eval`. - let mut eval = eval(r#"for(let i = 0; i < 10; i++) { + let mut eval = document::eval(r#"for(let i = 0; i < 10; i++) { // You can send values asynchronously with the `dioxus.send()` method. dioxus.send(i); }"#); // You can receive values from the JavaScript code with the `recv` method on the object returned by `eval`. for _ in 0..10 { - let value = eval.recv().await.unwrap(); + let value: i32 = eval.recv().await.unwrap(); println!("Received {}", value); } }, @@ -109,7 +109,7 @@ fn app() -> Element { // ✅ You should run eval inside an effect or event. This will run after the component has been mounted use_effect(move || { spawn(async { - let count = eval(SCRIPT).await; + let count = document::eval(SCRIPT).await; println!("Count is {:?}", count); }); }); diff --git a/packages/document/docs/head.md b/packages/document/docs/head.md index ae726924de..b5d022830c 100644 --- a/packages/document/docs/head.md +++ b/packages/document/docs/head.md @@ -25,7 +25,7 @@ fn RedirectToDioxusHomepageWithoutJS() -> Element { rsx! { // You can use the meta component to render a meta tag into the head of the page // This meta tag will redirect the user to the dioxuslabs homepage in 10 seconds - Meta { + document::Meta { http_equiv: "refresh", content: "10;url=https://dioxuslabs.com", } @@ -46,12 +46,12 @@ If you have any important metadata that you want to render into the head, make s fn App() -> Element { rsx! { // This will render in SSR - Title { "My Page" } + document::Title { "My Page" } SuspenseBoundary { fallback: |_| rsx! { "Loading..." }, LoadData { // This will only be rendered on the client after hydration so it may not be visible to search engines - Meta { name: "description", content: "My Page" } + document::Meta { name: "description", content: "My Page" } } } } diff --git a/packages/document/src/elements/meta.rs b/packages/document/src/elements/meta.rs index a4227589b8..ea17bdb9e7 100644 --- a/packages/document/src/elements/meta.rs +++ b/packages/document/src/elements/meta.rs @@ -47,7 +47,7 @@ impl MetaProps { /// rsx! { /// // You can use the meta component to render a meta tag into the head of the page /// // This meta tag will redirect the user to the dioxuslabs homepage in 10 seconds -/// Meta { +/// document::Meta { /// http_equiv: "refresh", /// content: "10;url=https://dioxuslabs.com", /// } diff --git a/packages/document/src/elements/script.rs b/packages/document/src/elements/script.rs index b285b275dc..47f78fc6bd 100644 --- a/packages/document/src/elements/script.rs +++ b/packages/document/src/elements/script.rs @@ -73,7 +73,7 @@ impl ScriptProps { /// fn LoadScript() -> Element { /// rsx! { /// // You can use the Script component to render a script tag into the head of the page -/// Script { +/// document::Script { /// src: asset!("./assets/script.js"), /// } /// } diff --git a/packages/document/src/elements/style.rs b/packages/document/src/elements/style.rs index 1f386e332d..bb2f682153 100644 --- a/packages/document/src/elements/style.rs +++ b/packages/document/src/elements/style.rs @@ -50,7 +50,7 @@ impl StyleProps { /// rsx! { /// // You can use the style component to render a style tag into the head of the page /// // This style tag will set the background color of the page to red -/// Style { +/// document::Style { /// r#" /// body {{ /// background-color: red; diff --git a/packages/document/src/elements/title.rs b/packages/document/src/elements/title.rs index efb95f82ee..2a5fe24b83 100644 --- a/packages/document/src/elements/title.rs +++ b/packages/document/src/elements/title.rs @@ -22,7 +22,7 @@ pub struct TitleProps { /// fn App() -> Element { /// rsx! { /// // You can use the Title component to render a title tag into the head of the page or window -/// Title { "My Page" } +/// document::Title { "My Page" } /// } /// } /// ``` diff --git a/packages/html/docs/eval.md b/packages/html/docs/eval.md deleted file mode 100644 index 673a13f14d..0000000000 --- a/packages/html/docs/eval.md +++ /dev/null @@ -1,125 +0,0 @@ -# Communicating with JavaScript - -You can use the `eval` function to execute JavaScript code in your application with the desktop, mobile, web or liveview renderers. Eval takes a block of JavaScript code (that may be asynchronous) and returns a `UseEval` object that you can use to send data to the JavaScript code and receive data from it. - -<div class="warning"> - -## Safety - -Please be careful when executing JavaScript code with `eval`. You should only execute code that you trust. **This applies especially to web targets, where the JavaScript context has access to most, if not all of your application data.** Running untrusted code can lead to a [cross-site scripting](https://developer.mozilla.org/en-US/docs/Glossary/Cross-site_scripting) (XSS) vulnerability. - -</div> - -```rust -use dioxus::prelude::*; - -fn App() -> Element { - rsx! { - button { - onclick: move |_| async move { - // Eval is a global function you can use anywhere inside Dioxus. It will execute the given JavaScript code. - let result = eval(r#"console.log("Hello World"); - return "Hello World";"#); - - // You can use the `await` keyword to wait for the result of the JavaScript code. - println!("{:?}", result.await); - }, - "Log Hello World" - } - } -} -``` - -## Sending data to JavaScript - -When you execute JavaScript code with `eval`, you can pass data to it by formatting the value into the JavaScript code or sending values to the `UseEval` channel. - -```rust -use dioxus::prelude::*; - -fn app() -> Element { - rsx! { - button { - onclick: move |_| { - // You can pass initial data to the eval function by formatting it into the JavaScript code. - const LOOP_COUNT: usize = 10; - let eval = eval(&format!(r#"for(let i = 0; i < {LOOP_COUNT}; i++) {{ - // You can receive values asynchronously with the the `await dioxus.recv()` method. - let value = await dioxus.recv(); - console.log("Received", value); - }}"#)); - - // You can send values from rust to the JavaScript code with the `send` method on the object returned by `eval`. - for i in 0..LOOP_COUNT { - eval.send(i.into()).unwrap(); - } - }, - "Log Count" - } - } -} -``` - -## Sending data from JavaScript - -The `UseEval` struct also contains methods for receiving values you send from JavaScript. You can use the `dioxus.send()` method to send values to the JavaScript code and the `UseEval::recv()` method to receive values from the JavaScript code. - -```rust -use dioxus::prelude::*; - -fn app() -> Element { - rsx! { - button { - onclick: move |_| async move { - // You can send values from rust to the JavaScript code by using the `send` method on the object returned by `eval`. - let mut eval = eval(r#"for(let i = 0; i < 10; i++) { - // You can send values asynchronously with the `dioxus.send()` method. - dioxus.send(i); - }"#); - - // You can receive values from the JavaScript code with the `recv` method on the object returned by `eval`. - for _ in 0..10 { - let value = eval.recv().await.unwrap(); - println!("Received {}", value); - } - }, - "Log Count" - } - } -} -``` - -## Interacting with the DOM with Eval - -You can also use the `eval` function to execute JavaScript code that reads or modifies the DOM. If you want to interact with the mounted DOM, you need to use `eval` inside the [`dioxus_hooks::use_effect`] hook which runs after the component has been mounted. - -```rust -use dioxus::prelude::*; - -const SCRIPT: &str = r#" - let element = document.getElementById("my-element"); - element.innerHTML = "Hello World"; - return element.getAttribute("data-count"); -"#; - -fn app() -> Element { - // ❌ You shouldn't run eval in the body of a component. This will run before the component has been mounted - // eval(SCRIPT); - - // ✅ You should run eval inside an effect or event. This will run after the component has been mounted - use_effect(move || { - spawn(async { - let count = eval(SCRIPT).await; - println!("Count is {:?}", count); - }); - }); - - - rsx! { - div { - id: "my-element", - "data-count": "123", - } - } -} -``` diff --git a/packages/html/docs/head.md b/packages/html/docs/head.md deleted file mode 100644 index ae726924de..0000000000 --- a/packages/html/docs/head.md +++ /dev/null @@ -1,59 +0,0 @@ -# Modifying the Head - -Dioxus includes a series of components that render into the head of the page: - -- [Title](crate::Title) -- [Meta](crate::Meta) -- [document::Link](crate::document::Link) -- [Script](crate::Script) -- [Style](crate::Style) - -Each of these components can be used to add extra information to the head of the page. For example, you can use the `Title` component to set the title of the page, or the `Meta` component to add extra metadata to the page. - -## Limitations - -Components that render into the head of the page do have a few key limitations: - -- With the exception of the `Title` component, all components that render into the head cannot be modified after the first time they are rendered. -- Components that render into the head will not be removed even after the component is removed from the tree. - -## Example - -```rust, no_run -# use dioxus::prelude::*; -fn RedirectToDioxusHomepageWithoutJS() -> Element { - rsx! { - // You can use the meta component to render a meta tag into the head of the page - // This meta tag will redirect the user to the dioxuslabs homepage in 10 seconds - Meta { - http_equiv: "refresh", - content: "10;url=https://dioxuslabs.com", - } - } -} -``` - -## Fullstack Rendering - -Head components are compatible with fullstack rendering, but only head components that are rendered in the initial render (before suspense boundaries resolve) will be rendered into the head. - -If you have any important metadata that you want to render into the head, make sure to render it outside of any pending suspense boundaries. - -```rust, no_run -# use dioxus::prelude::*; -# #[component] -# fn LoadData(children: Element) -> Element { unimplemented!() } -fn App() -> Element { - rsx! { - // This will render in SSR - Title { "My Page" } - SuspenseBoundary { - fallback: |_| rsx! { "Loading..." }, - LoadData { - // This will only be rendered on the client after hydration so it may not be visible to search engines - Meta { name: "description", content: "My Page" } - } - } - } -} -``` From 467764cd8f88f0f761d3a38d78f1904abe4b56d5 Mon Sep 17 00:00:00 2001 From: Evan Almloff <evanalmloff@gmail.com> Date: Mon, 7 Oct 2024 19:08:26 -0500 Subject: [PATCH 136/139] even more doc test fixes --- packages/core/src/scheduler.rs | 2 +- packages/document/docs/eval.md | 2 +- packages/hooks/docs/side_effects.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/core/src/scheduler.rs b/packages/core/src/scheduler.rs index 2a87586dce..189015832b 100644 --- a/packages/core/src/scheduler.rs +++ b/packages/core/src/scheduler.rs @@ -32,7 +32,7 @@ //! use_effect(move || { //! let id = id.read(); //! // This will panic if the id is not written to the DOM before the effect is run -//! eval(format!(r#"document.getElementById("{id}").innerHTML = "Hello World";"#)); +//! document::eval(format!(r#"document.getElementById("{id}").innerHTML = "Hello World";"#)); //! }); //! //! rsx! { diff --git a/packages/document/docs/eval.md b/packages/document/docs/eval.md index 7608c9b3d9..eb7847f162 100644 --- a/packages/document/docs/eval.md +++ b/packages/document/docs/eval.md @@ -104,7 +104,7 @@ const SCRIPT: &str = r#" fn app() -> Element { // ❌ You shouldn't run eval in the body of a component. This will run before the component has been mounted - // eval(SCRIPT); + // document::eval(SCRIPT); // ✅ You should run eval inside an effect or event. This will run after the component has been mounted use_effect(move || { diff --git a/packages/hooks/docs/side_effects.md b/packages/hooks/docs/side_effects.md index 1f63877b8d..b1d87dbfdc 100644 --- a/packages/hooks/docs/side_effects.md +++ b/packages/hooks/docs/side_effects.md @@ -16,7 +16,7 @@ fn MyComponent() -> Element { let count = count.read(); // You can use the count value to update the DOM manually - eval(&format!( + document::eval(&format!( r#"var c = document.getElementById("dioxus-canvas"); var ctx = c.getContext("2d"); ctx.font = "30px Arial"; From b0edbe6d5e41e097183c1219af495ffdd95754e8 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley <jkelleyrtp@gmail.com> Date: Mon, 14 Oct 2024 09:25:57 -0700 Subject: [PATCH 137/139] regen hash --- packages/desktop/src/js/hash.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/desktop/src/js/hash.txt b/packages/desktop/src/js/hash.txt index 6ef70bcf9e..92bf35c553 100644 --- a/packages/desktop/src/js/hash.txt +++ b/packages/desktop/src/js/hash.txt @@ -1 +1 @@ -[8429135317751680054] +[14101548031762241351] \ No newline at end of file From 2e2eff564c529b703f359a0af688d1393e3ca3c5 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley <jkelleyrtp@gmail.com> Date: Mon, 14 Oct 2024 09:42:40 -0700 Subject: [PATCH 138/139] fix merge conflicts --- packages/dioxus/src/lib.rs | 1 + packages/document/src/document.rs | 27 ++++++++++++++++++++++++++- packages/web/build.rs | 4 ---- packages/web/src/document.rs | 10 +++------- 4 files changed, 30 insertions(+), 12 deletions(-) diff --git a/packages/dioxus/src/lib.rs b/packages/dioxus/src/lib.rs index 43a44dc955..a247ea1813 100644 --- a/packages/dioxus/src/lib.rs +++ b/packages/dioxus/src/lib.rs @@ -66,6 +66,7 @@ pub mod prelude { #[cfg(feature = "document")] #[cfg_attr(docsrs, doc(cfg(feature = "document")))] pub use dioxus_document as document; + pub use dioxus_document::Document; #[cfg(feature = "launch")] #[cfg_attr(docsrs, doc(cfg(feature = "launch")))] diff --git a/packages/document/src/document.rs b/packages/document/src/document.rs index 3de8beeca7..76b3fe4b74 100644 --- a/packages/document/src/document.rs +++ b/packages/document/src/document.rs @@ -126,6 +126,31 @@ pub struct NoOpDocument; impl Document for NoOpDocument { fn eval(&self, _: String) -> Eval { let owner = generational_box::Owner::default(); - Eval::new(owner.invalid()) + let boxed = owner.insert(Box::new(NoOpEvaluator {}) as Box<dyn Evaluator + 'static>); + Eval::new(boxed) + } +} + +/// An evluator that does nothing +#[derive(Default)] +pub struct NoOpEvaluator; + +impl Evaluator for NoOpEvaluator { + fn send(&self, _data: serde_json::Value) -> Result<(), EvalError> { + Err(EvalError::Unsupported) + } + + fn poll_recv( + &mut self, + _context: &mut std::task::Context<'_>, + ) -> std::task::Poll<Result<serde_json::Value, EvalError>> { + std::task::Poll::Ready(Err(EvalError::Unsupported)) + } + + fn poll_join( + &mut self, + _context: &mut std::task::Context<'_>, + ) -> std::task::Poll<Result<serde_json::Value, EvalError>> { + std::task::Poll::Ready(Err(EvalError::Unsupported)) } } diff --git a/packages/web/build.rs b/packages/web/build.rs index 59da5348c1..860c568758 100644 --- a/packages/web/build.rs +++ b/packages/web/build.rs @@ -2,10 +2,6 @@ fn main() { // If any TS files change, re-run the build script lazy_js_bundle::LazyTypeScriptBindings::new() .with_watching("./src/ts") -<<<<<<<< HEAD:packages/web/build.rs .with_binding("./src/ts/eval.ts", "./src/js/eval.js") -======== - .with_binding("./src/ts/head.ts", "./src/js/head.js") ->>>>>>>> main:packages/html/build.rs .run(); } diff --git a/packages/web/src/document.rs b/packages/web/src/document.rs index 2f796041d1..ff9d8b4616 100644 --- a/packages/web/src/document.rs +++ b/packages/web/src/document.rs @@ -91,10 +91,8 @@ impl WebEvaluator { fn create(js: String) -> GenerationalBox<Box<dyn Evaluator>> { let owner = UnsyncStorage::owner(); - let generational_box = owner.insert(Box::new(NoOpEvaluator) as Box<dyn Evaluator>); - // add the drop handler to DioxusChannel so that it gets dropped when the channel is dropped in js - let channels = WebDioxusChannel::new(JSOwner::new(owner)); + let channels = WebDioxusChannel::new(JSOwner::new(owner.clone())); // The Rust side of the channel is a weak reference to the DioxusChannel let weak_channels = channels.weak(); @@ -127,13 +125,11 @@ impl WebEvaluator { )), }; - generational_box.set(Box::new(Self { + owner.insert(Box::new(Self { channels: weak_channels, result: Some(result), next_future: None, - }) as Box<dyn Evaluator>); - - generational_box + }) as Box<dyn Evaluator>) } } From c5a4408f76d0c1d4b850af0af475391ace612ad7 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley <jkelleyrtp@gmail.com> Date: Mon, 14 Oct 2024 10:09:40 -0700 Subject: [PATCH 139/139] fix document imports, add comments for assets --- packages/dioxus/src/launch.rs | 2 +- packages/dioxus/src/lib.rs | 1 - packages/document/assets/script.js | 1 + packages/document/assets/style.css | 1 + packages/document/src/document.rs | 2 +- 5 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/dioxus/src/launch.rs b/packages/dioxus/src/launch.rs index 7141f5691b..ae7e3984bc 100644 --- a/packages/dioxus/src/launch.rs +++ b/packages/dioxus/src/launch.rs @@ -330,7 +330,7 @@ fn web_launch( #[cfg(all(feature = "static-generation", not(feature = "fullstack")))] use dioxus_static_site_generation::document; let document = std::rc::Rc::new(document::web::FullstackWebDocument) - as std::rc::Rc<dyn crate::prelude::Document>; + as std::rc::Rc<dyn crate::prelude::document::Document>; vdom.provide_root_context(document); } vdom diff --git a/packages/dioxus/src/lib.rs b/packages/dioxus/src/lib.rs index a247ea1813..43a44dc955 100644 --- a/packages/dioxus/src/lib.rs +++ b/packages/dioxus/src/lib.rs @@ -66,7 +66,6 @@ pub mod prelude { #[cfg(feature = "document")] #[cfg_attr(docsrs, doc(cfg(feature = "document")))] pub use dioxus_document as document; - pub use dioxus_document::Document; #[cfg(feature = "launch")] #[cfg_attr(docsrs, doc(cfg(feature = "launch")))] diff --git a/packages/document/assets/script.js b/packages/document/assets/script.js index e69de29bb2..72714dc658 100644 --- a/packages/document/assets/script.js +++ b/packages/document/assets/script.js @@ -0,0 +1 @@ +// this script is included as an asset!() for a test diff --git a/packages/document/assets/style.css b/packages/document/assets/style.css index e69de29bb2..b47db6fc63 100644 --- a/packages/document/assets/style.css +++ b/packages/document/assets/style.css @@ -0,0 +1 @@ +/* this stylesheet is included as an asset!() for a test */ diff --git a/packages/document/src/document.rs b/packages/document/src/document.rs index 76b3fe4b74..709e68638a 100644 --- a/packages/document/src/document.rs +++ b/packages/document/src/document.rs @@ -131,7 +131,7 @@ impl Document for NoOpDocument { } } -/// An evluator that does nothing +/// An evaluator that does nothing #[derive(Default)] pub struct NoOpEvaluator;

      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